/* Mapbox GL JS is Copyright © 2020 Mapbox and subject to the Mapbox Terms of Service ((https://www.mapbox.com/legal/tos/). */
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.mapboxgl = factory());
})(this, (function () { 'use strict';

/* eslint-disable */

var shared, worker, mapboxgl;
// define gets called three times: one for each chunk. we rely on the order
// they're imported to know which is which
function define(_, chunk) {
if (!shared) {
    shared = chunk;
} else if (!worker) {
    worker = chunk;
} else {
    var workerBundleString = "self.onerror = function() { console.error('An error occurred while parsing the WebWorker bundle. This is most likely due to improper transpilation by Babel; please see https://docs.mapbox.com/mapbox-gl-js/api/#transpiling-v2'); }; var sharedChunk = {}; (" + shared + ")(sharedChunk); (" + worker + ")(sharedChunk); self.onerror = null;"

    var sharedChunk = {};
    shared(sharedChunk);
    mapboxgl = chunk(sharedChunk);
    if (typeof window !== 'undefined') {
        mapboxgl.workerUrl = window.URL.createObjectURL(new Blob([workerBundleString], { type: 'text/javascript' }));
    }
}
}


define(['exports'], (function (exports) { 'use strict';

var version = "2.3.0";

var unitbezier = UnitBezier;
function UnitBezier(p1x, p1y, p2x, p2y) {
    this.cx = 3 * p1x;
    this.bx = 3 * (p2x - p1x) - this.cx;
    this.ax = 1 - this.cx - this.bx;
    this.cy = 3 * p1y;
    this.by = 3 * (p2y - p1y) - this.cy;
    this.ay = 1 - this.cy - this.by;
    this.p1x = p1x;
    this.p1y = p2y;
    this.p2x = p2x;
    this.p2y = p2y;
}
UnitBezier.prototype.sampleCurveX = function (t) {
    return ((this.ax * t + this.bx) * t + this.cx) * t;
};
UnitBezier.prototype.sampleCurveY = function (t) {
    return ((this.ay * t + this.by) * t + this.cy) * t;
};
UnitBezier.prototype.sampleCurveDerivativeX = function (t) {
    return (3 * this.ax * t + 2 * this.bx) * t + this.cx;
};
UnitBezier.prototype.solveCurveX = function (x, epsilon) {
    if (typeof epsilon === 'undefined')
        epsilon = 0.000001;
    var t0, t1, t2, x2, i;
    for (t2 = x, i = 0; i < 8; i++) {
        x2 = this.sampleCurveX(t2) - x;
        if (Math.abs(x2) < epsilon)
            return t2;
        var d2 = this.sampleCurveDerivativeX(t2);
        if (Math.abs(d2) < 0.000001)
            break;
        t2 = t2 - x2 / d2;
    }
    t0 = 0;
    t1 = 1;
    t2 = x;
    if (t2 < t0)
        return t0;
    if (t2 > t1)
        return t1;
    while (t0 < t1) {
        x2 = this.sampleCurveX(t2);
        if (Math.abs(x2 - x) < epsilon)
            return t2;
        if (x > x2) {
            t0 = t2;
        } else {
            t1 = t2;
        }
        t2 = (t1 - t0) * 0.5 + t0;
    }
    return t2;
};
UnitBezier.prototype.solve = function (x, epsilon) {
    return this.sampleCurveY(this.solveCurveX(x, epsilon));
};

var pointGeometry = Point;
function Point(x, y) {
    this.x = x;
    this.y = y;
}
Point.prototype = {
    clone: function () {
        return new Point(this.x, this.y);
    },
    add: function (p) {
        return this.clone()._add(p);
    },
    sub: function (p) {
        return this.clone()._sub(p);
    },
    multByPoint: function (p) {
        return this.clone()._multByPoint(p);
    },
    divByPoint: function (p) {
        return this.clone()._divByPoint(p);
    },
    mult: function (k) {
        return this.clone()._mult(k);
    },
    div: function (k) {
        return this.clone()._div(k);
    },
    rotate: function (a) {
        return this.clone()._rotate(a);
    },
    rotateAround: function (a, p) {
        return this.clone()._rotateAround(a, p);
    },
    matMult: function (m) {
        return this.clone()._matMult(m);
    },
    unit: function () {
        return this.clone()._unit();
    },
    perp: function () {
        return this.clone()._perp();
    },
    round: function () {
        return this.clone()._round();
    },
    mag: function () {
        return Math.sqrt(this.x * this.x + this.y * this.y);
    },
    equals: function (other) {
        return this.x === other.x && this.y === other.y;
    },
    dist: function (p) {
        return Math.sqrt(this.distSqr(p));
    },
    distSqr: function (p) {
        var dx = p.x - this.x, dy = p.y - this.y;
        return dx * dx + dy * dy;
    },
    angle: function () {
        return Math.atan2(this.y, this.x);
    },
    angleTo: function (b) {
        return Math.atan2(this.y - b.y, this.x - b.x);
    },
    angleWith: function (b) {
        return this.angleWithSep(b.x, b.y);
    },
    angleWithSep: function (x, y) {
        return Math.atan2(this.x * y - this.y * x, this.x * x + this.y * y);
    },
    _matMult: function (m) {
        var 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;
    },
    _add: function (p) {
        this.x += p.x;
        this.y += p.y;
        return this;
    },
    _sub: function (p) {
        this.x -= p.x;
        this.y -= p.y;
        return this;
    },
    _mult: function (k) {
        this.x *= k;
        this.y *= k;
        return this;
    },
    _div: function (k) {
        this.x /= k;
        this.y /= k;
        return this;
    },
    _multByPoint: function (p) {
        this.x *= p.x;
        this.y *= p.y;
        return this;
    },
    _divByPoint: function (p) {
        this.x /= p.x;
        this.y /= p.y;
        return this;
    },
    _unit: function () {
        this._div(this.mag());
        return this;
    },
    _perp: function () {
        var y = this.y;
        this.y = this.x;
        this.x = -y;
        return this;
    },
    _rotate: function (angle) {
        var 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;
    },
    _rotateAround: function (angle, p) {
        var 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: function () {
        this.x = Math.round(this.x);
        this.y = Math.round(this.y);
        return this;
    }
};
Point.convert = function (a) {
    if (a instanceof Point) {
        return a;
    }
    if (Array.isArray(a)) {
        return new Point(a[0], a[1]);
    }
    return a;
};

var window$1 = typeof self !== 'undefined' ? self : {};

const MAX_SAFE_INTEGER = Math.pow(2, 53) - 1;
const DEG_TO_RAD = Math.PI / 180;
const RAD_TO_DEG = 180 / Math.PI;
function degToRad(a) {
    return a * DEG_TO_RAD;
}
function radToDeg(a) {
    return a * RAD_TO_DEG;
}
const TILE_CORNERS = [
    [
        0,
        0
    ],
    [
        1,
        0
    ],
    [
        1,
        1
    ],
    [
        0,
        1
    ]
];
function furthestTileCorner(bearing) {
    const alignedBearing = (bearing + 45 + 360) % 360;
    const cornerIdx = Math.round(alignedBearing / 90) % 4;
    return TILE_CORNERS[cornerIdx];
}
function easeCubicInOut(t) {
    if (t <= 0)
        return 0;
    if (t >= 1)
        return 1;
    const t2 = t * t, t3 = t2 * t;
    return 4 * (t < 0.5 ? t3 : 3 * (t - t2) + t3 - 0.75);
}
function getBounds(points) {
    let minX = Infinity;
    let minY = Infinity;
    let maxX = -Infinity;
    let maxY = -Infinity;
    for (const p of points) {
        minX = Math.min(minX, p.x);
        minY = Math.min(minY, p.y);
        maxX = Math.max(maxX, p.x);
        maxY = Math.max(maxY, p.y);
    }
    return {
        min: new pointGeometry(minX, minY),
        max: new pointGeometry(maxX, maxY)
    };
}
function getAABBPointSquareDist(min, max, point) {
    let sqDist = 0;
    for (let i = 0; i < 2; ++i) {
        const v = point ? point[i] : 0;
        if (min[i] > v)
            sqDist += (min[i] - v) * (min[i] - v);
        if (max[i] < v)
            sqDist += (v - max[i]) * (v - max[i]);
    }
    return sqDist;
}
function polygonizeBounds(min, max, buffer = 0, close = true) {
    const offset = new pointGeometry(buffer, buffer);
    const minBuf = min.sub(offset);
    const maxBuf = max.add(offset);
    const polygon = [
        minBuf,
        new pointGeometry(maxBuf.x, minBuf.y),
        maxBuf,
        new pointGeometry(minBuf.x, maxBuf.y)
    ];
    if (close) {
        polygon.push(minBuf);
    }
    return polygon;
}
function bufferConvexPolygon(ring, buffer) {
    const output = [];
    for (let currIdx = 0; currIdx < ring.length; currIdx++) {
        const prevIdx = wrap(currIdx - 1, -1, ring.length - 1);
        const nextIdx = wrap(currIdx + 1, -1, ring.length - 1);
        const prev = ring[prevIdx];
        const curr = ring[currIdx];
        const next = ring[nextIdx];
        const p1 = prev.sub(curr).unit();
        const p2 = next.sub(curr).unit();
        const interiorAngle = p2.angleWithSep(p1.x, p1.y);
        const offset = p1.add(p2).unit().mult(-1 * buffer / Math.sin(interiorAngle / 2));
        output.push(curr.add(offset));
    }
    return output;
}
function bezier(p1x, p1y, p2x, p2y) {
    const bezier = new unitbezier(p1x, p1y, p2x, p2y);
    return function (t) {
        return bezier.solve(t);
    };
}
const ease = bezier(0.25, 0.1, 0.25, 1);
function clamp(n, min, max) {
    return Math.min(max, Math.max(min, n));
}
function smoothstep(e0, e1, x) {
    x = clamp((x - e0) / (e1 - e0), 0, 1);
    return x * x * (3 - 2 * x);
}
function wrap(n, min, max) {
    const d = max - min;
    const w = ((n - min) % d + d) % d + min;
    return w === min ? max : w;
}
function asyncAll(array, fn, callback) {
    if (!array.length) {
        return callback(null, []);
    }
    let remaining = array.length;
    const results = new Array(array.length);
    let error = null;
    array.forEach((item, i) => {
        fn(item, (err, result) => {
            if (err)
                error = err;
            results[i] = result;
            if (--remaining === 0)
                callback(error, results);
        });
    });
}
function values(obj) {
    const result = [];
    for (const k in obj) {
        result.push(obj[k]);
    }
    return result;
}
function keysDifference(obj, other) {
    const difference = [];
    for (const i in obj) {
        if (!(i in other)) {
            difference.push(i);
        }
    }
    return difference;
}
function extend$1(dest, ...sources) {
    for (const src of sources) {
        for (const k in src) {
            dest[k] = src[k];
        }
    }
    return dest;
}
function pick(src, properties) {
    const result = {};
    for (let i = 0; i < properties.length; i++) {
        const k = properties[i];
        if (k in src) {
            result[k] = src[k];
        }
    }
    return result;
}
let id = 1;
function uniqueId() {
    return id++;
}
function uuid() {
    function b(a) {
        return a ? (a ^ Math.random() * 16 >> a / 4).toString(16) : ([10000000] + -[1000] + -4000 + -8000 + -100000000000).replace(/[018]/g, b);
    }
    return b();
}
function nextPowerOfTwo(value) {
    if (value <= 1)
        return 1;
    return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2));
}
function prevPowerOfTwo(value) {
    if (value <= 1)
        return 1;
    return Math.pow(2, Math.floor(Math.log(value) / Math.LN2));
}
function validateUuid(str) {
    return str ? /^[0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(str) : false;
}
function bindAll(fns, context) {
    fns.forEach(fn => {
        if (!context[fn]) {
            return;
        }
        context[fn] = context[fn].bind(context);
    });
}
function endsWith(string, suffix) {
    return string.indexOf(suffix, string.length - suffix.length) !== -1;
}
function mapObject(input, iterator, context) {
    const output = {};
    for (const key in input) {
        output[key] = iterator.call(context || this, input[key], key, input);
    }
    return output;
}
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;
}
function clone$2(input) {
    if (Array.isArray(input)) {
        return input.map(clone$2);
    } else if (typeof input === 'object' && input) {
        return mapObject(input, clone$2);
    } else {
        return input;
    }
}
function arraysIntersect(a, b) {
    for (let l = 0; l < a.length; l++) {
        if (b.indexOf(a[l]) >= 0)
            return true;
    }
    return false;
}
const warnOnceHistory = {};
function warnOnce(message) {
    if (!warnOnceHistory[message]) {
        if (typeof console !== 'undefined')
            console.warn(message);
        warnOnceHistory[message] = true;
    }
}
function isCounterClockwise(a, b, c) {
    return (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x);
}
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;
}
function isWorker() {
    return typeof WorkerGlobalScope !== 'undefined' && typeof self !== 'undefined' && self instanceof WorkerGlobalScope;
}
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;
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 storageAvailable(type) {
    try {
        const storage = window$1[type];
        storage.setItem('_mapbox_test_', 1);
        storage.removeItem('_mapbox_test_');
        return true;
    } catch (e) {
        return false;
    }
}
function b64EncodeUnicode(str) {
    return window$1.btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (match, p1) => {
        return String.fromCharCode(Number('0x' + p1));
    }));
}
function b64DecodeUnicode(str) {
    return decodeURIComponent(window$1.atob(str).split('').map(c => {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
}

let linkEl;
let reducedMotionQuery;
let stubTime;
const exported$1 = {
    now() {
        if (stubTime !== undefined) {
            return stubTime;
        }
        return window$1.performance.now();
    },
    setNow(time) {
        stubTime = time;
    },
    restoreNow() {
        stubTime = undefined;
    },
    frame(fn) {
        const frame = window$1.requestAnimationFrame(fn);
        return { cancel: () => window$1.cancelAnimationFrame(frame) };
    },
    getImageData(img, padding = 0) {
        const canvas = window$1.document.createElement('canvas');
        const context = canvas.getContext('2d');
        if (!context) {
            throw new Error('failed to create canvas 2d context');
        }
        canvas.width = img.width;
        canvas.height = img.height;
        context.drawImage(img, 0, 0, img.width, img.height);
        return context.getImageData(-padding, -padding, img.width + 2 * padding, img.height + 2 * padding);
    },
    resolveURL(path) {
        if (!linkEl)
            linkEl = window$1.document.createElement('a');
        linkEl.href = path;
        return linkEl.href;
    },
    get devicePixelRatio() {
        return window$1.devicePixelRatio;
    },
    get prefersReducedMotion() {
        if (!window$1.matchMedia)
            return false;
        if (reducedMotionQuery == null) {
            reducedMotionQuery = window$1.matchMedia('(prefers-reduced-motion: reduce)');
        }
        return reducedMotionQuery.matches;
    }
};

let mapboxHTTPURLRegex;
const config = {
    API_URL: 'https://api.mapbox.com',
    get API_URL_REGEX() {
        if (mapboxHTTPURLRegex == null) {
            const prodMapboxHTTPURLRegex = /^((https?:)?\/\/)?([^\/]+\.)?mapbox\.c(n|om)(\/|\?|$)/i;
            try {
                mapboxHTTPURLRegex = process.env.API_URL_REGEX != null ? new RegExp(process.env.API_URL_REGEX) : prodMapboxHTTPURLRegex;
            } catch (e) {
                mapboxHTTPURLRegex = prodMapboxHTTPURLRegex;
            }
        }
        return mapboxHTTPURLRegex;
    },
    get EVENTS_URL() {
        if (!this.API_URL) {
            return null;
        }
        if (this.API_URL.indexOf('https://api.mapbox.cn') === 0) {
            return 'https://events.mapbox.cn/events/v2';
        } else if (this.API_URL.indexOf('https://api.mapbox.com') === 0) {
            return 'https://events.mapbox.com/events/v2';
        } else {
            return null;
        }
    },
    SESSION_PATH: '/map-sessions/v1',
    FEEDBACK_URL: 'https://apps.mapbox.com/feedback',
    TILE_URL_VERSION: 'v4',
    RASTER_URL_PREFIX: 'raster/v1',
    REQUIRE_ACCESS_TOKEN: true,
    ACCESS_TOKEN: null,
    MAX_PARALLEL_IMAGE_REQUESTS: 16
};

const exported = {
    supported: false,
    testSupport
};
let glForTesting;
let webpCheckComplete = false;
let webpImgTest;
let webpImgTestOnloadComplete = false;
if (window$1.document) {
    webpImgTest = window$1.document.createElement('img');
    webpImgTest.onload = function () {
        if (glForTesting)
            testWebpTextureUpload(glForTesting);
        glForTesting = null;
        webpImgTestOnloadComplete = true;
    };
    webpImgTest.onerror = function () {
        webpCheckComplete = true;
        glForTesting = null;
    };
    webpImgTest.src = 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA=';
}
function testSupport(gl) {
    if (webpCheckComplete || !webpImgTest)
        return;
    if (webpImgTestOnloadComplete) {
        testWebpTextureUpload(gl);
    } else {
        glForTesting = gl;
    }
}
function testWebpTextureUpload(gl) {
    const texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);
    try {
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, webpImgTest);
        if (gl.isContextLost())
            return;
        exported.supported = true;
    } catch (e) {
    }
    gl.deleteTexture(texture);
    webpCheckComplete = true;
}

const SKU_ID = '01';
function createSkuToken() {
    const TOKEN_VERSION = '1';
    const base62chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    let sessionRandomizer = '';
    for (let i = 0; i < 10; i++) {
        sessionRandomizer += base62chars[Math.floor(Math.random() * 62)];
    }
    const expiration = 12 * 60 * 60 * 1000;
    const token = [
        TOKEN_VERSION,
        SKU_ID,
        sessionRandomizer
    ].join('');
    const tokenExpiresAt = Date.now() + expiration;
    return {
        token,
        tokenExpiresAt
    };
}

const AUTH_ERR_MSG = 'NO_ACCESS_TOKEN';
class RequestManager {
    constructor(transformRequestFn, customAccessToken, silenceAuthErrors) {
        this._transformRequestFn = transformRequestFn;
        this._customAccessToken = customAccessToken;
        this._silenceAuthErrors = !!silenceAuthErrors;
        this._createSkuToken();
    }
    _createSkuToken() {
        const skuToken = createSkuToken();
        this._skuToken = skuToken.token;
        this._skuTokenExpiresAt = skuToken.tokenExpiresAt;
    }
    _isSkuTokenExpired() {
        return Date.now() > this._skuTokenExpiresAt;
    }
    transformRequest(url, type) {
        if (this._transformRequestFn) {
            return this._transformRequestFn(url, type) || { url };
        }
        return { url };
    }
    normalizeStyleURL(url, accessToken) {
        if (!isMapboxURL(url))
            return url;
        const urlObject = parseUrl(url);
        urlObject.path = `/styles/v1${ urlObject.path }`;
        return this._makeAPIURL(urlObject, this._customAccessToken || accessToken);
    }
    normalizeGlyphsURL(url, accessToken) {
        if (!isMapboxURL(url))
            return url;
        const urlObject = parseUrl(url);
        urlObject.path = `/fonts/v1${ urlObject.path }`;
        return this._makeAPIURL(urlObject, this._customAccessToken || accessToken);
    }
    normalizeSourceURL(url, accessToken) {
        if (!isMapboxURL(url))
            return url;
        const urlObject = parseUrl(url);
        urlObject.path = `/v4/${ urlObject.authority }.json`;
        urlObject.params.push('secure');
        return this._makeAPIURL(urlObject, this._customAccessToken || accessToken);
    }
    normalizeSpriteURL(url, format, extension, accessToken) {
        const urlObject = parseUrl(url);
        if (!isMapboxURL(url)) {
            urlObject.path += `${ format }${ extension }`;
            return formatUrl(urlObject);
        }
        urlObject.path = `/styles/v1${ urlObject.path }/sprite${ format }${ extension }`;
        return this._makeAPIURL(urlObject, this._customAccessToken || accessToken);
    }
    normalizeTileURL(tileURL, use2x, rasterTileSize) {
        if (this._isSkuTokenExpired()) {
            this._createSkuToken();
        }
        if (tileURL && !isMapboxURL(tileURL))
            return tileURL;
        const urlObject = parseUrl(tileURL);
        const imageExtensionRe = /(\.(png|jpg)\d*)(?=$)/;
        const extension = exported.supported ? '.webp' : '$1';
        const use2xAs512 = rasterTileSize && urlObject.authority !== 'raster' && rasterTileSize === 512;
        const suffix = use2x || use2xAs512 ? '@2x' : '';
        urlObject.path = urlObject.path.replace(imageExtensionRe, `${ suffix }${ extension }`);
        if (urlObject.authority === 'raster') {
            urlObject.path = `/${ config.RASTER_URL_PREFIX }${ urlObject.path }`;
        } else {
            const tileURLAPIPrefixRe = /^.+\/v4\//;
            urlObject.path = urlObject.path.replace(tileURLAPIPrefixRe, '/');
            urlObject.path = `/${ config.TILE_URL_VERSION }${ urlObject.path }`;
        }
        const accessToken = this._customAccessToken || getAccessToken(urlObject.params) || config.ACCESS_TOKEN;
        if (config.REQUIRE_ACCESS_TOKEN && accessToken && this._skuToken) {
            urlObject.params.push(`sku=${ this._skuToken }`);
        }
        return this._makeAPIURL(urlObject, accessToken);
    }
    canonicalizeTileURL(url, removeAccessToken) {
        const extensionRe = /\.[\w]+$/;
        const urlObject = parseUrl(url);
        if (!urlObject.path.match(/^(\/v4\/|\/raster\/v1\/)/) || !urlObject.path.match(extensionRe)) {
            return url;
        }
        let result = 'mapbox://';
        if (urlObject.path.match(/^\/raster\/v1\//)) {
            const rasterPrefix = `/${ config.RASTER_URL_PREFIX }/`;
            result += `raster/${ urlObject.path.replace(rasterPrefix, '') }`;
        } else {
            const tilesPrefix = `/${ config.TILE_URL_VERSION }/`;
            result += `tiles/${ urlObject.path.replace(tilesPrefix, '') }`;
        }
        let params = urlObject.params;
        if (removeAccessToken) {
            params = params.filter(p => !p.match(/^access_token=/));
        }
        if (params.length)
            result += `?${ params.join('&') }`;
        return result;
    }
    canonicalizeTileset(tileJSON, sourceURL) {
        const removeAccessToken = sourceURL ? isMapboxURL(sourceURL) : false;
        const canonical = [];
        for (const url of tileJSON.tiles || []) {
            if (isMapboxHTTPURL(url)) {
                canonical.push(this.canonicalizeTileURL(url, removeAccessToken));
            } else {
                canonical.push(url);
            }
        }
        return canonical;
    }
    _makeAPIURL(urlObject, accessToken) {
        const help = 'See https://www.mapbox.com/api-documentation/#access-tokens-and-token-scopes';
        const apiUrlObject = parseUrl(config.API_URL);
        urlObject.protocol = apiUrlObject.protocol;
        urlObject.authority = apiUrlObject.authority;
        if (urlObject.protocol === 'http') {
            const i = urlObject.params.indexOf('secure');
            if (i >= 0)
                urlObject.params.splice(i, 1);
        }
        if (apiUrlObject.path !== '/') {
            urlObject.path = `${ apiUrlObject.path }${ urlObject.path }`;
        }
        if (!config.REQUIRE_ACCESS_TOKEN)
            return formatUrl(urlObject);
        accessToken = accessToken || config.ACCESS_TOKEN;
        if (!this._silenceAuthErrors) {
            if (!accessToken)
                throw new Error(`An API access token is required to use Mapbox GL. ${ help }`);
            if (accessToken[0] === 's')
                throw new Error(`Use a public access token (pk.*) with Mapbox GL, not a secret access token (sk.*). ${ help }`);
        }
        urlObject.params = urlObject.params.filter(d => d.indexOf('access_token') === -1);
        urlObject.params.push(`access_token=${ accessToken || '' }`);
        return formatUrl(urlObject);
    }
}
function isMapboxURL(url) {
    return url.indexOf('mapbox:') === 0;
}
function isMapboxHTTPURL(url) {
    return config.API_URL_REGEX.test(url);
}
function hasCacheDefeatingSku(url) {
    return url.indexOf('sku=') > 0 && isMapboxHTTPURL(url);
}
function getAccessToken(params) {
    for (const param of params) {
        const match = param.match(/^access_token=(.*)$/);
        if (match) {
            return match[1];
        }
    }
    return null;
}
const urlRe = /^(\w+):\/\/([^/?]*)(\/[^?]+)?\??(.+)?/;
function parseUrl(url) {
    const parts = url.match(urlRe);
    if (!parts) {
        throw new Error('Unable to parse URL object');
    }
    return {
        protocol: parts[1],
        authority: parts[2],
        path: parts[3] || '/',
        params: parts[4] ? parts[4].split('&') : []
    };
}
function formatUrl(obj) {
    const params = obj.params.length ? `?${ obj.params.join('&') }` : '';
    return `${ obj.protocol }://${ obj.authority }${ obj.path }${ params }`;
}
const telemEventKey = 'mapbox.eventData';
function parseAccessToken(accessToken) {
    if (!accessToken) {
        return null;
    }
    const parts = accessToken.split('.');
    if (!parts || parts.length !== 3) {
        return null;
    }
    try {
        const jsonData = JSON.parse(b64DecodeUnicode(parts[1]));
        return jsonData;
    } catch (e) {
        return null;
    }
}
class TelemetryEvent {
    constructor(type) {
        this.type = type;
        this.anonId = null;
        this.eventData = {};
        this.queue = [];
        this.pendingRequest = null;
    }
    getStorageKey(domain) {
        const tokenData = parseAccessToken(config.ACCESS_TOKEN);
        let u = '';
        if (tokenData && tokenData['u']) {
            u = b64EncodeUnicode(tokenData['u']);
        } else {
            u = config.ACCESS_TOKEN || '';
        }
        return domain ? `${ telemEventKey }.${ domain }:${ u }` : `${ telemEventKey }:${ u }`;
    }
    fetchEventData() {
        const isLocalStorageAvailable = storageAvailable('localStorage');
        const storageKey = this.getStorageKey();
        const uuidKey = this.getStorageKey('uuid');
        if (isLocalStorageAvailable) {
            try {
                const data = window$1.localStorage.getItem(storageKey);
                if (data) {
                    this.eventData = JSON.parse(data);
                }
                const uuid = window$1.localStorage.getItem(uuidKey);
                if (uuid)
                    this.anonId = uuid;
            } catch (e) {
                warnOnce('Unable to read from LocalStorage');
            }
        }
    }
    saveEventData() {
        const isLocalStorageAvailable = storageAvailable('localStorage');
        const storageKey = this.getStorageKey();
        const uuidKey = this.getStorageKey('uuid');
        if (isLocalStorageAvailable) {
            try {
                window$1.localStorage.setItem(uuidKey, this.anonId);
                if (Object.keys(this.eventData).length >= 1) {
                    window$1.localStorage.setItem(storageKey, JSON.stringify(this.eventData));
                }
            } catch (e) {
                warnOnce('Unable to write to LocalStorage');
            }
        }
    }
    processRequests(_) {
    }
    postEvent(timestamp, additionalPayload, callback, customAccessToken) {
        if (!config.EVENTS_URL)
            return;
        const eventsUrlObject = parseUrl(config.EVENTS_URL);
        eventsUrlObject.params.push(`access_token=${ customAccessToken || config.ACCESS_TOKEN || '' }`);
        const payload = {
            event: this.type,
            created: new Date(timestamp).toISOString(),
            sdkIdentifier: 'mapbox-gl-js',
            sdkVersion: version,
            skuId: SKU_ID,
            userId: this.anonId
        };
        const finalPayload = additionalPayload ? extend$1(payload, additionalPayload) : payload;
        const request = {
            url: formatUrl(eventsUrlObject),
            headers: { 'Content-Type': 'text/plain' },
            body: JSON.stringify([finalPayload])
        };
        this.pendingRequest = postData(request, error => {
            this.pendingRequest = null;
            callback(error);
            this.saveEventData();
            this.processRequests(customAccessToken);
        });
    }
    queueRequest(event, customAccessToken) {
        this.queue.push(event);
        this.processRequests(customAccessToken);
    }
}
class MapLoadEvent extends TelemetryEvent {
    constructor() {
        super('map.load');
        this.success = {};
        this.skuToken = '';
    }
    postMapLoadEvent(mapId, skuToken, customAccessToken, callback) {
        this.skuToken = skuToken;
        this.errorCb = callback;
        if (config.EVENTS_URL) {
            if (customAccessToken || config.ACCESS_TOKEN) {
                this.queueRequest({
                    id: mapId,
                    timestamp: Date.now()
                }, customAccessToken);
            } else {
                this.errorCb(new Error(AUTH_ERR_MSG));
            }
        }
    }
    processRequests(customAccessToken) {
        if (this.pendingRequest || this.queue.length === 0)
            return;
        const {id, timestamp} = this.queue.shift();
        if (id && this.success[id])
            return;
        if (!this.anonId) {
            this.fetchEventData();
        }
        if (!validateUuid(this.anonId)) {
            this.anonId = uuid();
        }
        this.postEvent(timestamp, { skuToken: this.skuToken }, err => {
            if (err) {
                this.errorCb(err);
            } else {
                if (id)
                    this.success[id] = true;
            }
        }, customAccessToken);
    }
}
class MapSessionAPI extends TelemetryEvent {
    constructor() {
        super('map.auth');
        this.success = {};
        this.skuToken = '';
    }
    getSession(timestamp, token, callback, customAccessToken) {
        if (!config.API_URL || !config.SESSION_PATH)
            return;
        const authUrlObject = parseUrl(config.API_URL + config.SESSION_PATH);
        authUrlObject.params.push(`sku=${ token || '' }`);
        authUrlObject.params.push(`access_token=${ customAccessToken || config.ACCESS_TOKEN || '' }`);
        const request = {
            url: formatUrl(authUrlObject),
            headers: { 'Content-Type': 'text/plain' }
        };
        this.pendingRequest = getData(request, error => {
            this.pendingRequest = null;
            callback(error);
            this.saveEventData();
            this.processRequests(customAccessToken);
        });
    }
    getSessionAPI(mapId, skuToken, customAccessToken, callback) {
        this.skuToken = skuToken;
        this.errorCb = callback;
        if (config.SESSION_PATH && config.API_URL) {
            if (customAccessToken || config.ACCESS_TOKEN) {
                this.queueRequest({
                    id: mapId,
                    timestamp: Date.now()
                }, customAccessToken);
            } else {
                this.errorCb(new Error(AUTH_ERR_MSG));
            }
        }
    }
    processRequests(customAccessToken) {
        if (this.pendingRequest || this.queue.length === 0)
            return;
        const {id, timestamp} = this.queue.shift();
        if (id && this.success[id])
            return;
        this.getSession(timestamp, this.skuToken, err => {
            if (err) {
                this.errorCb(err);
            } else {
                if (id)
                    this.success[id] = true;
            }
        }, customAccessToken);
    }
}
class TurnstileEvent extends TelemetryEvent {
    constructor(customAccessToken) {
        super('appUserTurnstile');
        this._customAccessToken = customAccessToken;
    }
    postTurnstileEvent(tileUrls, customAccessToken) {
        if (config.EVENTS_URL && config.ACCESS_TOKEN && Array.isArray(tileUrls) && tileUrls.some(url => isMapboxURL(url) || isMapboxHTTPURL(url))) {
            this.queueRequest(Date.now(), customAccessToken);
        }
    }
    processRequests(customAccessToken) {
        if (this.pendingRequest || this.queue.length === 0) {
            return;
        }
        if (!this.anonId || !this.eventData.lastSuccess || !this.eventData.tokenU) {
            this.fetchEventData();
        }
        const tokenData = parseAccessToken(config.ACCESS_TOKEN);
        const tokenU = tokenData ? tokenData['u'] : config.ACCESS_TOKEN;
        let dueForEvent = tokenU !== this.eventData.tokenU;
        if (!validateUuid(this.anonId)) {
            this.anonId = uuid();
            dueForEvent = true;
        }
        const nextUpdate = this.queue.shift();
        if (this.eventData.lastSuccess) {
            const lastUpdate = new Date(this.eventData.lastSuccess);
            const nextDate = new Date(nextUpdate);
            const daysElapsed = (nextUpdate - this.eventData.lastSuccess) / (24 * 60 * 60 * 1000);
            dueForEvent = dueForEvent || daysElapsed >= 1 || daysElapsed < -1 || lastUpdate.getDate() !== nextDate.getDate();
        } else {
            dueForEvent = true;
        }
        if (!dueForEvent) {
            return this.processRequests();
        }
        this.postEvent(nextUpdate, { 'enabled.telemetry': false }, err => {
            if (!err) {
                this.eventData.lastSuccess = nextUpdate;
                this.eventData.tokenU = tokenU;
            }
        }, customAccessToken);
    }
}
const turnstileEvent_ = new TurnstileEvent();
const postTurnstileEvent = turnstileEvent_.postTurnstileEvent.bind(turnstileEvent_);
const mapLoadEvent_ = new MapLoadEvent();
mapLoadEvent_.postMapLoadEvent.bind(mapLoadEvent_);
const mapSessionAPI_ = new MapSessionAPI();
mapSessionAPI_.getSessionAPI.bind(mapSessionAPI_);
const authenticatedMaps = new Set();
function storeAuthState(gl, state) {
    if (state) {
        authenticatedMaps.add(gl);
    } else {
        authenticatedMaps.delete(gl);
    }
}
function isMapAuthenticated(gl) {
    return authenticatedMaps.has(gl);
}
function removeAuthState(gl) {
    authenticatedMaps.delete(gl);
}

const CACHE_NAME = 'mapbox-tiles';
let cacheLimit = 500;
let cacheCheckThreshold = 50;
const MIN_TIME_UNTIL_EXPIRY = 1000 * 60 * 7;
let sharedCache;
function cacheOpen() {
    if (window$1.caches && !sharedCache) {
        sharedCache = window$1.caches.open(CACHE_NAME);
    }
}
let responseConstructorSupportsReadableStream;
function prepareBody(response, callback) {
    if (responseConstructorSupportsReadableStream === undefined) {
        try {
            new Response(new ReadableStream());
            responseConstructorSupportsReadableStream = true;
        } catch (e) {
            responseConstructorSupportsReadableStream = false;
        }
    }
    if (responseConstructorSupportsReadableStream) {
        callback(response.body);
    } else {
        response.blob().then(callback);
    }
}
function cachePut(request, response, requestTime) {
    cacheOpen();
    if (!sharedCache)
        return;
    const options = {
        status: response.status,
        statusText: response.statusText,
        headers: new window$1.Headers()
    };
    response.headers.forEach((v, k) => options.headers.set(k, v));
    const cacheControl = parseCacheControl(response.headers.get('Cache-Control') || '');
    if (cacheControl['no-store']) {
        return;
    }
    if (cacheControl['max-age']) {
        options.headers.set('Expires', new Date(requestTime + cacheControl['max-age'] * 1000).toUTCString());
    }
    const timeUntilExpiry = new Date(options.headers.get('Expires')).getTime() - requestTime;
    if (timeUntilExpiry < MIN_TIME_UNTIL_EXPIRY)
        return;
    prepareBody(response, body => {
        const clonedResponse = new window$1.Response(body, options);
        cacheOpen();
        if (!sharedCache)
            return;
        sharedCache.then(cache => cache.put(stripQueryParameters(request.url), clonedResponse)).catch(e => warnOnce(e.message));
    });
}
function stripQueryParameters(url) {
    const start = url.indexOf('?');
    return start < 0 ? url : url.slice(0, start);
}
function cacheGet(request, callback) {
    cacheOpen();
    if (!sharedCache)
        return callback(null);
    const strippedURL = stripQueryParameters(request.url);
    sharedCache.then(cache => {
        cache.match(strippedURL).then(response => {
            const fresh = isFresh(response);
            cache.delete(strippedURL);
            if (fresh) {
                cache.put(strippedURL, response.clone());
            }
            callback(null, response, fresh);
        }).catch(callback);
    }).catch(callback);
}
function isFresh(response) {
    if (!response)
        return false;
    const expires = new Date(response.headers.get('Expires') || 0);
    const cacheControl = parseCacheControl(response.headers.get('Cache-Control') || '');
    return expires > Date.now() && !cacheControl['no-cache'];
}
let globalEntryCounter = Infinity;
function cacheEntryPossiblyAdded(dispatcher) {
    globalEntryCounter++;
    if (globalEntryCounter > cacheCheckThreshold) {
        dispatcher.getActor().send('enforceCacheSizeLimit', cacheLimit);
        globalEntryCounter = 0;
    }
}
function enforceCacheSizeLimit(limit) {
    cacheOpen();
    if (!sharedCache)
        return;
    sharedCache.then(cache => {
        cache.keys().then(keys => {
            for (let i = 0; i < keys.length - limit; i++) {
                cache.delete(keys[i]);
            }
        });
    });
}
function clearTileCache(callback) {
    const promise = window$1.caches.delete(CACHE_NAME);
    if (callback) {
        promise.catch(callback).then(() => callback());
    }
}
function setCacheLimits(limit, checkThreshold) {
    cacheLimit = limit;
    cacheCheckThreshold = checkThreshold;
}

const ResourceType = {
    Unknown: 'Unknown',
    Style: 'Style',
    Source: 'Source',
    Tile: 'Tile',
    Glyphs: 'Glyphs',
    SpriteImage: 'SpriteImage',
    SpriteJSON: 'SpriteJSON',
    Image: 'Image'
};
if (typeof Object.freeze == 'function') {
    Object.freeze(ResourceType);
}
class AJAXError extends Error {
    constructor(message, status, url) {
        if (status === 401 && isMapboxHTTPURL(url)) {
            message += ': you may have provided an invalid Mapbox access token. See https://www.mapbox.com/api-documentation/#access-tokens-and-token-scopes';
        }
        super(message);
        this.status = status;
        this.url = url;
    }
    toString() {
        return `${ this.name }: ${ this.message } (${ this.status }): ${ this.url }`;
    }
}
const getReferrer = isWorker() ? () => self.worker && self.worker.referrer : () => (window$1.location.protocol === 'blob:' ? window$1.parent : window$1).location.href;
const isFileURL = url => /^file:/.test(url) || /^file:/.test(getReferrer()) && !/^\w+:/.test(url);
function makeFetchRequest(requestParameters, callback) {
    const controller = new window$1.AbortController();
    const request = new window$1.Request(requestParameters.url, {
        method: requestParameters.method || 'GET',
        body: requestParameters.body,
        credentials: requestParameters.credentials,
        headers: requestParameters.headers,
        referrer: getReferrer(),
        signal: controller.signal
    });
    let complete = false;
    let aborted = false;
    const cacheIgnoringSearch = hasCacheDefeatingSku(request.url);
    if (requestParameters.type === 'json') {
        request.headers.set('Accept', 'application/json');
    }
    const validateOrFetch = (err, cachedResponse, responseIsFresh) => {
        if (aborted)
            return;
        if (err) {
            if (err.message !== 'SecurityError') {
                warnOnce(err);
            }
        }
        if (cachedResponse && responseIsFresh) {
            return finishRequest(cachedResponse);
        }
        const requestTime = Date.now();
        window$1.fetch(request).then(response => {
            if (response.ok) {
                const cacheableResponse = cacheIgnoringSearch ? response.clone() : null;
                return finishRequest(response, cacheableResponse, requestTime);
            } else {
                return callback(new AJAXError(response.statusText, response.status, requestParameters.url));
            }
        }).catch(error => {
            if (error.code === 20) {
                return;
            }
            callback(new Error(error.message));
        });
    };
    const finishRequest = (response, cacheableResponse, requestTime) => {
        (requestParameters.type === 'arrayBuffer' ? response.arrayBuffer() : requestParameters.type === 'json' ? response.json() : response.text()).then(result => {
            if (aborted)
                return;
            if (cacheableResponse && requestTime) {
                cachePut(request, cacheableResponse, requestTime);
            }
            complete = true;
            callback(null, result, response.headers.get('Cache-Control'), response.headers.get('Expires'));
        }).catch(err => {
            if (!aborted)
                callback(new Error(err.message));
        });
    };
    if (cacheIgnoringSearch) {
        cacheGet(request, validateOrFetch);
    } else {
        validateOrFetch(null, null);
    }
    return {
        cancel: () => {
            aborted = true;
            if (!complete)
                controller.abort();
        }
    };
}
function makeXMLHttpRequest(requestParameters, callback) {
    const xhr = new window$1.XMLHttpRequest();
    xhr.open(requestParameters.method || 'GET', requestParameters.url, true);
    if (requestParameters.type === 'arrayBuffer') {
        xhr.responseType = 'arraybuffer';
    }
    for (const k in requestParameters.headers) {
        xhr.setRequestHeader(k, requestParameters.headers[k]);
    }
    if (requestParameters.type === 'json') {
        xhr.responseType = 'text';
        xhr.setRequestHeader('Accept', 'application/json');
    }
    xhr.withCredentials = requestParameters.credentials === 'include';
    xhr.onerror = () => {
        callback(new Error(xhr.statusText));
    };
    xhr.onload = () => {
        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) {
                    return callback(err);
                }
            }
            callback(null, data, xhr.getResponseHeader('Cache-Control'), xhr.getResponseHeader('Expires'));
        } else {
            callback(new AJAXError(xhr.statusText, xhr.status, requestParameters.url));
        }
    };
    xhr.send(requestParameters.body);
    return { cancel: () => xhr.abort() };
}
const makeRequest = function (requestParameters, callback) {
    if (!isFileURL(requestParameters.url)) {
        if (window$1.fetch && window$1.Request && window$1.AbortController && window$1.Request.prototype.hasOwnProperty('signal')) {
            return makeFetchRequest(requestParameters, callback);
        }
        if (isWorker() && self.worker && self.worker.actor) {
            const queueOnMainThread = true;
            return self.worker.actor.send('getResource', requestParameters, callback, undefined, queueOnMainThread);
        }
    }
    return makeXMLHttpRequest(requestParameters, callback);
};
const getJSON = function (requestParameters, callback) {
    return makeRequest(extend$1(requestParameters, { type: 'json' }), callback);
};
const getArrayBuffer = function (requestParameters, callback) {
    return makeRequest(extend$1(requestParameters, { type: 'arrayBuffer' }), callback);
};
const postData = function (requestParameters, callback) {
    return makeRequest(extend$1(requestParameters, { method: 'POST' }), callback);
};
const getData = function (requestParameters, callback) {
    return makeRequest(extend$1(requestParameters, { method: 'GET' }), callback);
};
function sameOrigin(url) {
    const a = window$1.document.createElement('a');
    a.href = url;
    return a.protocol === window$1.document.location.protocol && a.host === window$1.document.location.host;
}
const transparentPngUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII=';
function arrayBufferToImage(data, callback) {
    const img = new window$1.Image();
    const URL = window$1.URL;
    img.onload = () => {
        callback(null, img);
        URL.revokeObjectURL(img.src);
        img.onload = null;
        window$1.requestAnimationFrame(() => {
            img.src = transparentPngUrl;
        });
    };
    img.onerror = () => callback(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 window$1.Blob([new Uint8Array(data)], { type: 'image/png' });
    img.src = data.byteLength ? URL.createObjectURL(blob) : transparentPngUrl;
}
function arrayBufferToImageBitmap(data, callback) {
    const blob = new window$1.Blob([new Uint8Array(data)], { type: 'image/png' });
    window$1.createImageBitmap(blob).then(imgBitmap => {
        callback(null, imgBitmap);
    }).catch(e => {
        callback(new Error(`Could not load image because of ${ e.message }. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported.`));
    });
}
let imageQueue, numImageRequests;
const resetImageRequestQueue = () => {
    imageQueue = [];
    numImageRequests = 0;
};
resetImageRequestQueue();
const getImage = function (requestParameters, callback) {
    if (exported.supported) {
        if (!requestParameters.headers) {
            requestParameters.headers = {};
        }
        requestParameters.headers.accept = 'image/webp,*/*';
    }
    if (numImageRequests >= config.MAX_PARALLEL_IMAGE_REQUESTS) {
        const queued = {
            requestParameters,
            callback,
            cancelled: false,
            cancel() {
                this.cancelled = true;
            }
        };
        imageQueue.push(queued);
        return queued;
    }
    numImageRequests++;
    let advanced = false;
    const advanceImageRequestQueue = () => {
        if (advanced)
            return;
        advanced = true;
        numImageRequests--;
        while (imageQueue.length && numImageRequests < config.MAX_PARALLEL_IMAGE_REQUESTS) {
            const request = imageQueue.shift();
            const {requestParameters, callback, cancelled} = request;
            if (!cancelled) {
                request.cancel = getImage(requestParameters, callback).cancel;
            }
        }
    };
    const request = getArrayBuffer(requestParameters, (err, data, cacheControl, expires) => {
        advanceImageRequestQueue();
        if (err) {
            callback(err);
        } else if (data) {
            if (window$1.createImageBitmap) {
                arrayBufferToImageBitmap(data, (err, imgBitmap) => callback(err, imgBitmap, cacheControl, expires));
            } else {
                arrayBufferToImage(data, (err, img) => callback(err, img, cacheControl, expires));
            }
        }
    });
    return {
        cancel: () => {
            request.cancel();
            advanceImageRequestQueue();
        }
    };
};
const getVideo = function (urls, callback) {
    const video = window$1.document.createElement('video');
    video.muted = true;
    video.onloadstart = function () {
        callback(null, video);
    };
    for (let i = 0; i < urls.length; i++) {
        const s = window$1.document.createElement('source');
        if (!sameOrigin(urls[i])) {
            video.crossOrigin = 'Anonymous';
        }
        s.src = urls[i];
        video.appendChild(s);
    }
    return {
        cancel: () => {
        }
    };
};

function _addEventListener(type, listener, listenerList) {
    const listenerExists = listenerList[type] && listenerList[type].indexOf(listener) !== -1;
    if (!listenerExists) {
        listenerList[type] = listenerList[type] || [];
        listenerList[type].push(listener);
    }
}
function _removeEventListener(type, listener, listenerList) {
    if (listenerList && listenerList[type]) {
        const index = listenerList[type].indexOf(listener);
        if (index !== -1) {
            listenerList[type].splice(index, 1);
        }
    }
}
class Event {
    constructor(type, data = {}) {
        extend$1(this, data);
        this.type = type;
    }
}
class ErrorEvent extends Event {
    constructor(error, data = {}) {
        super('error', extend$1({ error }, data));
    }
}
class Evented {
    on(type, listener) {
        this._listeners = this._listeners || {};
        _addEventListener(type, listener, this._listeners);
        return this;
    }
    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 = this._oneTimeListeners || {};
        _addEventListener(type, listener, this._oneTimeListeners);
        return this;
    }
    fire(event, properties) {
        if (typeof event === 'string') {
            event = new Event(event, properties || {});
        }
        const type = event.type;
        if (this.listens(type)) {
            event.target = this;
            const listeners = this._listeners && this._listeners[type] ? this._listeners[type].slice() : [];
            for (const listener of listeners) {
                listener.call(this, event);
            }
            const oneTimeListeners = this._oneTimeListeners && this._oneTimeListeners[type] ? this._oneTimeListeners[type].slice() : [];
            for (const listener of oneTimeListeners) {
                _removeEventListener(type, listener, this._oneTimeListeners);
                listener.call(this, event);
            }
            const parent = this._eventedParent;
            if (parent) {
                extend$1(event, typeof this._eventedParentData === 'function' ? this._eventedParentData() : this._eventedParentData);
                parent.fire(event);
            }
        } else if (event instanceof ErrorEvent) {
            console.error(event.error);
        }
        return this;
    }
    listens(type) {
        return !!(this._listeners && this._listeners[type] && this._listeners[type].length > 0 || this._oneTimeListeners && this._oneTimeListeners[type] && this._oneTimeListeners[type].length > 0 || this._eventedParent && this._eventedParent.listens(type));
    }
    setEventedParent(parent, data) {
        this._eventedParent = parent;
        this._eventedParentData = data;
        return this;
    }
}

var $version = 8;
var $root = {
	version: {
		required: true,
		type: "enum",
		values: [
			8
		]
	},
	name: {
		type: "string"
	},
	metadata: {
		type: "*"
	},
	center: {
		type: "array",
		value: "number"
	},
	zoom: {
		type: "number"
	},
	bearing: {
		type: "number",
		"default": 0,
		period: 360,
		units: "degrees"
	},
	pitch: {
		type: "number",
		"default": 0,
		units: "degrees"
	},
	light: {
		type: "light"
	},
	terrain: {
		type: "terrain"
	},
	fog: {
		type: "fog"
	},
	sources: {
		required: true,
		type: "sources"
	},
	sprite: {
		type: "string"
	},
	glyphs: {
		type: "string"
	},
	transition: {
		type: "transition"
	},
	layers: {
		required: true,
		type: "array",
		value: "layer"
	}
};
var sources = {
	"*": {
		type: "source"
	}
};
var source = [
	"source_vector",
	"source_raster",
	"source_raster_dem",
	"source_geojson",
	"source_video",
	"source_image"
];
var 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
	},
	zoomOffset: {
		type: "number",
		"default": 0
	},
	"*": {
		type: "*"
	}
};
var 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
	},
	zoomOffset: {
		type: "number",
		"default": 0
	},
	"*": {
		type: "*"
	}
};
var 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: {
			}
		},
		"default": "mapbox"
	},
	volatile: {
		type: "boolean",
		"default": false
	},
	zoomOffset: {
		type: "number",
		"default": 0
	},
	"*": {
		type: "*"
	}
};
var source_geojson = {
	type: {
		required: true,
		type: "enum",
		values: {
			geojson: {
			}
		}
	},
	data: {
		type: "*"
	},
	maxzoom: {
		type: "number",
		"default": 18
	},
	attribution: {
		type: "string"
	},
	buffer: {
		type: "number",
		"default": 128,
		maximum: 512,
		minimum: 0
	},
	filter: {
		type: "*"
	},
	tolerance: {
		type: "number",
		"default": 0.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"
	}
};
var 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"
		}
	}
};
var 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"
		}
	}
};
var layer = {
	id: {
		type: "string",
		required: true
	},
	type: {
		type: "enum",
		values: {
			fill: {
			},
			line: {
			},
			symbol: {
			},
			circle: {
			},
			heatmap: {
			},
			"fill-extrusion": {
			},
			raster: {
			},
			hillshade: {
			},
			background: {
			},
			sky: {
			}
		},
		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"
	}
};
var layout$6 = [
	"layout_fill",
	"layout_line",
	"layout_circle",
	"layout_heatmap",
	"layout_fill-extrusion",
	"layout_symbol",
	"layout_raster",
	"layout_hillshade",
	"layout_background",
	"layout_sky"
];
var layout_background = {
	visibility: {
		type: "enum",
		values: {
			visible: {
			},
			none: {
			}
		},
		"default": "visible",
		"property-type": "constant"
	}
};
var layout_sky = {
	visibility: {
		type: "enum",
		values: {
			visible: {
			},
			none: {
			}
		},
		"default": "visible",
		"property-type": "constant"
	}
};
var 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",
		"property-type": "constant"
	}
};
var 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",
		"property-type": "constant"
	}
};
var layout_heatmap = {
	visibility: {
		type: "enum",
		values: {
			visible: {
			},
			none: {
			}
		},
		"default": "visible",
		"property-type": "constant"
	}
};
var 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"
			]
		},
		"property-type": "data-constant"
	},
	"line-round-limit": {
		type: "number",
		"default": 1.05,
		requires: [
			{
				"line-join": "round"
			}
		],
		expression: {
			interpolated: true,
			parameters: [
				"zoom"
			]
		},
		"property-type": "data-constant"
	},
	"line-sort-key": {
		type: "number",
		expression: {
			interpolated: false,
			parameters: [
				"zoom",
				"feature"
			]
		},
		"property-type": "data-driven"
	},
	visibility: {
		type: "enum",
		values: {
			visible: {
			},
			none: {
			}
		},
		"default": "visible",
		"property-type": "constant"
	}
};
var 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"
		],
		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: "number",
		"default": 2,
		minimum: 0,
		units: "pixels",
		requires: [
			"icon-image"
		],
		expression: {
			interpolated: true,
			parameters: [
				"zoom"
			]
		},
		"property-type": "data-constant"
	},
	"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: {
			},
			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",
				"feature"
			]
		},
		"property-type": "data-driven"
	},
	"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-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"
		],
		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",
		"property-type": "constant"
	}
};
var layout_raster = {
	visibility: {
		type: "enum",
		values: {
			visible: {
			},
			none: {
			}
		},
		"default": "visible",
		"property-type": "constant"
	}
};
var layout_hillshade = {
	visibility: {
		type: "enum",
		values: {
			visible: {
			},
			none: {
			}
		},
		"default": "visible",
		"property-type": "constant"
	}
};
var filter = {
	type: "array",
	value: "*"
};
var filter_operator = {
	type: "enum",
	values: {
		"==": {
		},
		"!=": {
		},
		">": {
		},
		">=": {
		},
		"<": {
		},
		"<=": {
		},
		"in": {
		},
		"!in": {
		},
		all: {
		},
		any: {
		},
		none: {
		},
		has: {
		},
		"!has": {
		},
		within: {
		}
	}
};
var geometry_type = {
	type: "enum",
	values: {
		Point: {
		},
		LineString: {
		},
		Polygon: {
		}
	}
};
var function_stop = {
	type: "array",
	minimum: 0,
	maximum: 24,
	value: [
		"number",
		"color"
	],
	length: 2
};
var expression = {
	type: "array",
	value: "*",
	minimum: 1
};
var fog = {
	range: {
		type: "array",
		"default": [
			0.5,
			10
		],
		minimum: -20,
		maximum: 20,
		length: 2,
		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
	},
	"horizon-blend": {
		type: "number",
		"property-type": "data-constant",
		"default": 0.1,
		minimum: 0,
		maximum: 1,
		expression: {
			interpolated: true,
			parameters: [
				"zoom"
			]
		},
		transition: true
	}
};
var 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": 0.5,
		minimum: 0,
		maximum: 1,
		expression: {
			interpolated: true,
			parameters: [
				"zoom"
			]
		},
		transition: true
	}
};
var terrain = {
	source: {
		type: "string",
		required: true
	},
	exaggeration: {
		type: "number",
		"property-type": "data-constant",
		"default": 1,
		minimum: 0,
		maximum: 1000,
		expression: {
			interpolated: true,
			parameters: [
				"zoom"
			]
		},
		transition: true
	}
};
var paint$a = [
	"paint_fill",
	"paint_line",
	"paint_circle",
	"paint_heatmap",
	"paint_fill-extrusion",
	"paint_symbol",
	"paint_raster",
	"paint_hillshade",
	"paint_background",
	"paint_sky"
];
var 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-color": {
		type: "color",
		"default": "#000000",
		transition: true,
		requires: [
			{
				"!": "fill-pattern"
			}
		],
		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"
	}
};
var 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-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"
	}
};
var 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"
	}
};
var 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)",
			0.1,
			"royalblue",
			0.3,
			"cyan",
			0.5,
			"lime",
			0.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"
	}
};
var 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"
	}
};
var 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"
	},
	"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"
	}
};
var paint_hillshade = {
	"hillshade-illumination-direction": {
		type: "number",
		"default": 335,
		minimum: 0,
		maximum: 359,
		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": 0.5,
		minimum: 0,
		maximum: 1,
		transition: true,
		expression: {
			interpolated: true,
			parameters: [
				"zoom"
			]
		},
		"property-type": "data-constant"
	},
	"hillshade-shadow-color": {
		type: "color",
		"default": "#000000",
		transition: true,
		expression: {
			interpolated: true,
			parameters: [
				"zoom"
			]
		},
		"property-type": "data-constant"
	},
	"hillshade-highlight-color": {
		type: "color",
		"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"
	}
};
var 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"
	}
};
var paint_sky = {
	"sky-type": {
		type: "enum",
		values: {
			gradient: {
			},
			atmosphere: {
			}
		},
		"default": "atmosphere",
		expression: {
			interpolated: false,
			parameters: [
				"zoom"
			]
		},
		"property-type": "data-constant"
	},
	"sky-atmosphere-sun": {
		type: "array",
		value: "number",
		length: 2,
		units: "degrees",
		minimum: [
			0,
			0
		],
		maximum: [
			360,
			180
		],
		transition: false,
		requires: [
			{
				"sky-type": "atmosphere"
			}
		],
		expression: {
			interpolated: false,
			parameters: [
				"zoom"
			]
		},
		"property-type": "data-constant"
	},
	"sky-atmosphere-sun-intensity": {
		type: "number",
		requires: [
			{
				"sky-type": "atmosphere"
			}
		],
		"default": 10,
		minimum: 0,
		maximum: 100,
		transition: false,
		"property-type": "data-constant"
	},
	"sky-gradient-center": {
		type: "array",
		requires: [
			{
				"sky-type": "gradient"
			}
		],
		value: "number",
		"default": [
			0,
			0
		],
		length: 2,
		units: "degrees",
		minimum: [
			0,
			0
		],
		maximum: [
			360,
			180
		],
		transition: false,
		expression: {
			interpolated: false,
			parameters: [
				"zoom"
			]
		},
		"property-type": "data-constant"
	},
	"sky-gradient-radius": {
		type: "number",
		requires: [
			{
				"sky-type": "gradient"
			}
		],
		"default": 90,
		minimum: 0,
		maximum: 180,
		transition: false,
		expression: {
			interpolated: false,
			parameters: [
				"zoom"
			]
		},
		"property-type": "data-constant"
	},
	"sky-gradient": {
		type: "color",
		"default": [
			"interpolate",
			[
				"linear"
			],
			[
				"sky-radial-progress"
			],
			0.8,
			"#87ceeb",
			1,
			"white"
		],
		transition: false,
		requires: [
			{
				"sky-type": "gradient"
			}
		],
		expression: {
			interpolated: true,
			parameters: [
				"sky-radial-progress"
			]
		},
		"property-type": "color-ramp"
	},
	"sky-atmosphere-halo-color": {
		type: "color",
		"default": "white",
		transition: false,
		requires: [
			{
				"sky-type": "atmosphere"
			}
		],
		"property-type": "data-constant"
	},
	"sky-atmosphere-color": {
		type: "color",
		"default": "white",
		transition: false,
		requires: [
			{
				"sky-type": "atmosphere"
			}
		],
		"property-type": "data-constant"
	},
	"sky-opacity": {
		type: "number",
		"default": 1,
		minimum: 0,
		maximum: 1,
		transition: true,
		expression: {
			interpolated: true,
			parameters: [
				"zoom"
			]
		},
		"property-type": "data-constant"
	}
};
var transition = {
	duration: {
		type: "number",
		"default": 300,
		minimum: 0,
		units: "milliseconds"
	},
	delay: {
		type: "number",
		"default": 0,
		minimum: 0,
		units: "milliseconds"
	}
};
var promoteId = {
	"*": {
		type: "string"
	}
};
var spec = {
	$version: $version,
	$root: $root,
	sources: sources,
	source: source,
	source_vector: source_vector,
	source_raster: source_raster,
	source_raster_dem: source_raster_dem,
	source_geojson: source_geojson,
	source_video: source_video,
	source_image: source_image,
	layer: layer,
	layout: layout$6,
	layout_background: layout_background,
	layout_sky: layout_sky,
	layout_fill: layout_fill,
	layout_circle: layout_circle,
	layout_heatmap: layout_heatmap,
	"layout_fill-extrusion": {
	visibility: {
		type: "enum",
		values: {
			visible: {
			},
			none: {
			}
		},
		"default": "visible",
		"property-type": "constant"
	}
},
	layout_line: layout_line,
	layout_symbol: layout_symbol,
	layout_raster: layout_raster,
	layout_hillshade: layout_hillshade,
	filter: filter,
	filter_operator: filter_operator,
	geometry_type: geometry_type,
	"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: function_stop,
	expression: expression,
	fog: fog,
	light: light,
	terrain: terrain,
	paint: paint$a,
	paint_fill: paint_fill,
	"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: paint_line,
	paint_circle: paint_circle,
	paint_heatmap: paint_heatmap,
	paint_symbol: paint_symbol,
	paint_raster: paint_raster,
	paint_hillshade: paint_hillshade,
	paint_background: paint_background,
	paint_sky: paint_sky,
	transition: transition,
	"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: promoteId
};

class ValidationError {
    constructor(key, value, message, identifier) {
        this.message = (key ? `${ key }: ` : '') + message;
        if (identifier)
            this.identifier = identifier;
        if (value !== null && value !== undefined && value.__line__) {
            this.line = value.__line__;
        }
    }
}

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 extend (output, ...inputs) {
    for (const input of inputs) {
        for (const k in input) {
            output[k] = input[k];
        }
    }
    return output;
}

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);
}

class ParsingError extends Error {
    constructor(key, message) {
        super(message);
        this.message = message;
        this.key = key;
    }
}
var ParsingError$1 = ParsingError;

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;
    }
}
var Scope$1 = Scope;

const NullType = { kind: 'null' };
const NumberType = { kind: 'number' };
const StringType = { kind: 'string' };
const BooleanType = { kind: 'boolean' };
const ColorType = { kind: 'color' };
const ObjectType = { kind: 'object' };
const ValueType = { kind: 'value' };
const ErrorType = { kind: 'error' };
const CollatorType = { kind: 'collator' };
const FormattedType = { kind: 'formatted' };
const ResolvedImageType = { kind: 'resolvedImage' };
function array$1(itemType, N) {
    return {
        kind: 'array',
        itemType,
        N
    };
}
function toString$1(type) {
    if (type.kind === 'array') {
        const itemType = toString$1(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,
    FormattedType,
    ObjectType,
    array$1(ValueType),
    ResolvedImageType
];
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 ${ toString$1(expected) } but found ${ toString$1(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;
        }
    });
}

function createCommonjsModule(fn) {
  var module = { exports: {} };
	return fn(module, module.exports), module.exports;
}

var csscolorparser = createCommonjsModule(function (module, exports) {
var kCSSColorTable = {
    'transparent': [
        0,
        0,
        0,
        0
    ],
    'aliceblue': [
        240,
        248,
        255,
        1
    ],
    'antiquewhite': [
        250,
        235,
        215,
        1
    ],
    'aqua': [
        0,
        255,
        255,
        1
    ],
    'aquamarine': [
        127,
        255,
        212,
        1
    ],
    'azure': [
        240,
        255,
        255,
        1
    ],
    'beige': [
        245,
        245,
        220,
        1
    ],
    'bisque': [
        255,
        228,
        196,
        1
    ],
    'black': [
        0,
        0,
        0,
        1
    ],
    'blanchedalmond': [
        255,
        235,
        205,
        1
    ],
    'blue': [
        0,
        0,
        255,
        1
    ],
    'blueviolet': [
        138,
        43,
        226,
        1
    ],
    'brown': [
        165,
        42,
        42,
        1
    ],
    'burlywood': [
        222,
        184,
        135,
        1
    ],
    'cadetblue': [
        95,
        158,
        160,
        1
    ],
    'chartreuse': [
        127,
        255,
        0,
        1
    ],
    'chocolate': [
        210,
        105,
        30,
        1
    ],
    'coral': [
        255,
        127,
        80,
        1
    ],
    'cornflowerblue': [
        100,
        149,
        237,
        1
    ],
    'cornsilk': [
        255,
        248,
        220,
        1
    ],
    'crimson': [
        220,
        20,
        60,
        1
    ],
    'cyan': [
        0,
        255,
        255,
        1
    ],
    'darkblue': [
        0,
        0,
        139,
        1
    ],
    'darkcyan': [
        0,
        139,
        139,
        1
    ],
    'darkgoldenrod': [
        184,
        134,
        11,
        1
    ],
    'darkgray': [
        169,
        169,
        169,
        1
    ],
    'darkgreen': [
        0,
        100,
        0,
        1
    ],
    'darkgrey': [
        169,
        169,
        169,
        1
    ],
    'darkkhaki': [
        189,
        183,
        107,
        1
    ],
    'darkmagenta': [
        139,
        0,
        139,
        1
    ],
    'darkolivegreen': [
        85,
        107,
        47,
        1
    ],
    'darkorange': [
        255,
        140,
        0,
        1
    ],
    'darkorchid': [
        153,
        50,
        204,
        1
    ],
    'darkred': [
        139,
        0,
        0,
        1
    ],
    'darksalmon': [
        233,
        150,
        122,
        1
    ],
    'darkseagreen': [
        143,
        188,
        143,
        1
    ],
    'darkslateblue': [
        72,
        61,
        139,
        1
    ],
    'darkslategray': [
        47,
        79,
        79,
        1
    ],
    'darkslategrey': [
        47,
        79,
        79,
        1
    ],
    'darkturquoise': [
        0,
        206,
        209,
        1
    ],
    'darkviolet': [
        148,
        0,
        211,
        1
    ],
    'deeppink': [
        255,
        20,
        147,
        1
    ],
    'deepskyblue': [
        0,
        191,
        255,
        1
    ],
    'dimgray': [
        105,
        105,
        105,
        1
    ],
    'dimgrey': [
        105,
        105,
        105,
        1
    ],
    'dodgerblue': [
        30,
        144,
        255,
        1
    ],
    'firebrick': [
        178,
        34,
        34,
        1
    ],
    'floralwhite': [
        255,
        250,
        240,
        1
    ],
    'forestgreen': [
        34,
        139,
        34,
        1
    ],
    'fuchsia': [
        255,
        0,
        255,
        1
    ],
    'gainsboro': [
        220,
        220,
        220,
        1
    ],
    'ghostwhite': [
        248,
        248,
        255,
        1
    ],
    'gold': [
        255,
        215,
        0,
        1
    ],
    'goldenrod': [
        218,
        165,
        32,
        1
    ],
    'gray': [
        128,
        128,
        128,
        1
    ],
    'green': [
        0,
        128,
        0,
        1
    ],
    'greenyellow': [
        173,
        255,
        47,
        1
    ],
    'grey': [
        128,
        128,
        128,
        1
    ],
    'honeydew': [
        240,
        255,
        240,
        1
    ],
    'hotpink': [
        255,
        105,
        180,
        1
    ],
    'indianred': [
        205,
        92,
        92,
        1
    ],
    'indigo': [
        75,
        0,
        130,
        1
    ],
    'ivory': [
        255,
        255,
        240,
        1
    ],
    'khaki': [
        240,
        230,
        140,
        1
    ],
    'lavender': [
        230,
        230,
        250,
        1
    ],
    'lavenderblush': [
        255,
        240,
        245,
        1
    ],
    'lawngreen': [
        124,
        252,
        0,
        1
    ],
    'lemonchiffon': [
        255,
        250,
        205,
        1
    ],
    'lightblue': [
        173,
        216,
        230,
        1
    ],
    'lightcoral': [
        240,
        128,
        128,
        1
    ],
    'lightcyan': [
        224,
        255,
        255,
        1
    ],
    'lightgoldenrodyellow': [
        250,
        250,
        210,
        1
    ],
    'lightgray': [
        211,
        211,
        211,
        1
    ],
    'lightgreen': [
        144,
        238,
        144,
        1
    ],
    'lightgrey': [
        211,
        211,
        211,
        1
    ],
    'lightpink': [
        255,
        182,
        193,
        1
    ],
    'lightsalmon': [
        255,
        160,
        122,
        1
    ],
    'lightseagreen': [
        32,
        178,
        170,
        1
    ],
    'lightskyblue': [
        135,
        206,
        250,
        1
    ],
    'lightslategray': [
        119,
        136,
        153,
        1
    ],
    'lightslategrey': [
        119,
        136,
        153,
        1
    ],
    'lightsteelblue': [
        176,
        196,
        222,
        1
    ],
    'lightyellow': [
        255,
        255,
        224,
        1
    ],
    'lime': [
        0,
        255,
        0,
        1
    ],
    'limegreen': [
        50,
        205,
        50,
        1
    ],
    'linen': [
        250,
        240,
        230,
        1
    ],
    'magenta': [
        255,
        0,
        255,
        1
    ],
    'maroon': [
        128,
        0,
        0,
        1
    ],
    'mediumaquamarine': [
        102,
        205,
        170,
        1
    ],
    'mediumblue': [
        0,
        0,
        205,
        1
    ],
    'mediumorchid': [
        186,
        85,
        211,
        1
    ],
    'mediumpurple': [
        147,
        112,
        219,
        1
    ],
    'mediumseagreen': [
        60,
        179,
        113,
        1
    ],
    'mediumslateblue': [
        123,
        104,
        238,
        1
    ],
    'mediumspringgreen': [
        0,
        250,
        154,
        1
    ],
    'mediumturquoise': [
        72,
        209,
        204,
        1
    ],
    'mediumvioletred': [
        199,
        21,
        133,
        1
    ],
    'midnightblue': [
        25,
        25,
        112,
        1
    ],
    'mintcream': [
        245,
        255,
        250,
        1
    ],
    'mistyrose': [
        255,
        228,
        225,
        1
    ],
    'moccasin': [
        255,
        228,
        181,
        1
    ],
    'navajowhite': [
        255,
        222,
        173,
        1
    ],
    'navy': [
        0,
        0,
        128,
        1
    ],
    'oldlace': [
        253,
        245,
        230,
        1
    ],
    'olive': [
        128,
        128,
        0,
        1
    ],
    'olivedrab': [
        107,
        142,
        35,
        1
    ],
    'orange': [
        255,
        165,
        0,
        1
    ],
    'orangered': [
        255,
        69,
        0,
        1
    ],
    'orchid': [
        218,
        112,
        214,
        1
    ],
    'palegoldenrod': [
        238,
        232,
        170,
        1
    ],
    'palegreen': [
        152,
        251,
        152,
        1
    ],
    'paleturquoise': [
        175,
        238,
        238,
        1
    ],
    'palevioletred': [
        219,
        112,
        147,
        1
    ],
    'papayawhip': [
        255,
        239,
        213,
        1
    ],
    'peachpuff': [
        255,
        218,
        185,
        1
    ],
    'peru': [
        205,
        133,
        63,
        1
    ],
    'pink': [
        255,
        192,
        203,
        1
    ],
    'plum': [
        221,
        160,
        221,
        1
    ],
    'powderblue': [
        176,
        224,
        230,
        1
    ],
    'purple': [
        128,
        0,
        128,
        1
    ],
    'rebeccapurple': [
        102,
        51,
        153,
        1
    ],
    'red': [
        255,
        0,
        0,
        1
    ],
    'rosybrown': [
        188,
        143,
        143,
        1
    ],
    'royalblue': [
        65,
        105,
        225,
        1
    ],
    'saddlebrown': [
        139,
        69,
        19,
        1
    ],
    'salmon': [
        250,
        128,
        114,
        1
    ],
    'sandybrown': [
        244,
        164,
        96,
        1
    ],
    'seagreen': [
        46,
        139,
        87,
        1
    ],
    'seashell': [
        255,
        245,
        238,
        1
    ],
    'sienna': [
        160,
        82,
        45,
        1
    ],
    'silver': [
        192,
        192,
        192,
        1
    ],
    'skyblue': [
        135,
        206,
        235,
        1
    ],
    'slateblue': [
        106,
        90,
        205,
        1
    ],
    'slategray': [
        112,
        128,
        144,
        1
    ],
    'slategrey': [
        112,
        128,
        144,
        1
    ],
    'snow': [
        255,
        250,
        250,
        1
    ],
    'springgreen': [
        0,
        255,
        127,
        1
    ],
    'steelblue': [
        70,
        130,
        180,
        1
    ],
    'tan': [
        210,
        180,
        140,
        1
    ],
    'teal': [
        0,
        128,
        128,
        1
    ],
    'thistle': [
        216,
        191,
        216,
        1
    ],
    'tomato': [
        255,
        99,
        71,
        1
    ],
    'turquoise': [
        64,
        224,
        208,
        1
    ],
    'violet': [
        238,
        130,
        238,
        1
    ],
    'wheat': [
        245,
        222,
        179,
        1
    ],
    'white': [
        255,
        255,
        255,
        1
    ],
    'whitesmoke': [
        245,
        245,
        245,
        1
    ],
    'yellow': [
        255,
        255,
        0,
        1
    ],
    'yellowgreen': [
        154,
        205,
        50,
        1
    ]
};
function clamp_css_byte(i) {
    i = Math.round(i);
    return i < 0 ? 0 : i > 255 ? 255 : i;
}
function clamp_css_float(f) {
    return f < 0 ? 0 : f > 1 ? 1 : f;
}
function parse_css_int(str) {
    if (str[str.length - 1] === '%')
        return clamp_css_byte(parseFloat(str) / 100 * 255);
    return clamp_css_byte(parseInt(str));
}
function parse_css_float(str) {
    if (str[str.length - 1] === '%')
        return clamp_css_float(parseFloat(str) / 100);
    return clamp_css_float(parseFloat(str));
}
function css_hue_to_rgb(m1, m2, h) {
    if (h < 0)
        h += 1;
    else if (h > 1)
        h -= 1;
    if (h * 6 < 1)
        return m1 + (m2 - m1) * h * 6;
    if (h * 2 < 1)
        return m2;
    if (h * 3 < 2)
        return m1 + (m2 - m1) * (2 / 3 - h) * 6;
    return m1;
}
function parseCSSColor(css_str) {
    var str = css_str.replace(/ /g, '').toLowerCase();
    if (str in kCSSColorTable)
        return kCSSColorTable[str].slice();
    if (str[0] === '#') {
        if (str.length === 4) {
            var iv = parseInt(str.substr(1), 16);
            if (!(iv >= 0 && iv <= 4095))
                return null;
            return [
                (iv & 3840) >> 4 | (iv & 3840) >> 8,
                iv & 240 | (iv & 240) >> 4,
                iv & 15 | (iv & 15) << 4,
                1
            ];
        } else if (str.length === 7) {
            var iv = parseInt(str.substr(1), 16);
            if (!(iv >= 0 && iv <= 16777215))
                return null;
            return [
                (iv & 16711680) >> 16,
                (iv & 65280) >> 8,
                iv & 255,
                1
            ];
        }
        return null;
    }
    var op = str.indexOf('('), ep = str.indexOf(')');
    if (op !== -1 && ep + 1 === str.length) {
        var fname = str.substr(0, op);
        var params = str.substr(op + 1, ep - (op + 1)).split(',');
        var alpha = 1;
        switch (fname) {
        case 'rgba':
            if (params.length !== 4)
                return null;
            alpha = parse_css_float(params.pop());
        case 'rgb':
            if (params.length !== 3)
                return null;
            return [
                parse_css_int(params[0]),
                parse_css_int(params[1]),
                parse_css_int(params[2]),
                alpha
            ];
        case 'hsla':
            if (params.length !== 4)
                return null;
            alpha = parse_css_float(params.pop());
        case 'hsl':
            if (params.length !== 3)
                return null;
            var h = (parseFloat(params[0]) % 360 + 360) % 360 / 360;
            var s = parse_css_float(params[1]);
            var l = parse_css_float(params[2]);
            var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
            var m1 = l * 2 - m2;
            return [
                clamp_css_byte(css_hue_to_rgb(m1, m2, h + 1 / 3) * 255),
                clamp_css_byte(css_hue_to_rgb(m1, m2, h) * 255),
                clamp_css_byte(css_hue_to_rgb(m1, m2, h - 1 / 3) * 255),
                alpha
            ];
        default:
            return null;
        }
    }
    return null;
}
try {
    exports.parseCSSColor = parseCSSColor;
} catch (e) {
}
});

class Color {
    constructor(r, g, b, a = 1) {
        this.r = r;
        this.g = g;
        this.b = b;
        this.a = a;
    }
    static parse(input) {
        if (!input) {
            return undefined;
        }
        if (input instanceof Color) {
            return input;
        }
        if (typeof input !== 'string') {
            return undefined;
        }
        const rgba = csscolorparser.parseCSSColor(input);
        if (!rgba) {
            return undefined;
        }
        return new Color(rgba[0] / 255 * rgba[3], rgba[1] / 255 * rgba[3], rgba[2] / 255 * rgba[3], rgba[3]);
    }
    toString() {
        const [r, g, b, a] = this.toArray();
        return `rgba(${ Math.round(r) },${ Math.round(g) },${ Math.round(b) },${ a })`;
    }
    toArray() {
        const {r, g, b, a} = this;
        return a === 0 ? [
            0,
            0,
            0,
            0
        ] : [
            r * 255 / a,
            g * 255 / a,
            b * 255 / a,
            a
        ];
    }
}
Color.black = new Color(0, 0, 0, 1);
Color.white = new Color(1, 1, 1, 1);
Color.transparent = new Color(0, 0, 0, 0);
Color.red = new Color(1, 0, 0, 1);
Color.blue = new Color(0, 0, 1, 1);
var Color$1 = Color;

class Collator {
    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;
    }
}

class FormattedSection {
    constructor(text, image, scale, fontStack, textColor) {
        this.text = text;
        this.image = image;
        this.scale = scale;
        this.fontStack = fontStack;
        this.textColor = textColor;
    }
}
class Formatted {
    constructor(sections) {
        this.sections = sections;
    }
    static fromString(unformatted) {
        return new Formatted([new FormattedSection(unformatted, 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('');
    }
    serialize() {
        const serialized = ['format'];
        for (const section of this.sections) {
            if (section.image) {
                serialized.push([
                    'image',
                    section.image.name
                ]);
                continue;
            }
            serialized.push(section.text);
            const options = {};
            if (section.fontStack) {
                options['text-font'] = [
                    'literal',
                    section.fontStack.split(',')
                ];
            }
            if (section.scale) {
                options['font-scale'] = section.scale;
            }
            if (section.textColor) {
                options['text-color'] = ['rgba'].concat(section.textColor.toArray());
            }
            serialized.push(options);
        }
        return serialized;
    }
}

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
        });
    }
    serialize() {
        return [
            'image',
            this.name
        ];
    }
}

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)) {
        const value = typeof a === 'number' ? [
            r,
            g,
            b,
            a
        ] : [
            r,
            g,
            b
        ];
        return `Invalid rgba value [${ value.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) {
        return true;
    } else if (typeof mixed === 'string') {
        return true;
    } else if (typeof mixed === 'boolean') {
        return true;
    } else if (typeof mixed === 'number') {
        return true;
    } else if (mixed instanceof Color$1) {
        return true;
    } else if (mixed instanceof Collator) {
        return true;
    } else if (mixed instanceof Formatted) {
        return true;
    } else if (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$1) {
        return ColorType;
    } else if (value instanceof Collator) {
        return CollatorType;
    } else if (value instanceof Formatted) {
        return FormattedType;
    } 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$1(itemType || ValueType, length);
    } else {
        return ObjectType;
    }
}
function toString(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$1 || value instanceof Formatted || value instanceof ResolvedImage) {
        return value.toString();
    } else {
        return JSON.stringify(value);
    }
}

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;
    }
    serialize() {
        if (this.type.kind === 'array' || this.type.kind === 'object') {
            return [
                'literal',
                this.value
            ];
        } else if (this.value instanceof Color$1) {
            return ['rgba'].concat(this.value.toArray());
        } else if (this.value instanceof Formatted) {
            return this.value.serialize();
        } else {
            return this.value;
        }
    }
}
var Literal$1 = Literal;

class RuntimeError {
    constructor(message) {
        this.name = 'ExpressionEvaluationError';
        this.message = message;
    }
    toJSON() {
        return this.message;
    }
}
var RuntimeError$1 = RuntimeError;

const types$1 = {
    string: StringType,
    number: NumberType,
    boolean: BooleanType,
    object: ObjectType
};
class Assertion {
    constructor(type, args) {
        this.type = type;
        this.args = args;
    }
    static parse(args, context) {
        if (args.length < 2)
            return context.error(`Expected at least one argument.`);
        let i = 1;
        let type;
        const name = args[0];
        if (name === 'array') {
            let itemType;
            if (args.length > 2) {
                const type = args[1];
                if (typeof type !== 'string' || !(type in types$1) || type === 'object')
                    return context.error('The item type argument of "array" must be one of string, number, boolean', 1);
                itemType = types$1[type];
                i++;
            } else {
                itemType = ValueType;
            }
            let N;
            if (args.length > 3) {
                if (args[2] !== null && (typeof args[2] !== 'number' || args[2] < 0 || args[2] !== Math.floor(args[2]))) {
                    return context.error('The length argument to "array" must be a positive integer literal', 2);
                }
                N = args[2];
                i++;
            }
            type = array$1(itemType, N);
        } else {
            type = types$1[name];
        }
        const parsed = [];
        for (; i < args.length; i++) {
            const input = context.parse(args[i], i, ValueType);
            if (!input)
                return null;
            parsed.push(input);
        }
        return new Assertion(type, parsed);
    }
    evaluate(ctx) {
        for (let i = 0; i < this.args.length; i++) {
            const value = this.args[i].evaluate(ctx);
            const error = checkSubtype(this.type, typeOf(value));
            if (!error) {
                return value;
            } else if (i === this.args.length - 1) {
                throw new RuntimeError$1(`Expected value to be of type ${ toString$1(this.type) }, but found ${ toString$1(typeOf(value)) } instead.`);
            }
        }
        return null;
    }
    eachChild(fn) {
        this.args.forEach(fn);
    }
    outputDefined() {
        return this.args.every(arg => arg.outputDefined());
    }
    serialize() {
        const type = this.type;
        const serialized = [type.kind];
        if (type.kind === 'array') {
            const itemType = type.itemType;
            if (itemType.kind === 'string' || itemType.kind === 'number' || itemType.kind === 'boolean') {
                serialized.push(itemType.kind);
                const N = type.N;
                if (typeof N === 'number' || this.args.length > 1) {
                    serialized.push(N);
                }
            }
        }
        return serialized.concat(this.args.map(arg => arg.serialize()));
    }
}
var Assertion$1 = Assertion;

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$1(StringType));
                    if (!font)
                        return null;
                }
                let textColor = null;
                if (arg['text-color']) {
                    textColor = context.parse(arg['text-color'], 1, ColorType);
                    if (!textColor)
                        return null;
                }
                const lastExpression = sections[sections.length - 1];
                lastExpression.scale = scale;
                lastExpression.font = font;
                lastExpression.textColor = textColor;
            } 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
                });
            }
        }
        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);
            }
            return new FormattedSection(toString(evaluatedContent), null, section.scale ? section.scale.evaluate(ctx) : null, section.font ? section.font.evaluate(ctx).join(',') : null, section.textColor ? section.textColor.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);
            }
        }
    }
    outputDefined() {
        return false;
    }
    serialize() {
        const serialized = ['format'];
        for (const section of this.sections) {
            serialized.push(section.content.serialize());
            const options = {};
            if (section.scale) {
                options['font-scale'] = section.scale.serialize();
            }
            if (section.font) {
                options['text-font'] = section.font.serialize();
            }
            if (section.textColor) {
                options['text-color'] = section.textColor.serialize();
            }
            serialized.push(options);
        }
        return serialized;
    }
}

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;
    }
    serialize() {
        return [
            'image',
            this.input.serialize()
        ];
    }
}

const types = {
    'to-boolean': BooleanType,
    'to-color': ColorType,
    'to-number': NumberType,
    'to-string': StringType
};
class Coercion {
    constructor(type, args) {
        this.type = type;
        this.args = args;
    }
    static parse(args, context) {
        if (args.length < 2)
            return context.error(`Expected at least one argument.`);
        const name = args[0];
        if ((name === 'to-boolean' || name === 'to-string') && args.length !== 2)
            return context.error(`Expected one argument.`);
        const type = types[name];
        const parsed = [];
        for (let i = 1; i < args.length; i++) {
            const input = context.parse(args[i], i, ValueType);
            if (!input)
                return null;
            parsed.push(input);
        }
        return new Coercion(type, parsed);
    }
    evaluate(ctx) {
        if (this.type.kind === 'boolean') {
            return Boolean(this.args[0].evaluate(ctx));
        } else if (this.type.kind === 'color') {
            let input;
            let error;
            for (const arg of this.args) {
                input = arg.evaluate(ctx);
                error = null;
                if (input instanceof Color$1) {
                    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 rbga 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$1(input[0] / 255, input[1] / 255, input[2] / 255, input[3]);
                    }
                }
            }
            throw new RuntimeError$1(error || `Could not parse color from value '${ typeof input === 'string' ? input : String(JSON.stringify(input)) }'`);
        } else if (this.type.kind === '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$1(`Could not convert ${ JSON.stringify(value) } to number.`);
        } else if (this.type.kind === 'formatted') {
            return Formatted.fromString(toString(this.args[0].evaluate(ctx)));
        } else if (this.type.kind === 'resolvedImage') {
            return ResolvedImage.fromString(toString(this.args[0].evaluate(ctx)));
        } else {
            return toString(this.args[0].evaluate(ctx));
        }
    }
    eachChild(fn) {
        this.args.forEach(fn);
    }
    outputDefined() {
        return this.args.every(arg => arg.outputDefined());
    }
    serialize() {
        if (this.type.kind === 'formatted') {
            return new FormatExpression([{
                    content: this.args[0],
                    scale: null,
                    font: null,
                    textColor: null
                }]).serialize();
        }
        if (this.type.kind === 'resolvedImage') {
            return new ImageExpression(this.args[0]).serialize();
        }
        const serialized = [`to-${ this.type.kind }`];
        this.eachChild(child => {
            serialized.push(child.serialize());
        });
        return serialized;
    }
}
var Coercion$1 = Coercion;

const geometryTypes = [
    'Unknown',
    'Point',
    'LineString',
    'Polygon'
];
class EvaluationContext {
    constructor() {
        this.globals = null;
        this.feature = null;
        this.featureState = null;
        this.formattedSection = null;
        this._parseColorCache = {};
        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[input];
        if (!cached) {
            cached = this._parseColorCache[input] = Color$1.parse(input);
        }
        return cached;
    }
}
var EvaluationContext$1 = EvaluationContext;

class CompoundExpression {
    constructor(name, type, evaluate, args) {
        this.name = name;
        this.type = type;
        this._evaluate = evaluate;
        this.args = args;
    }
    evaluate(ctx) {
        return this._evaluate(ctx, this.args);
    }
    eachChild(fn) {
        this.args.forEach(fn);
    }
    outputDefined() {
        return false;
    }
    serialize() {
        return [this.name].concat(this.args.map(arg => arg.serialize()));
    }
    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$1(context.registry, 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);
            }
        }
        if (overloads.length === 1) {
            context.errors.push(...signatureContext.errors);
        } else {
            const expected = overloads.length ? overloads : availableOverloads;
            const signatures = expected.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(toString$1(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 stringifySignature(signature) {
    if (Array.isArray(signature)) {
        return `(${ signature.map(toString$1).join(', ') })`;
    } else {
        return `(${ toString$1(signature.type) }...)`;
    }
}
var CompoundExpression$1 = CompoundExpression;

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'] === undefined ? false : options['case-sensitive'], 1, BooleanType);
        if (!caseSensitive)
            return null;
        const diacriticSensitive = context.parse(options['diacritic-sensitive'] === undefined ? 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;
    }
    serialize() {
        const options = {};
        options['case-sensitive'] = this.caseSensitive.serialize();
        options['diacritic-sensitive'] = this.diacriticSensitive.serialize();
        if (this.locale) {
            options['locale'] = this.locale.serialize();
        }
        return [
            'collator',
            options
        ];
    }
}

const EXTENT$1 = 8192;
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 mercatorXfromLng$1(lng) {
    return (180 + lng) / 360;
}
function mercatorYfromLat$1(lat) {
    return (90 - lat) / 360;
}
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 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$1),
        Math.round(y * tilesAtZoom * EXTENT$1)
    ];
}
function onBoundary(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 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 pointWithinPolygon(point, rings) {
    let inside = false;
    for (let i = 0, len = rings.length; i < len; i++) {
        const ring = rings[i];
        for (let j = 0, len2 = ring.length; j < len2 - 1; j++) {
            if (onBoundary(point, ring[j], ring[j + 1]))
                return false;
            if (rayIntersect(point, ring[j], ring[j + 1]))
                inside = !inside;
        }
    }
    return inside;
}
function pointWithinPolygons(point, polygons) {
    for (let i = 0; i < polygons.length; i++) {
        if (pointWithinPolygon(point, polygons[i]))
            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 lineIntersectLine(a, b, c, d) {
    const vectorP = [
        b[0] - a[0],
        b[1] - a[1]
    ];
    const vectorQ = [
        d[0] - c[0],
        d[1] - c[1]
    ];
    if (perp(vectorQ, 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 (lineIntersectLine(p1, p2, ring[j], ring[j + 1])) {
                return true;
            }
        }
    }
    return false;
}
function lineStringWithinPolygon(line, polygon) {
    for (let i = 0; i < line.length; ++i) {
        if (!pointWithinPolygon(line[i], 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 (let i = 0; i < polygons.length; i++) {
        if (lineStringWithinPolygon(line, polygons[i]))
            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 * 0.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$1;
    const shifts = [
        canonical.x * EXTENT$1,
        canonical.y * EXTENT$1
    ];
    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$1;
    const shifts = [
        canonical.x * EXTENT$1,
        canonical.y * EXTENT$1
    ];
    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;
}
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') {
                for (let i = 0; i < geojson.features.length; ++i) {
                    const type = geojson.features[i].geometry.type;
                    if (type === 'Polygon' || type === 'MultiPolygon') {
                        return new Within(geojson, geojson.features[i].geometry);
                    }
                }
            } 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;
    }
    serialize() {
        return [
            'within',
            this.geojson
        ];
    }
}
var Within$1 = Within;

function isFeatureConstant(e) {
    if (e instanceof CompoundExpression$1) {
        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$1) {
        return false;
    }
    let result = true;
    e.eachChild(arg => {
        if (result && !isFeatureConstant(arg)) {
            result = false;
        }
    });
    return result;
}
function isStateConstant(e) {
    if (e instanceof CompoundExpression$1) {
        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$1 && properties.indexOf(e.name) >= 0) {
        return false;
    }
    let result = true;
    e.eachChild(arg => {
        if (result && !isGlobalPropertyConstant(arg, properties)) {
            result = false;
        }
    });
    return result;
}

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;
    }
    serialize() {
        return [
            'var',
            this.name
        ];
    }
}
var Var$1 = Var;

class ParsingContext {
    constructor(registry, path = [], expectedType, scope = new Scope$1(), errors = []) {
        this.registry = registry;
        this.path = path;
        this.key = path.map(part => `[${ part }]`).join('');
        this.scope = scope;
        this.errors = errors;
        this.expectedType = expectedType;
    }
    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
            ];
        }
        function annotate(parsed, type, typeAnnotation) {
            if (typeAnnotation === 'assert') {
                return new Assertion$1(type, [parsed]);
            } else if (typeAnnotation === 'coerce') {
                return new Coercion$1(type, [parsed]);
            } 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 ((expected.kind === 'color' || expected.kind === 'formatted' || expected.kind === 'resolvedImage') && (actual.kind === 'value' || actual.kind === 'string')) {
                        parsed = annotate(parsed, expected, options.typeAnnotation || 'coerce');
                    } else if (this.checkSubtype(expected, actual)) {
                        return null;
                    }
                }
                if (!(parsed instanceof Literal$1) && parsed.type.kind !== 'resolvedImage' && isConstant(parsed)) {
                    const ec = new EvaluationContext$1();
                    try {
                        parsed = new Literal$1(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.`);
        }
    }
    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, path, expectedType || null, scope, this.errors);
    }
    error(error, ...keys) {
        const key = `${ this.key }${ keys.map(k => `[${ k }]`).join('') }`;
        this.errors.push(new ParsingError$1(key, error));
    }
    checkSubtype(expected, t) {
        const error = checkSubtype(expected, t);
        if (error)
            this.error(error);
        return error;
    }
}
var ParsingContext$1 = ParsingContext;
function isConstant(expression) {
    if (expression instanceof Var$1) {
        return isConstant(expression.boundExpression);
    } else if (expression instanceof CompoundExpression$1 && expression.name === 'error') {
        return false;
    } else if (expression instanceof CollatorExpression) {
        return false;
    } else if (expression instanceof Within$1) {
        return false;
    }
    const isTypeAnnotation = expression instanceof Coercion$1 || expression instanceof Assertion$1;
    let childrenConstant = true;
    expression.eachChild(child => {
        if (isTypeAnnotation) {
            childrenConstant = childrenConstant && isConstant(child);
        } else {
            childrenConstant = childrenConstant && child instanceof Literal$1;
        }
    });
    if (!childrenConstant) {
        return false;
    }
    return isFeatureConstant(expression) && isGlobalPropertyConstant(expression, [
        'zoom',
        'heatmap-density',
        'line-progress',
        'sky-radial-progress',
        'accumulated',
        'is-supported-script'
    ]);
}

function findStopLessThanOrEqualTo(stops, input) {
    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$1('Input is not a number.');
        }
    }
    return 0;
}

class Step {
    constructor(type, input, stops) {
        this.type = type;
        this.input = input;
        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);
    }
    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);
        return outputs[index].evaluate(ctx);
    }
    eachChild(fn) {
        fn(this.input);
        for (const expression of this.outputs) {
            fn(expression);
        }
    }
    outputDefined() {
        return this.outputs.every(out => out.outputDefined());
    }
    serialize() {
        const serialized = [
            'step',
            this.input.serialize()
        ];
        for (let i = 0; i < this.labels.length; i++) {
            if (i > 0) {
                serialized.push(this.labels[i]);
            }
            serialized.push(this.outputs[i].serialize());
        }
        return serialized;
    }
}
var Step$1 = Step;

function number(a, b, t) {
    return a * (1 - t) + b * t;
}
function color(from, to, t) {
    return new Color$1(number(from.r, to.r, t), number(from.g, to.g, t), number(from.b, to.b, t), number(from.a, to.a, t));
}
function array(from, to, t) {
    return from.map((d, i) => {
        return number(d, to[i], t);
    });
}

var interpolate = /*#__PURE__*/Object.freeze({
__proto__: null,
number: number,
color: color,
array: array
});

const Xn = 0.95047, Yn = 1, Zn = 1.08883, t0 = 4 / 29, t1 = 6 / 29, t2 = 3 * t1 * t1, t3 = t1 * t1 * t1, deg2rad = Math.PI / 180, rad2deg = 180 / Math.PI;
function xyz2lab(t) {
    return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;
}
function lab2xyz(t) {
    return t > t1 ? t * t * t : t2 * (t - t0);
}
function xyz2rgb(x) {
    return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);
}
function rgb2xyz(x) {
    x /= 255;
    return x <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
}
function rgbToLab(rgbColor) {
    const b = rgb2xyz(rgbColor.r), a = rgb2xyz(rgbColor.g), l = rgb2xyz(rgbColor.b), x = xyz2lab((0.4124564 * b + 0.3575761 * a + 0.1804375 * l) / Xn), y = xyz2lab((0.2126729 * b + 0.7151522 * a + 0.072175 * l) / Yn), z = xyz2lab((0.0193339 * b + 0.119192 * a + 0.9503041 * l) / Zn);
    return {
        l: 116 * y - 16,
        a: 500 * (x - y),
        b: 200 * (y - z),
        alpha: rgbColor.a
    };
}
function labToRgb(labColor) {
    let y = (labColor.l + 16) / 116, x = isNaN(labColor.a) ? y : y + labColor.a / 500, z = isNaN(labColor.b) ? y : y - labColor.b / 200;
    y = Yn * lab2xyz(y);
    x = Xn * lab2xyz(x);
    z = Zn * lab2xyz(z);
    return new Color$1(xyz2rgb(3.2404542 * x - 1.5371385 * y - 0.4985314 * z), xyz2rgb(-0.969266 * x + 1.8760108 * y + 0.041556 * z), xyz2rgb(0.0556434 * x - 0.2040259 * y + 1.0572252 * z), labColor.alpha);
}
function interpolateLab(from, to, t) {
    return {
        l: number(from.l, to.l, t),
        a: number(from.a, to.a, t),
        b: number(from.b, to.b, t),
        alpha: number(from.alpha, to.alpha, t)
    };
}
function rgbToHcl(rgbColor) {
    const {l, a, b} = rgbToLab(rgbColor);
    const h = Math.atan2(b, a) * rad2deg;
    return {
        h: h < 0 ? h + 360 : h,
        c: Math.sqrt(a * a + b * b),
        l,
        alpha: rgbColor.a
    };
}
function hclToRgb(hclColor) {
    const h = hclColor.h * deg2rad, c = hclColor.c, l = hclColor.l;
    return labToRgb({
        l,
        a: Math.cos(h) * c,
        b: Math.sin(h) * c,
        alpha: hclColor.alpha
    });
}
function interpolateHue(a, b, t) {
    const d = b - a;
    return a + t * (d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d);
}
function interpolateHcl(from, to, t) {
    return {
        h: interpolateHue(from.h, to.h, t),
        c: number(from.c, to.c, t),
        l: number(from.l, to.l, t),
        alpha: number(from.alpha, to.alpha, t)
    };
}
const lab = {
    forward: rgbToLab,
    reverse: labToRgb,
    interpolate: interpolateLab
};
const hcl = {
    forward: rgbToHcl,
    reverse: hclToRgb,
    interpolate: interpolateHcl
};

var colorSpaces = /*#__PURE__*/Object.freeze({
__proto__: null,
lab: lab,
hcl: hcl
});

class Interpolate {
    constructor(type, operator, interpolation, input, stops) {
        this.type = type;
        this.operator = operator;
        this.interpolation = interpolation;
        this.input = input;
        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;
            const ub = new unitbezier(c[0], c[1], c[2], c[3]);
            t = ub.solve(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: 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') {
            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 (outputType.kind !== 'number' && outputType.kind !== 'color' && !(outputType.kind === 'array' && outputType.itemType.kind === 'number' && typeof outputType.N === 'number')) {
            return context.error(`Type ${ toString$1(outputType) } is not interpolatable.`);
        }
        return new Interpolate(outputType, operator, interpolation, input, stops);
    }
    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);
        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);
        if (this.operator === 'interpolate') {
            return interpolate[this.type.kind.toLowerCase()](outputLower, outputUpper, t);
        } else if (this.operator === 'interpolate-hcl') {
            return hcl.reverse(hcl.interpolate(hcl.forward(outputLower), hcl.forward(outputUpper), t));
        } else {
            return lab.reverse(lab.interpolate(lab.forward(outputLower), lab.forward(outputUpper), t));
        }
    }
    eachChild(fn) {
        fn(this.input);
        for (const expression of this.outputs) {
            fn(expression);
        }
    }
    outputDefined() {
        return this.outputs.every(out => out.outputDefined());
    }
    serialize() {
        let interpolation;
        if (this.interpolation.name === 'linear') {
            interpolation = ['linear'];
        } else if (this.interpolation.name === 'exponential') {
            if (this.interpolation.base === 1) {
                interpolation = ['linear'];
            } else {
                interpolation = [
                    'exponential',
                    this.interpolation.base
                ];
            }
        } else {
            interpolation = ['cubic-bezier'].concat(this.interpolation.controlPoints);
        }
        const serialized = [
            this.operator,
            interpolation,
            this.input.serialize()
        ];
        for (let i = 0; i < this.labels.length; i++) {
            serialized.push(this.labels[i], this.outputs[i].serialize());
        }
        return serialized;
    }
}
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);
    }
}
var Interpolate$1 = Interpolate;

class Coalesce {
    constructor(type, args) {
        this.type = type;
        this.args = args;
    }
    static parse(args, context) {
        if (args.length < 2) {
            return context.error('Expectected 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, undefined, { typeAnnotation: 'omit' });
            if (!parsed)
                return null;
            outputType = outputType || parsed.type;
            parsedArgs.push(parsed);
        }
        const needsAnnotation = expectedType && parsedArgs.some(arg => checkSubtype(expectedType, arg.type));
        return needsAnnotation ? 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());
    }
    serialize() {
        const serialized = ['coalesce'];
        this.eachChild(child => {
            serialized.push(child.serialize());
        });
        return serialized;
    }
}
var Coalesce$1 = Coalesce;

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();
    }
    serialize() {
        const serialized = ['let'];
        for (const [name, expr] of this.bindings) {
            serialized.push(name, expr.serialize());
        }
        serialized.push(this.result.serialize());
        return serialized;
    }
}
var Let$1 = Let;

class At {
    constructor(type, index, input) {
        this.type = type;
        this.index = index;
        this.input = input;
    }
    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$1(context.expectedType || ValueType));
        if (!index || !input)
            return null;
        const t = input.type;
        return new At(t.itemType, index, input);
    }
    evaluate(ctx) {
        const index = this.index.evaluate(ctx);
        const array = this.input.evaluate(ctx);
        if (index < 0) {
            throw new RuntimeError$1(`Array index out of bounds: ${ index } < 0.`);
        }
        if (index >= array.length) {
            throw new RuntimeError$1(`Array index out of bounds: ${ index } > ${ array.length - 1 }.`);
        }
        if (index !== Math.floor(index)) {
            throw new RuntimeError$1(`Array index must be an integer, but found ${ index } instead.`);
        }
        return array[index];
    }
    eachChild(fn) {
        fn(this.index);
        fn(this.input);
    }
    outputDefined() {
        return false;
    }
    serialize() {
        return [
            'at',
            this.index.serialize(),
            this.input.serialize()
        ];
    }
}
var At$1 = At;

class In {
    constructor(needle, haystack) {
        this.type = BooleanType;
        this.needle = needle;
        this.haystack = haystack;
    }
    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 ${ toString$1(needle.type) } instead`);
        }
        return new In(needle, haystack);
    }
    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$1(`Expected first argument to be of type boolean, string, number or null, but found ${ toString$1(typeOf(needle)) } instead.`);
        }
        if (!isValidNativeType(haystack, [
                'string',
                'array'
            ])) {
            throw new RuntimeError$1(`Expected second argument to be of type array or string, but found ${ toString$1(typeOf(haystack)) } instead.`);
        }
        return haystack.indexOf(needle) >= 0;
    }
    eachChild(fn) {
        fn(this.needle);
        fn(this.haystack);
    }
    outputDefined() {
        return true;
    }
    serialize() {
        return [
            'in',
            this.needle.serialize(),
            this.haystack.serialize()
        ];
    }
}
var In$1 = In;

class IndexOf {
    constructor(needle, haystack, fromIndex) {
        this.type = NumberType;
        this.needle = needle;
        this.haystack = haystack;
        this.fromIndex = fromIndex;
    }
    static parse(args, context) {
        if (args.length <= 2 || args.length >= 5) {
            return context.error(`Expected 3 or 4 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 ${ toString$1(needle.type) } instead`);
        }
        if (args.length === 4) {
            const fromIndex = context.parse(args[3], 3, NumberType);
            if (!fromIndex)
                return null;
            return new IndexOf(needle, haystack, fromIndex);
        } else {
            return new IndexOf(needle, haystack);
        }
    }
    evaluate(ctx) {
        const needle = this.needle.evaluate(ctx);
        const haystack = this.haystack.evaluate(ctx);
        if (!isValidNativeType(needle, [
                'boolean',
                'string',
                'number',
                'null'
            ])) {
            throw new RuntimeError$1(`Expected first argument to be of type boolean, string, number or null, but found ${ toString$1(typeOf(needle)) } instead.`);
        }
        if (!isValidNativeType(haystack, [
                'string',
                'array'
            ])) {
            throw new RuntimeError$1(`Expected second argument to be of type array or string, but found ${ toString$1(typeOf(haystack)) } instead.`);
        }
        if (this.fromIndex) {
            const fromIndex = this.fromIndex.evaluate(ctx);
            return haystack.indexOf(needle, fromIndex);
        }
        return haystack.indexOf(needle);
    }
    eachChild(fn) {
        fn(this.needle);
        fn(this.haystack);
        if (this.fromIndex) {
            fn(this.fromIndex);
        }
    }
    outputDefined() {
        return false;
    }
    serialize() {
        if (this.fromIndex != null && this.fromIndex !== undefined) {
            const fromIndex = this.fromIndex.serialize();
            return [
                'index-of',
                this.needle.serialize(),
                this.haystack.serialize(),
                fromIndex
            ];
        }
        return [
            'index-of',
            this.needle.serialize(),
            this.haystack.serialize()
        ];
    }
}
var IndexOf$1 = IndexOf;

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);
        const output = typeOf(input) === this.inputType && this.outputs[this.cases[input]] || this.otherwise;
        return output.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();
    }
    serialize() {
        const serialized = [
            'match',
            this.input.serialize()
        ];
        const sortedLabels = Object.keys(this.cases).sort();
        const groupedByOutput = [];
        const outputLookup = {};
        for (const label of sortedLabels) {
            const outputIndex = outputLookup[this.cases[label]];
            if (outputIndex === undefined) {
                outputLookup[this.cases[label]] = groupedByOutput.length;
                groupedByOutput.push([
                    this.cases[label],
                    [label]
                ]);
            } else {
                groupedByOutput[outputIndex][1].push(label);
            }
        }
        const coerceLabel = label => this.inputType.kind === 'number' ? Number(label) : label;
        for (const [outputIndex, labels] of groupedByOutput) {
            if (labels.length === 1) {
                serialized.push(coerceLabel(labels[0]));
            } else {
                serialized.push(labels.map(coerceLabel));
            }
            serialized.push(this.outputs[outputIndex].serialize());
        }
        serialized.push(this.otherwise.serialize());
        return serialized;
    }
}
var Match$1 = Match;

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;
        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();
    }
    serialize() {
        const serialized = ['case'];
        this.eachChild(child => {
            serialized.push(child.serialize());
        });
        return serialized;
    }
}
var Case$1 = Case;

class Slice {
    constructor(type, input, beginIndex, endIndex) {
        this.type = type;
        this.input = input;
        this.beginIndex = beginIndex;
        this.endIndex = endIndex;
    }
    static parse(args, context) {
        if (args.length <= 2 || args.length >= 5) {
            return context.error(`Expected 3 or 4 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$1(ValueType),
                StringType,
                ValueType
            ])) {
            return context.error(`Expected first argument to be of type array or string, but found ${ toString$1(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, endIndex);
        } else {
            return new Slice(input.type, input, beginIndex);
        }
    }
    evaluate(ctx) {
        const input = this.input.evaluate(ctx);
        const beginIndex = this.beginIndex.evaluate(ctx);
        if (!isValidNativeType(input, [
                'string',
                'array'
            ])) {
            throw new RuntimeError$1(`Expected first argument to be of type array or string, but found ${ toString$1(typeOf(input)) } instead.`);
        }
        if (this.endIndex) {
            const endIndex = this.endIndex.evaluate(ctx);
            return input.slice(beginIndex, endIndex);
        }
        return input.slice(beginIndex);
    }
    eachChild(fn) {
        fn(this.input);
        fn(this.beginIndex);
        if (this.endIndex) {
            fn(this.endIndex);
        }
    }
    outputDefined() {
        return false;
    }
    serialize() {
        if (this.endIndex != null && this.endIndex !== undefined) {
            const endIndex = this.endIndex.serialize();
            return [
                'slice',
                this.input.serialize(),
                this.beginIndex.serialize(),
                endIndex
            ];
        }
        return [
            'slice',
            this.input.serialize(),
            this.beginIndex.serialize()
        ];
    }
}
var Slice$1 = Slice;

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;
}
function makeComparison(op, compareBasic, compareWithCollator) {
    const isOrderComparison = op !== '==' && op !== '!=';
    return class Comparison {
        constructor(lhs, rhs, collator) {
            this.type = BooleanType;
            this.lhs = lhs;
            this.rhs = rhs;
            this.collator = collator;
            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 '${ toString$1(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 '${ toString$1(rhs.type) }'.`);
            }
            if (lhs.type.kind !== rhs.type.kind && lhs.type.kind !== 'value' && rhs.type.kind !== 'value') {
                return context.error(`Cannot compare types '${ toString$1(lhs.type) }' and '${ toString$1(rhs.type) }'.`);
            }
            if (isOrderComparison) {
                if (lhs.type.kind === 'value' && rhs.type.kind !== 'value') {
                    lhs = new Assertion$1(rhs.type, [lhs]);
                } else if (lhs.type.kind !== 'value' && rhs.type.kind === 'value') {
                    rhs = new Assertion$1(lhs.type, [rhs]);
                }
            }
            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, 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$1(`Expected arguments for "${ op }" to be (string, string) or (number, number), but found (${ lt.kind }, ${ rt.kind }) instead.`);
                }
            }
            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;
        }
        serialize() {
            const serialized = [op];
            this.eachChild(child => {
                serialized.push(child.serialize());
            });
            return serialized;
        }
    };
}
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);

class NumberFormat {
    constructor(number, locale, currency, minFractionDigits, maxFractionDigits) {
        this.type = StringType;
        this.number = number;
        this.locale = locale;
        this.currency = currency;
        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 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, minFractionDigits, maxFractionDigits);
    }
    evaluate(ctx) {
        return new Intl.NumberFormat(this.locale ? this.locale.evaluate(ctx) : [], {
            style: this.currency ? 'currency' : 'decimal',
            currency: this.currency ? this.currency.evaluate(ctx) : undefined,
            minimumFractionDigits: this.minFractionDigits ? this.minFractionDigits.evaluate(ctx) : undefined,
            maximumFractionDigits: this.maxFractionDigits ? this.maxFractionDigits.evaluate(ctx) : undefined
        }).format(this.number.evaluate(ctx));
    }
    eachChild(fn) {
        fn(this.number);
        if (this.locale) {
            fn(this.locale);
        }
        if (this.currency) {
            fn(this.currency);
        }
        if (this.minFractionDigits) {
            fn(this.minFractionDigits);
        }
        if (this.maxFractionDigits) {
            fn(this.maxFractionDigits);
        }
    }
    outputDefined() {
        return false;
    }
    serialize() {
        const options = {};
        if (this.locale) {
            options['locale'] = this.locale.serialize();
        }
        if (this.currency) {
            options['currency'] = this.currency.serialize();
        }
        if (this.minFractionDigits) {
            options['min-fraction-digits'] = this.minFractionDigits.serialize();
        }
        if (this.maxFractionDigits) {
            options['max-fraction-digits'] = this.maxFractionDigits.serialize();
        }
        return [
            'number-format',
            this.number.serialize(),
            options
        ];
    }
}

class Length {
    constructor(input) {
        this.type = NumberType;
        this.input = input;
    }
    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 ${ toString$1(input.type) } instead.`);
        return new Length(input);
    }
    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$1(`Expected value to be of type string or array, but found ${ toString$1(typeOf(input)) } instead.`);
        }
    }
    eachChild(fn) {
        fn(this.input);
    }
    outputDefined() {
        return false;
    }
    serialize() {
        const serialized = ['length'];
        this.eachChild(child => {
            serialized.push(child.serialize());
        });
        return serialized;
    }
}
var Length$1 = Length;

const expressions = {
    '==': Equals,
    '!=': NotEquals,
    '>': GreaterThan,
    '<': LessThan,
    '>=': GreaterThanOrEqual,
    '<=': LessThanOrEqual,
    'array': Assertion$1,
    'at': At$1,
    'boolean': Assertion$1,
    'case': Case$1,
    'coalesce': Coalesce$1,
    'collator': CollatorExpression,
    'format': FormatExpression,
    'image': ImageExpression,
    'in': In$1,
    'index-of': IndexOf$1,
    'interpolate': Interpolate$1,
    'interpolate-hcl': Interpolate$1,
    'interpolate-lab': Interpolate$1,
    'length': Length$1,
    'let': Let$1,
    'literal': Literal$1,
    'match': Match$1,
    'number': Assertion$1,
    'number-format': NumberFormat,
    'object': Assertion$1,
    'slice': Slice$1,
    'step': Step$1,
    'string': Assertion$1,
    'to-boolean': Coercion$1,
    'to-color': Coercion$1,
    'to-number': Coercion$1,
    'to-string': Coercion$1,
    'var': Var$1,
    'within': Within$1
};
function rgba(ctx, [r, g, b, a]) {
    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$1(error);
    return new Color$1(r / 255 * alpha, g / 255 * alpha, b / 255 * alpha, alpha);
}
function has(key, obj) {
    return key in obj;
}
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$1.register(expressions, {
    'error': [
        ErrorType,
        [StringType],
        (ctx, [v]) => {
            throw new RuntimeError$1(v.evaluate(ctx));
        }
    ],
    'typeof': [
        StringType,
        [ValueType],
        (ctx, [v]) => toString$1(typeOf(v.evaluate(ctx)))
    ],
    'to-rgba': [
        array$1(NumberType, 4),
        [ColorType],
        (ctx, [v]) => {
            return v.evaluate(ctx).toArray();
        }
    ],
    '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
    ],
    'line-progress': [
        NumberType,
        [],
        ctx => ctx.globals.lineProgress || 0
    ],
    'sky-radial-progress': [
        NumberType,
        [],
        ctx => ctx.globals.skyRadialProgress || 0
    ],
    'accumulated': [
        ValueType,
        [],
        ctx => ctx.globals.accumulated === undefined ? 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]) => k.value in ctx.properties()
    ],
    'filter-has-id': [
        BooleanType,
        [],
        ctx => ctx.id() !== null && ctx.id() !== undefined
    ],
    'filter-type-in': [
        BooleanType,
        [array$1(StringType)],
        (ctx, [v]) => v.value.indexOf(ctx.geometryType()) >= 0
    ],
    'filter-id-in': [
        BooleanType,
        [array$1(ValueType)],
        (ctx, [v]) => v.value.indexOf(ctx.id()) >= 0
    ],
    'filter-in-small': [
        BooleanType,
        [
            StringType,
            array$1(ValueType)
        ],
        (ctx, [k, v]) => v.value.indexOf(ctx.properties()[k.value]) >= 0
    ],
    'filter-in-large': [
        BooleanType,
        [
            StringType,
            array$1(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 => toString(arg.evaluate(ctx))).join('')
    ],
    'resolved-locale': [
        StringType,
        [CollatorType],
        (ctx, [collator]) => collator.evaluate(ctx).resolvedLocale()
    ]
});
var expressions$1 = expressions;

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 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);
}
function identityFunction(x) {
    return x;
}
function createFunction(parameters, propertySpec) {
    const isColor = propertySpec.type === 'color';
    const zoomAndFeatureDependent = parameters.stops && typeof parameters.stops[0][0] === 'object';
    const featureDependent = zoomAndFeatureDependent || parameters.property !== undefined;
    const zoomDependent = zoomAndFeatureDependent || !featureDependent;
    const type = parameters.type || (supportsInterpolation(propertySpec) ? 'exponential' : 'interval');
    if (isColor) {
        parameters = extend({}, parameters);
        if (parameters.stops) {
            parameters.stops = parameters.stops.map(stop => {
                return [
                    stop[0],
                    Color$1.parse(stop[1])
                ];
            });
        }
        if (parameters.default) {
            parameters.default = Color$1.parse(parameters.default);
        } else {
            parameters.default = Color$1.parse(propertySpec.default);
        }
    }
    if (parameters.colorSpace && parameters.colorSpace !== 'rgb' && !colorSpaces[parameters.colorSpace]) {
        throw new Error(`Unknown color space: ${ parameters.colorSpace }`);
    }
    let innerFun;
    let hashedStops;
    let categoricalKeyType;
    if (type === 'exponential') {
        innerFun = evaluateExponentialFunction;
    } else if (type === 'interval') {
        innerFun = evaluateIntervalFunction;
    } else if (type === 'categorical') {
        innerFun = evaluateCategoricalFunction;
        hashedStops = Object.create(null);
        for (const stop of parameters.stops) {
            hashedStops[stop[0]] = stop[1];
        }
        categoricalKeyType = typeof parameters.stops[0][0];
    } else if (type === 'identity') {
        innerFun = evaluateIdentityFunction;
    } else {
        throw new Error(`Unknown function type "${ type }"`);
    }
    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] === undefined) {
                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$1.interpolationFactor.bind(undefined, 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 !== undefined ? parameters.base : 1
        } : null;
        return {
            kind: 'camera',
            interpolationType,
            interpolationFactor: Interpolate$1.interpolationFactor.bind(undefined, 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] : undefined;
                if (value === undefined) {
                    return coalesce(parameters.default, propertySpec.default);
                }
                return innerFun(parameters, propertySpec, value, hashedStops, categoricalKeyType);
            }
        };
    }
}
function coalesce(a, b, c) {
    if (a !== undefined)
        return a;
    if (b !== undefined)
        return b;
    if (c !== undefined)
        return c;
}
function evaluateCategoricalFunction(parameters, propertySpec, input, hashedStops, keyType) {
    const evaluated = typeof input === keyType ? hashedStops[input] : undefined;
    return coalesce(evaluated, parameters.default, propertySpec.default);
}
function evaluateIntervalFunction(parameters, propertySpec, input) {
    if (getType(input) !== 'number')
        return coalesce(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 !== undefined ? parameters.base : 1;
    if (getType(input) !== 'number')
        return coalesce(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];
    let interp = interpolate[propertySpec.type] || identityFunction;
    if (parameters.colorSpace && parameters.colorSpace !== 'rgb') {
        const colorspace = colorSpaces[parameters.colorSpace];
        interp = (a, b) => colorspace.reverse(colorspace.interpolate(colorspace.forward(a), colorspace.forward(b), t));
    }
    if (typeof outputLower.evaluate === 'function') {
        return {
            evaluate(...args) {
                const evaluatedLower = outputLower.evaluate.apply(undefined, args);
                const evaluatedUpper = outputUpper.evaluate.apply(undefined, args);
                if (evaluatedLower === undefined || evaluatedUpper === undefined) {
                    return undefined;
                }
                return interp(evaluatedLower, evaluatedUpper, t);
            }
        };
    }
    return interp(outputLower, outputUpper, t);
}
function evaluateIdentityFunction(parameters, propertySpec, input) {
    if (propertySpec.type === 'color') {
        input = Color$1.parse(input);
    } else if (propertySpec.type === 'formatted') {
        input = Formatted.fromString(input.toString());
    } else if (propertySpec.type === 'resolvedImage') {
        input = ResolvedImage.fromString(input.toString());
    } else if (getType(input) !== propertySpec.type && (propertySpec.type !== 'enum' || !propertySpec.values[input])) {
        input = undefined;
    }
    return coalesce(input, parameters.default, propertySpec.default);
}
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);
    }
}

class StyleExpression {
    constructor(expression, propertySpec) {
        this.expression = expression;
        this._warningHistory = {};
        this._evaluator = new EvaluationContext$1();
        this._defaultValue = propertySpec ? getDefaultValue(propertySpec) : null;
        this._enumValues = propertySpec && propertySpec.type === 'enum' ? propertySpec.values : null;
    }
    evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection) {
        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) {
        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 === undefined || typeof val === 'number' && val !== val) {
                return this._defaultValue;
            }
            if (this._enumValues && !(val in this._enumValues)) {
                throw new RuntimeError$1(`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) {
            if (!this._warningHistory[e.message]) {
                this._warningHistory[e.message] = true;
                if (typeof console !== 'undefined') {
                    console.warn(e.message);
                }
            }
            return this._defaultValue;
        }
    }
}
function isExpression(expression) {
    return Array.isArray(expression) && expression.length > 0 && typeof expression[0] === 'string' && expression[0] in expressions$1;
}
function createExpression(expression, propertySpec) {
    const parser = new ParsingContext$1(expressions$1, [], propertySpec ? getExpectedType(propertySpec) : undefined);
    const parsed = parser.parse(expression, undefined, undefined, undefined, propertySpec && propertySpec.type === 'string' ? { typeAnnotation: 'coerce' } : undefined);
    if (!parsed) {
        return error(parser.errors);
    }
    return success(new StyleExpression(parsed, propertySpec));
}
class ZoomConstantExpression {
    constructor(kind, expression) {
        this.kind = kind;
        this._styleExpression = expression;
        this.isStateDependent = kind !== 'constant' && !isStateConstant(expression.expression);
    }
    evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection) {
        return this._styleExpression.evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection);
    }
    evaluate(globals, feature, featureState, canonical, availableImages, formattedSection) {
        return this._styleExpression.evaluate(globals, feature, featureState, canonical, availableImages, formattedSection);
    }
}
class ZoomDependentExpression {
    constructor(kind, expression, zoomStops, interpolationType) {
        this.kind = kind;
        this.zoomStops = zoomStops;
        this._styleExpression = expression;
        this.isStateDependent = kind !== 'camera' && !isStateConstant(expression.expression);
        this.interpolationType = interpolationType;
    }
    evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection) {
        return this._styleExpression.evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection);
    }
    evaluate(globals, feature, featureState, canonical, availableImages, formattedSection) {
        return this._styleExpression.evaluate(globals, feature, featureState, canonical, availableImages, formattedSection);
    }
    interpolationFactor(input, lower, upper) {
        if (this.interpolationType) {
            return Interpolate$1.interpolationFactor(this.interpolationType, input, lower, upper);
        } else {
            return 0;
        }
    }
}
function createPropertyExpression(expression, propertySpec) {
    expression = createExpression(expression, propertySpec);
    if (expression.result === 'error') {
        return expression;
    }
    const parsed = expression.value.expression;
    const isFeatureConstant$1 = isFeatureConstant(parsed);
    if (!isFeatureConstant$1 && !supportsPropertyExpression(propertySpec)) {
        return error([new ParsingError$1('', 'data expressions not supported')]);
    }
    const isZoomConstant = isGlobalPropertyConstant(parsed, ['zoom']);
    if (!isZoomConstant && !supportsZoomExpression(propertySpec)) {
        return error([new ParsingError$1('', 'zoom expressions not supported')]);
    }
    const zoomCurve = findZoomCurve(parsed);
    if (!zoomCurve && !isZoomConstant) {
        return error([new ParsingError$1('', '"zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.')]);
    } else if (zoomCurve instanceof ParsingError$1) {
        return error([zoomCurve]);
    } else if (zoomCurve instanceof Interpolate$1 && !supportsInterpolation(propertySpec)) {
        return error([new ParsingError$1('', '"interpolate" expressions cannot be used with this property')]);
    }
    if (!zoomCurve) {
        return success(isFeatureConstant$1 ? new ZoomConstantExpression('constant', expression.value) : new ZoomConstantExpression('source', expression.value));
    }
    const interpolationType = zoomCurve instanceof Interpolate$1 ? zoomCurve.interpolation : undefined;
    return success(isFeatureConstant$1 ? new ZoomDependentExpression('camera', expression.value, zoomCurve.labels, interpolationType) : new ZoomDependentExpression('composite', expression.value, zoomCurve.labels, interpolationType));
}
class StylePropertyFunction {
    constructor(parameters, specification) {
        this._parameters = parameters;
        this._specification = specification;
        extend(this, createFunction(this._parameters, this._specification));
    }
    static deserialize(serialized) {
        return new StylePropertyFunction(serialized._parameters, serialized._specification);
    }
    static serialize(input) {
        return {
            _parameters: input._parameters,
            _specification: input._specification
        };
    }
}
function normalizePropertyExpression(value, specification) {
    if (isFunction(value)) {
        return new StylePropertyFunction(value, specification);
    } else if (isExpression(value)) {
        const expression = createPropertyExpression(value, specification);
        if (expression.result === 'error') {
            throw new Error(expression.value.map(err => `${ err.key }: ${ err.message }`).join(', '));
        }
        return expression.value;
    } else {
        let constant = value;
        if (typeof value === 'string' && specification.type === 'color') {
            constant = Color$1.parse(value);
        }
        return {
            kind: 'constant',
            evaluate: () => constant
        };
    }
}
function findZoomCurve(expression) {
    let result = null;
    if (expression instanceof Let$1) {
        result = findZoomCurve(expression.result);
    } else if (expression instanceof Coalesce$1) {
        for (const arg of expression.args) {
            result = findZoomCurve(arg);
            if (result) {
                break;
            }
        }
    } else if ((expression instanceof Step$1 || expression instanceof Interpolate$1) && expression.input instanceof CompoundExpression$1 && expression.input.name === 'zoom') {
        result = expression;
    }
    if (result instanceof ParsingError$1) {
        return result;
    }
    expression.eachChild(child => {
        const childResult = findZoomCurve(child);
        if (childResult instanceof ParsingError$1) {
            result = childResult;
        } else if (!result && childResult) {
            result = new ParsingError$1('', '"zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.');
        } else if (result && childResult && result !== childResult) {
            result = new ParsingError$1('', 'Only one zoom-based "step" or "interpolate" subexpression may be used in an expression.');
        }
    });
    return result;
}
function getExpectedType(spec) {
    const types = {
        color: ColorType,
        string: StringType,
        number: NumberType,
        enum: StringType,
        boolean: BooleanType,
        formatted: FormattedType,
        resolvedImage: ResolvedImageType
    };
    if (spec.type === 'array') {
        return array$1(types[spec.value] || ValueType, spec.length);
    }
    return types[spec.type];
}
function getDefaultValue(spec) {
    if (spec.type === 'color' && isFunction(spec.default)) {
        return new Color$1(0, 0, 0, 0);
    } else if (spec.type === 'color') {
        return Color$1.parse(spec.default) || null;
    } else if (spec.default === undefined) {
        return null;
    } else {
        return spec.default;
    }
}

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;
    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 = elementSpecs[elementSpecKey] || elementSpecs['*'];
        let validateElement;
        if (elementValidators[elementSpecKey]) {
            validateElement = elementValidators[elementSpecKey];
        } else if (elementSpecs[elementSpecKey]) {
            validateElement = validate;
        } else if (elementValidators['*']) {
            validateElement = elementValidators['*'];
        } else if (elementSpecs['*']) {
            validateElement = validate;
        } 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
        }, object));
    }
    for (const elementSpecKey in elementSpecs) {
        if (elementValidators[elementSpecKey]) {
            continue;
        }
        if (elementSpecs[elementSpecKey].required && elementSpecs[elementSpecKey]['default'] === undefined && object[elementSpecKey] === undefined) {
            errors.push(new ValidationError(key, object, `missing required property "${ elementSpecKey }"`));
        }
    }
    return errors;
}

function validateArray(options) {
    const array = options.value;
    const arraySpec = options.valueSpec;
    const style = options.style;
    const styleSpec = options.styleSpec;
    const key = options.key;
    const validateArrayElement = options.arrayElementValidator || validate;
    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`)];
    }
    if (arraySpec['min-length'] && array.length < arraySpec['min-length']) {
        return [new ValidationError(key, array, `array length at least ${ arraySpec['min-length'] } expected, length ${ array.length } found`)];
    }
    let arrayElementSpec = {
        'type': arraySpec.value,
        'values': arraySpec.values,
        'minimum': arraySpec.minimum,
        'maximum': arraySpec.maximum
    };
    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,
            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) {
        let specMin = valueSpec.minimum;
        if (getType(valueSpec.minimum) === 'array') {
            const i = options.arrayIndex;
            specMin = valueSpec.minimum[i];
        }
        if (value < specMin) {
            return [new ValidationError(key, value, `${ value } is less than the minimum value ${ specMin }`)];
        }
    }
    if ('maximum' in valueSpec) {
        let specMax = valueSpec.maximum;
        if (getType(valueSpec.maximum) === 'array') {
            const i = options.arrayIndex;
            specMax = valueSpec.maximum[i];
        }
        if (value > specMax) {
            return [new ValidationError(key, value, `${ value } is greater than the maximum value ${ specMax }`)];
        }
    }
    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 === undefined;
    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,
        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 === undefined) {
        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,
            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 === undefined) {
                return [new ValidationError(key, value, 'object stop key must have zoom')];
            }
            if (value[0].value === undefined) {
                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 = undefined;
                stopDomainValues = {};
            }
            errors = errors.concat(validateObject({
                key: `${ key }[0]`,
                value: value[0],
                valueSpec: { zoom: {} },
                style: options.style,
                styleSpec: options.styleSpec,
                objectElementValidators: {
                    zoom: validateNumber,
                    value: validateStopDomainValue
                }
            }));
        } else {
            errors = errors.concat(validateStopDomainValue({
                key: `${ key }[0]`,
                value: value[0],
                valueSpec: {},
                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(validate({
            key: `${ key }[1]`,
            value: value[1],
            valueSpec: functionValueSpec,
            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 === undefined) {
                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 !== undefined && 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 validate({
            key: options.key,
            value: options.value,
            valueSpec: functionValueSpec,
            style: options.style,
            styleSpec: options.styleSpec
        });
    }
}

function validateExpression(options) {
    const expression = (options.expressionContext === 'property' ? createPropertyExpression : createExpression)(deepUnbundle(options.value), 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 (csscolorparser.parseCSSColor(value) === null) {
        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 isExpressionFilter(filter) {
    if (filter === true || filter === false) {
        return true;
    }
    if (!Array.isArray(filter) || filter.length === 0) {
        return false;
    }
    switch (filter[0]) {
    case 'has':
        return filter.length >= 2 && filter[1] !== '$id' && filter[1] !== '$type';
    case 'in':
        return filter.length >= 3 && (typeof filter[1] !== 'string' || Array.isArray(filter[2]));
    case '!in':
    case '!has':
    case 'none':
        return false;
    case '==':
    case '!=':
    case '>':
    case '>=':
    case '<':
    case '<=':
        return filter.length !== 3 || (Array.isArray(filter[1]) || Array.isArray(filter[2]));
    case 'any':
    case 'all':
        for (const f of filter.slice(1)) {
            if (!isExpressionFilter(f) && typeof f !== 'boolean') {
                return false;
            }
        }
        return true;
    default:
        return true;
    }
}
const filterSpec = {
    'type': 'boolean',
    'default': false,
    'transition': false,
    'property-type': 'data-driven',
    'expression': {
        'interpolated': false,
        'parameters': [
            'zoom',
            'feature'
        ]
    }
};
function createFilter(filter) {
    if (filter === null || filter === undefined) {
        return {
            filter: () => true,
            needGeometry: false
        };
    }
    if (!isExpressionFilter(filter)) {
        filter = convertFilter(filter);
    }
    const compiled = createExpression(filter, filterSpec);
    if (compiled.result === 'error') {
        throw new Error(compiled.value.map(err => `${ err.key }: ${ err.message }`).join(', '));
    } else {
        const needGeometry = geometryNeeded(filter);
        return {
            filter: (globalProperties, feature, canonical) => compiled.value.evaluate(globalProperties, feature, {}, canonical),
            needGeometry
        };
    }
}
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')
        return true;
    for (let index = 1; index < filter.length; index++) {
        if (geometryNeeded(filter[index]))
            return true;
    }
    return false;
}
function convertFilter(filter) {
    if (!filter)
        return true;
    const op = filter[0];
    if (filter.length <= 1)
        return op !== 'any';
    const converted = op === '==' ? convertComparisonOp(filter[1], filter[2], '==') : op === '!=' ? convertNegation(convertComparisonOp(filter[1], filter[2], '==')) : op === '<' || op === '>' || op === '<=' || op === '>=' ? convertComparisonOp(filter[1], filter[2], op) : op === 'any' ? convertDisjunctionOp(filter.slice(1)) : op === 'all' ? ['all'].concat(filter.slice(1).map(convertFilter)) : op === 'none' ? ['all'].concat(filter.slice(1).map(convertFilter).map(convertNegation)) : op === 'in' ? convertInOp(filter[1], filter.slice(2)) : op === '!in' ? convertNegation(convertInOp(filter[1], filter.slice(2))) : op === 'has' ? convertHasOp(filter[1]) : op === '!has' ? convertNegation(convertHasOp(filter[1])) : op === 'within' ? filter : true;
    return converted;
}
function convertComparisonOp(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));
}
function convertInOp(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(property) {
    switch (property) {
    case '$type':
        return true;
    case '$id':
        return [`filter-has-id`];
    default:
        return [
            `filter-has`,
            property
        ];
    }
}
function convertNegation(filter) {
    return [
        '!',
        filter
    ];
}

function validateFilter(options) {
    if (isExpressionFilter(deepUnbundle(options.value))) {
        return validateExpression(extend({}, options, {
            expressionContext: 'filter',
            valueSpec: { value: 'boolean' }
        }));
    } else {
        return validateNonExpressionFilter(options);
    }
}
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`));
        }
        break;
    case 'within':
        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 !== 'object') {
            errors.push(new ValidationError(`${ key }[1]`, value[1], `object expected, ${ type } found`));
        }
        break;
    }
    return errors;
}

function validateProperty(options, propertyType) {
    const key = options.key;
    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 validate({
            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\n` + `Use an identity property function instead: \`{ "type": "identity", "property": ${ JSON.stringify(tokenMatch[1]) } }\`.`)];
    }
    const errors = [];
    if (options.layerType === 'symbol') {
        if (propertyKey === 'text-field' && style && !style.glyphs) {
            errors.push(new ValidationError(key, value, 'use of "text-field" requires a style "glyphs" property'));
        }
        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(validate({
        key: options.key,
        value,
        valueSpec,
        style,
        styleSpec,
        expressionContext: 'property',
        propertyType,
        propertyKey
    }));
}

function validatePaintProperty$1(options) {
    return validateProperty(options, 'paint');
}

function validateLayoutProperty$1(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 (!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' || type === 'sky')) {
        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' && 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') {
                errors.push(new ValidationError(key, layer.source, 'raster-dem source can only be used with layer type \'hillshade\'.'));
            } 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.`));
            }
        }
    }
    errors = errors.concat(validateObject({
        key,
        value: layer,
        valueSpec: styleSpec.layer,
        style: options.style,
        styleSpec: options.styleSpec,
        objectElementValidators: {
            '*'() {
                return [];
            },
            type() {
                return validate({
                    key: `${ key }.type`,
                    value: layer.type,
                    valueSpec: styleSpec.layer.type,
                    style: options.style,
                    styleSpec: options.styleSpec,
                    object: layer,
                    objectKey: 'type'
                });
            },
            filter: validateFilter,
            layout(options) {
                return validateObject({
                    layer,
                    key: options.key,
                    value: options.value,
                    style: options.style,
                    styleSpec: options.styleSpec,
                    objectElementValidators: {
                        '*'(options) {
                            return validateLayoutProperty$1(extend({ layerType: type }, options));
                        }
                    }
                });
            },
            paint(options) {
                return validateObject({
                    layer,
                    key: options.key,
                    value: options.value,
                    style: options.style,
                    styleSpec: options.styleSpec,
                    objectElementValidators: {
                        '*'(options) {
                            return validatePaintProperty$1(extend({ 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 [];
}

const objectElementValidators = { promoteId: validatePromoteId };
function validateSource(options) {
    const value = options.value;
    const key = options.key;
    const styleSpec = options.styleSpec;
    const style = options.style;
    if (!value.type) {
        return [new ValidationError(key, value, '"type" is required')];
    }
    const type = unbundle(value.type);
    let errors;
    switch (type) {
    case 'vector':
    case 'raster':
    case 'raster-dem':
        errors = validateObject({
            key,
            value,
            valueSpec: styleSpec[`source_${ type.replace('-', '_') }`],
            style: options.style,
            styleSpec,
            objectElementValidators
        });
        return errors;
    case 'geojson':
        errors = validateObject({
            key,
            value,
            valueSpec: styleSpec.source_geojson,
            style,
            styleSpec,
            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,
                    expressionContext: 'cluster-map'
                }));
                errors.push(...validateExpression({
                    key: `${ key }.${ prop }.reduce`,
                    value: reduceExpr,
                    expressionContext: 'cluster-reduce'
                }));
            }
        }
        return errors;
    case 'video':
        return validateObject({
            key,
            value,
            valueSpec: styleSpec.source_video,
            style,
            styleSpec
        });
    case 'image':
        return validateObject({
            key,
            value,
            valueSpec: styleSpec.source_image,
            style,
            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,
            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$1(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 === undefined) {
        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(validate({
                key,
                value: light[key],
                valueSpec: styleSpec.transition,
                style,
                styleSpec
            }));
        } else if (lightSpec[key]) {
            errors = errors.concat(validate({
                key,
                value: light[key],
                valueSpec: lightSpec[key],
                style,
                styleSpec
            }));
        } else {
            errors = errors.concat([new ValidationError(key, light[key], `unknown property "${ key }"`)]);
        }
    }
    return errors;
}

function validateTerrain(options) {
    const terrain = options.value;
    const key = options.key;
    const style = options.style;
    const styleSpec = options.styleSpec;
    const terrainSpec = styleSpec.terrain;
    let errors = [];
    const rootType = getType(terrain);
    if (terrain === undefined) {
        return errors;
    } else if (rootType !== 'object') {
        errors = errors.concat([new ValidationError('terrain', terrain, `object expected, ${ rootType } found`)]);
        return errors;
    }
    for (const key in terrain) {
        const transitionMatch = key.match(/^(.*)-transition$/);
        if (transitionMatch && terrainSpec[transitionMatch[1]] && terrainSpec[transitionMatch[1]].transition) {
            errors = errors.concat(validate({
                key,
                value: terrain[key],
                valueSpec: styleSpec.transition,
                style,
                styleSpec
            }));
        } else if (terrainSpec[key]) {
            errors = errors.concat(validate({
                key,
                value: terrain[key],
                valueSpec: terrainSpec[key],
                style,
                styleSpec
            }));
        } else {
            errors = errors.concat([new ValidationError(key, terrain[key], `unknown property "${ key }"`)]);
        }
    }
    if (!terrain.source) {
        errors.push(new ValidationError(key, terrain, `terrain is missing required property "source"`));
    } else {
        const source = style.sources && style.sources[terrain.source];
        const sourceType = source && unbundle(source.type);
        if (!source) {
            errors.push(new ValidationError(key, terrain.source, `source "${ terrain.source }" not found`));
        } else if (sourceType !== 'raster-dem') {
            errors.push(new ValidationError(key, terrain.source, `terrain cannot be used with a source of type ${ sourceType }, it only be used with a "raster-dem" source type`));
        }
    }
    return errors;
}

function validateFog$1(options) {
    const fog = options.value;
    const style = options.style;
    const styleSpec = options.styleSpec;
    const fogSpec = styleSpec.fog;
    let errors = [];
    const rootType = getType(fog);
    if (fog === undefined) {
        return errors;
    } else if (rootType !== 'object') {
        errors = errors.concat([new ValidationError('fog', fog, `object expected, ${ rootType } found`)]);
        return errors;
    }
    if (fog.range && !isExpression(deepUnbundle(fog.range)) && fog.range[0] >= fog.range[1]) {
        errors = errors.concat([new ValidationError('fog', fog, 'fog.range[0] can\'t be greater than or equal to fog.range[1]')]);
    }
    for (const key in fog) {
        const transitionMatch = key.match(/^(.*)-transition$/);
        if (transitionMatch && fogSpec[transitionMatch[1]] && fogSpec[transitionMatch[1]].transition) {
            errors = errors.concat(validate({
                key,
                value: fog[key],
                valueSpec: styleSpec.transition,
                style,
                styleSpec
            }));
        } else if (fogSpec[key]) {
            errors = errors.concat(validate({
                key,
                value: fog[key],
                valueSpec: fogSpec[key],
                style,
                styleSpec
            }));
        } else {
            errors = errors.concat([new ValidationError(key, fog[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);
}

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$1,
    'terrain': validateTerrain,
    'fog': validateFog$1,
    'string': validateString,
    'formatted': validateFormatted,
    'resolvedImage': validateImage
};
function validate(options) {
    const value = options.value;
    const valueSpec = options.valueSpec;
    const styleSpec = options.styleSpec;
    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 {
        const valid = validateObject(extend({}, options, { valueSpec: valueSpec.type ? styleSpec[valueSpec.type] : valueSpec }));
        return valid;
    }
}

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;
}

function validateStyleMin(style, styleSpec = spec) {
    let errors = [];
    errors = errors.concat(validate({
        key: '',
        value: style,
        valueSpec: styleSpec.$root,
        styleSpec,
        style,
        objectElementValidators: {
            glyphs: validateGlyphsURL,
            '*'() {
                return [];
            }
        }
    }));
    if (style.constants) {
        errors = errors.concat(validateConstants({
            key: 'constants',
            value: style.constants,
            style,
            styleSpec
        }));
    }
    return sortErrors(errors);
}
validateStyleMin.source = wrapCleanErrors(validateSource);
validateStyleMin.light = wrapCleanErrors(validateLight$1);
validateStyleMin.terrain = wrapCleanErrors(validateTerrain);
validateStyleMin.fog = wrapCleanErrors(validateFog$1);
validateStyleMin.layer = wrapCleanErrors(validateLayer);
validateStyleMin.filter = wrapCleanErrors(validateFilter);
validateStyleMin.paintProperty = wrapCleanErrors(validatePaintProperty$1);
validateStyleMin.layoutProperty = wrapCleanErrors(validateLayoutProperty$1);
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 validateStyle = validateStyleMin;
validateStyle.source;
const validateLight = validateStyle.light;
validateStyle.terrain;
const validateFog = validateStyle.fog;
validateStyle.filter;
const validatePaintProperty = validateStyle.paintProperty;
const validateLayoutProperty = validateStyle.layoutProperty;
function emitValidationErrors(emitter, errors) {
    let hasErrors = false;
    if (errors && errors.length) {
        for (const error of errors) {
            emitter.fire(new ErrorEvent(new Error(error.message)));
            hasErrors = true;
        }
    }
    return hasErrors;
}

var gridIndex = GridIndex;
var NUM_PARAMS = 3;
function GridIndex(extent, n, padding) {
    var cells = this.cells = [];
    if (extent instanceof ArrayBuffer) {
        this.arrayBuffer = extent;
        var array = new Int32Array(this.arrayBuffer);
        extent = array[0];
        n = array[1];
        padding = array[2];
        this.d = n + 2 * padding;
        for (var k = 0; k < this.d * this.d; k++) {
            var start = array[NUM_PARAMS + k];
            var end = array[NUM_PARAMS + k + 1];
            cells.push(start === end ? null : array.subarray(start, end));
        }
        var keysOffset = array[NUM_PARAMS + cells.length];
        var 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 (var 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;
    var p = padding / n * extent;
    this.min = -p;
    this.max = extent + p;
}
GridIndex.prototype.insert = function (key, x1, y1, x2, y2) {
    this._forEachCell(x1, y1, x2, y2, this._insertCell, this.uid++);
    this.keys.push(key);
    this.bboxes.push(x1);
    this.bboxes.push(y1);
    this.bboxes.push(x2);
    this.bboxes.push(y2);
};
GridIndex.prototype._insertReadonly = function () {
    throw 'Cannot insert into a GridIndex created from an ArrayBuffer.';
};
GridIndex.prototype._insertCell = function (x1, y1, x2, y2, cellIndex, uid) {
    this.cells[cellIndex].push(uid);
};
GridIndex.prototype.query = function (x1, y1, x2, y2, intersectionTest) {
    var min = this.min;
    var max = this.max;
    if (x1 <= min && y1 <= min && max <= x2 && max <= y2 && !intersectionTest) {
        return Array.prototype.slice.call(this.keys);
    } else {
        var result = [];
        var seenUids = {};
        this._forEachCell(x1, y1, x2, y2, this._queryCell, result, seenUids, intersectionTest);
        return result;
    }
};
GridIndex.prototype._queryCell = function (x1, y1, x2, y2, cellIndex, result, seenUids, intersectionTest) {
    var cell = this.cells[cellIndex];
    if (cell !== null) {
        var keys = this.keys;
        var bboxes = this.bboxes;
        for (var u = 0; u < cell.length; u++) {
            var uid = cell[u];
            if (seenUids[uid] === undefined) {
                var 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;
                }
            }
        }
    }
};
GridIndex.prototype._forEachCell = function (x1, y1, x2, y2, fn, arg1, arg2, intersectionTest) {
    var cx1 = this._convertToCellCoord(x1);
    var cy1 = this._convertToCellCoord(y1);
    var cx2 = this._convertToCellCoord(x2);
    var cy2 = this._convertToCellCoord(y2);
    for (var x = cx1; x <= cx2; x++) {
        for (var y = cy1; y <= cy2; y++) {
            var 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;
        }
    }
};
GridIndex.prototype._convertFromCellCoord = function (x) {
    return (x - this.padding) / this.scale;
};
GridIndex.prototype._convertToCellCoord = function (x) {
    return Math.max(0, Math.min(this.d - 1, Math.floor(x * this.scale) + this.padding));
};
GridIndex.prototype.toArrayBuffer = function () {
    if (this.arrayBuffer)
        return this.arrayBuffer;
    var cells = this.cells;
    var metadataLength = NUM_PARAMS + this.cells.length + 1 + 1;
    var totalCellLength = 0;
    for (var i = 0; i < this.cells.length; i++) {
        totalCellLength += this.cells[i].length;
    }
    var array = new Int32Array(metadataLength + totalCellLength + this.keys.length + this.bboxes.length);
    array[0] = this.extent;
    array[1] = this.n;
    array[2] = this.padding;
    var offset = metadataLength;
    for (var k = 0; k < cells.length; k++) {
        var 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;
};

const {ImageData: ImageData$1, ImageBitmap: ImageBitmap$1} = window$1;
const registry = {};
function register(name, klass, options = {}) {
    Object.defineProperty(klass, '_classRegistryKey', {
        value: name,
        writeable: false
    });
    registry[name] = {
        klass,
        omit: options.omit || [],
        shallow: options.shallow || []
    };
}
register('Object', Object);
gridIndex.serialize = function serialize(grid, transferables) {
    const buffer = grid.toArrayBuffer();
    if (transferables) {
        transferables.push(buffer);
    }
    return { buffer };
};
gridIndex.deserialize = function deserialize(serialized) {
    return new gridIndex(serialized.buffer);
};
register('Grid', gridIndex);
register('Color', Color$1);
register('Error', Error);
register('ResolvedImage', ResolvedImage);
register('StylePropertyFunction', StylePropertyFunction);
register('StyleExpression', StyleExpression, { omit: ['_evaluator'] });
register('ZoomDependentExpression', ZoomDependentExpression);
register('ZoomConstantExpression', ZoomConstantExpression);
register('CompoundExpression', CompoundExpression$1, { omit: ['_evaluate'] });
for (const name in expressions$1) {
    if (expressions$1[name]._classRegistryKey)
        continue;
    register(`Expression_${ name }`, expressions$1[name]);
}
function isArrayBuffer(val) {
    return val && typeof ArrayBuffer !== 'undefined' && (val instanceof ArrayBuffer || val.constructor && val.constructor.name === 'ArrayBuffer');
}
function isImageBitmap(val) {
    return ImageBitmap$1 && val instanceof ImageBitmap$1;
}
function serialize(input, transferables) {
    if (input === null || input === undefined || typeof input === 'boolean' || typeof input === 'number' || typeof input === 'string' || input instanceof Boolean || input instanceof Number || input instanceof String || input instanceof Date || input instanceof RegExp) {
        return input;
    }
    if (isArrayBuffer(input) || isImageBitmap(input)) {
        if (transferables) {
            transferables.push(input);
        }
        return input;
    }
    if (ArrayBuffer.isView(input)) {
        const view = input;
        if (transferables) {
            transferables.push(view.buffer);
        }
        return view;
    }
    if (input instanceof ImageData$1) {
        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') {
        const klass = input.constructor;
        const name = klass._classRegistryKey;
        if (!name) {
            throw new Error(`can't serialize object of unregistered class`);
        }
        const properties = klass.serialize ? klass.serialize(input, transferables) : {};
        if (!klass.serialize) {
            for (const key in input) {
                if (!input.hasOwnProperty(key))
                    continue;
                if (registry[name].omit.indexOf(key) >= 0)
                    continue;
                const property = input[key];
                properties[key] = registry[name].shallow.indexOf(key) >= 0 ? property : serialize(property, transferables);
            }
            if (input instanceof Error) {
                properties.message = input.message;
            }
        }
        if (properties.$name) {
            throw new Error('$name property is reserved for worker serialization logic.');
        }
        if (name !== 'Object') {
            properties.$name = name;
        }
        return properties;
    }
    throw new Error(`can't serialize object of type ${ typeof input }`);
}
function deserialize$1(input) {
    if (input === null || input === undefined || typeof input === 'boolean' || typeof input === 'number' || typeof input === 'string' || input instanceof Boolean || input instanceof Number || input instanceof String || input instanceof Date || input instanceof RegExp || isArrayBuffer(input) || isImageBitmap(input) || ArrayBuffer.isView(input) || input instanceof ImageData$1) {
        return input;
    }
    if (Array.isArray(input)) {
        return input.map(deserialize$1);
    }
    if (typeof input === 'object') {
        const name = input.$name || 'Object';
        const {klass} = registry[name];
        if (!klass) {
            throw new Error(`can't deserialize unregistered class ${ name }`);
        }
        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[name].shallow.indexOf(key) >= 0 ? value : deserialize$1(value);
        }
        return result;
    }
    throw new Error(`can't deserialize object of type ${ typeof input }`);
}

class ZoomHistory {
    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;
    }
}

const unicodeBlockLookup = {
    'Latin-1 Supplement': char => char >= 128 && char <= 255,
    'Arabic': char => char >= 1536 && char <= 1791,
    'Arabic Supplement': char => char >= 1872 && char <= 1919,
    'Arabic Extended-A': char => char >= 2208 && char <= 2303,
    'Hangul Jamo': char => char >= 4352 && char <= 4607,
    'Unified Canadian Aboriginal Syllabics': char => char >= 5120 && char <= 5759,
    'Khmer': char => char >= 6016 && char <= 6143,
    'Unified Canadian Aboriginal Syllabics Extended': char => char >= 6320 && char <= 6399,
    'General Punctuation': char => char >= 8192 && char <= 8303,
    'Letterlike Symbols': char => char >= 8448 && char <= 8527,
    'Number Forms': char => char >= 8528 && char <= 8591,
    'Miscellaneous Technical': char => char >= 8960 && char <= 9215,
    'Control Pictures': char => char >= 9216 && char <= 9279,
    'Optical Character Recognition': char => char >= 9280 && char <= 9311,
    'Enclosed Alphanumerics': char => char >= 9312 && char <= 9471,
    'Geometric Shapes': char => char >= 9632 && char <= 9727,
    'Miscellaneous Symbols': char => char >= 9728 && char <= 9983,
    'Miscellaneous Symbols and Arrows': char => char >= 11008 && char <= 11263,
    'CJK Radicals Supplement': char => char >= 11904 && char <= 12031,
    'Kangxi Radicals': char => char >= 12032 && char <= 12255,
    'Ideographic Description Characters': char => char >= 12272 && char <= 12287,
    'CJK Symbols and Punctuation': char => char >= 12288 && char <= 12351,
    'Hiragana': char => char >= 12352 && char <= 12447,
    'Katakana': char => char >= 12448 && char <= 12543,
    'Bopomofo': char => char >= 12544 && char <= 12591,
    'Hangul Compatibility Jamo': char => char >= 12592 && char <= 12687,
    'Kanbun': char => char >= 12688 && char <= 12703,
    'Bopomofo Extended': char => char >= 12704 && char <= 12735,
    'CJK Strokes': char => char >= 12736 && char <= 12783,
    'Katakana Phonetic Extensions': char => char >= 12784 && char <= 12799,
    'Enclosed CJK Letters and Months': char => char >= 12800 && char <= 13055,
    'CJK Compatibility': char => char >= 13056 && char <= 13311,
    'CJK Unified Ideographs Extension A': char => char >= 13312 && char <= 19903,
    'Yijing Hexagram Symbols': char => char >= 19904 && char <= 19967,
    'CJK Unified Ideographs': char => char >= 19968 && char <= 40959,
    'Yi Syllables': char => char >= 40960 && char <= 42127,
    'Yi Radicals': char => char >= 42128 && char <= 42191,
    'Hangul Jamo Extended-A': char => char >= 43360 && char <= 43391,
    'Hangul Syllables': char => char >= 44032 && char <= 55215,
    'Hangul Jamo Extended-B': char => char >= 55216 && char <= 55295,
    'Private Use Area': char => char >= 57344 && char <= 63743,
    'CJK Compatibility Ideographs': char => char >= 63744 && char <= 64255,
    'Arabic Presentation Forms-A': char => char >= 64336 && char <= 65023,
    'Vertical Forms': char => char >= 65040 && char <= 65055,
    'CJK Compatibility Forms': char => char >= 65072 && char <= 65103,
    'Small Form Variants': char => char >= 65104 && char <= 65135,
    'Arabic Presentation Forms-B': char => char >= 65136 && char <= 65279,
    'Halfwidth and Fullwidth Forms': char => char >= 65280 && char <= 65519
};

function allowsVerticalWritingMode(chars) {
    for (const char of chars) {
        if (charHasUprightVerticalOrientation(char.charCodeAt(0)))
            return true;
    }
    return false;
}
function allowsLetterSpacing(chars) {
    for (const char of chars) {
        if (!charAllowsLetterSpacing(char.charCodeAt(0)))
            return false;
    }
    return true;
}
function charAllowsLetterSpacing(char) {
    if (unicodeBlockLookup['Arabic'](char))
        return false;
    if (unicodeBlockLookup['Arabic Supplement'](char))
        return false;
    if (unicodeBlockLookup['Arabic Extended-A'](char))
        return false;
    if (unicodeBlockLookup['Arabic Presentation Forms-A'](char))
        return false;
    if (unicodeBlockLookup['Arabic Presentation Forms-B'](char))
        return false;
    return true;
}
function charAllowsIdeographicBreaking(char) {
    if (char < 11904)
        return false;
    if (unicodeBlockLookup['Bopomofo Extended'](char))
        return true;
    if (unicodeBlockLookup['Bopomofo'](char))
        return true;
    if (unicodeBlockLookup['CJK Compatibility Forms'](char))
        return true;
    if (unicodeBlockLookup['CJK Compatibility Ideographs'](char))
        return true;
    if (unicodeBlockLookup['CJK Compatibility'](char))
        return true;
    if (unicodeBlockLookup['CJK Radicals Supplement'](char))
        return true;
    if (unicodeBlockLookup['CJK Strokes'](char))
        return true;
    if (unicodeBlockLookup['CJK Symbols and Punctuation'](char))
        return true;
    if (unicodeBlockLookup['CJK Unified Ideographs Extension A'](char))
        return true;
    if (unicodeBlockLookup['CJK Unified Ideographs'](char))
        return true;
    if (unicodeBlockLookup['Enclosed CJK Letters and Months'](char))
        return true;
    if (unicodeBlockLookup['Halfwidth and Fullwidth Forms'](char))
        return true;
    if (unicodeBlockLookup['Hiragana'](char))
        return true;
    if (unicodeBlockLookup['Ideographic Description Characters'](char))
        return true;
    if (unicodeBlockLookup['Kangxi Radicals'](char))
        return true;
    if (unicodeBlockLookup['Katakana Phonetic Extensions'](char))
        return true;
    if (unicodeBlockLookup['Katakana'](char))
        return true;
    if (unicodeBlockLookup['Vertical Forms'](char))
        return true;
    if (unicodeBlockLookup['Yi Radicals'](char))
        return true;
    if (unicodeBlockLookup['Yi Syllables'](char))
        return true;
    return false;
}
function charHasUprightVerticalOrientation(char) {
    if (char === 746 || char === 747) {
        return true;
    }
    if (char < 4352)
        return false;
    if (unicodeBlockLookup['Bopomofo Extended'](char))
        return true;
    if (unicodeBlockLookup['Bopomofo'](char))
        return true;
    if (unicodeBlockLookup['CJK Compatibility Forms'](char)) {
        if (!(char >= 65097 && char <= 65103)) {
            return true;
        }
    }
    if (unicodeBlockLookup['CJK Compatibility Ideographs'](char))
        return true;
    if (unicodeBlockLookup['CJK Compatibility'](char))
        return true;
    if (unicodeBlockLookup['CJK Radicals Supplement'](char))
        return true;
    if (unicodeBlockLookup['CJK Strokes'](char))
        return true;
    if (unicodeBlockLookup['CJK Symbols and Punctuation'](char)) {
        if (!(char >= 12296 && char <= 12305) && !(char >= 12308 && char <= 12319) && char !== 12336) {
            return true;
        }
    }
    if (unicodeBlockLookup['CJK Unified Ideographs Extension A'](char))
        return true;
    if (unicodeBlockLookup['CJK Unified Ideographs'](char))
        return true;
    if (unicodeBlockLookup['Enclosed CJK Letters and Months'](char))
        return true;
    if (unicodeBlockLookup['Hangul Compatibility Jamo'](char))
        return true;
    if (unicodeBlockLookup['Hangul Jamo Extended-A'](char))
        return true;
    if (unicodeBlockLookup['Hangul Jamo Extended-B'](char))
        return true;
    if (unicodeBlockLookup['Hangul Jamo'](char))
        return true;
    if (unicodeBlockLookup['Hangul Syllables'](char))
        return true;
    if (unicodeBlockLookup['Hiragana'](char))
        return true;
    if (unicodeBlockLookup['Ideographic Description Characters'](char))
        return true;
    if (unicodeBlockLookup['Kanbun'](char))
        return true;
    if (unicodeBlockLookup['Kangxi Radicals'](char))
        return true;
    if (unicodeBlockLookup['Katakana Phonetic Extensions'](char))
        return true;
    if (unicodeBlockLookup['Katakana'](char)) {
        if (char !== 12540) {
            return true;
        }
    }
    if (unicodeBlockLookup['Halfwidth and Fullwidth Forms'](char)) {
        if (char !== 65288 && char !== 65289 && char !== 65293 && !(char >= 65306 && char <= 65310) && char !== 65339 && char !== 65341 && char !== 65343 && !(char >= 65371 && char <= 65503) && char !== 65507 && !(char >= 65512 && char <= 65519)) {
            return true;
        }
    }
    if (unicodeBlockLookup['Small Form Variants'](char)) {
        if (!(char >= 65112 && char <= 65118) && !(char >= 65123 && char <= 65126)) {
            return true;
        }
    }
    if (unicodeBlockLookup['Unified Canadian Aboriginal Syllabics'](char))
        return true;
    if (unicodeBlockLookup['Unified Canadian Aboriginal Syllabics Extended'](char))
        return true;
    if (unicodeBlockLookup['Vertical Forms'](char))
        return true;
    if (unicodeBlockLookup['Yijing Hexagram Symbols'](char))
        return true;
    if (unicodeBlockLookup['Yi Syllables'](char))
        return true;
    if (unicodeBlockLookup['Yi Radicals'](char))
        return true;
    return false;
}
function charHasNeutralVerticalOrientation(char) {
    if (unicodeBlockLookup['Latin-1 Supplement'](char)) {
        if (char === 167 || char === 169 || char === 174 || char === 177 || char === 188 || char === 189 || char === 190 || char === 215 || char === 247) {
            return true;
        }
    }
    if (unicodeBlockLookup['General Punctuation'](char)) {
        if (char === 8214 || char === 8224 || char === 8225 || char === 8240 || char === 8241 || char === 8251 || char === 8252 || char === 8258 || char === 8263 || char === 8264 || char === 8265 || char === 8273) {
            return true;
        }
    }
    if (unicodeBlockLookup['Letterlike Symbols'](char))
        return true;
    if (unicodeBlockLookup['Number Forms'](char))
        return true;
    if (unicodeBlockLookup['Miscellaneous Technical'](char)) {
        if (char >= 8960 && char <= 8967 || char >= 8972 && char <= 8991 || char >= 8996 && char <= 9000 || char === 9003 || char >= 9085 && char <= 9114 || char >= 9150 && char <= 9165 || char === 9167 || char >= 9169 && char <= 9179 || char >= 9186 && char <= 9215) {
            return true;
        }
    }
    if (unicodeBlockLookup['Control Pictures'](char) && char !== 9251)
        return true;
    if (unicodeBlockLookup['Optical Character Recognition'](char))
        return true;
    if (unicodeBlockLookup['Enclosed Alphanumerics'](char))
        return true;
    if (unicodeBlockLookup['Geometric Shapes'](char))
        return true;
    if (unicodeBlockLookup['Miscellaneous Symbols'](char)) {
        if (!(char >= 9754 && char <= 9759)) {
            return true;
        }
    }
    if (unicodeBlockLookup['Miscellaneous Symbols and Arrows'](char)) {
        if (char >= 11026 && char <= 11055 || char >= 11088 && char <= 11097 || char >= 11192 && char <= 11243) {
            return true;
        }
    }
    if (unicodeBlockLookup['CJK Symbols and Punctuation'](char))
        return true;
    if (unicodeBlockLookup['Katakana'](char))
        return true;
    if (unicodeBlockLookup['Private Use Area'](char))
        return true;
    if (unicodeBlockLookup['CJK Compatibility Forms'](char))
        return true;
    if (unicodeBlockLookup['Small Form Variants'](char))
        return true;
    if (unicodeBlockLookup['Halfwidth and Fullwidth Forms'](char))
        return true;
    if (char === 8734 || char === 8756 || char === 8757 || char >= 9984 && char <= 10087 || char >= 10102 && char <= 10131 || char === 65532 || char === 65533) {
        return true;
    }
    return false;
}
function charHasRotatedVerticalOrientation(char) {
    return !(charHasUprightVerticalOrientation(char) || charHasNeutralVerticalOrientation(char));
}
function charInComplexShapingScript(char) {
    return unicodeBlockLookup['Arabic'](char) || unicodeBlockLookup['Arabic Supplement'](char) || unicodeBlockLookup['Arabic Extended-A'](char) || unicodeBlockLookup['Arabic Presentation Forms-A'](char) || unicodeBlockLookup['Arabic Presentation Forms-B'](char);
}
function charInRTLScript(char) {
    return char >= 1424 && char <= 2303 || unicodeBlockLookup['Arabic Presentation Forms-A'](char) || unicodeBlockLookup['Arabic Presentation Forms-B'](char);
}
function charInSupportedScript(char, canRenderRTL) {
    if (!canRenderRTL && charInRTLScript(char)) {
        return false;
    }
    if (char >= 2304 && char <= 3583 || char >= 3840 && char <= 4255 || unicodeBlockLookup['Khmer'](char)) {
        return false;
    }
    return true;
}
function stringContainsRTLText(chars) {
    for (const char of chars) {
        if (charInRTLScript(char.charCodeAt(0))) {
            return true;
        }
    }
    return false;
}
function isStringInSupportedScript(chars, canRenderRTL) {
    for (const char of chars) {
        if (!charInSupportedScript(char.charCodeAt(0), canRenderRTL)) {
            return false;
        }
    }
    return true;
}

const status = {
    unavailable: 'unavailable',
    deferred: 'deferred',
    loading: 'loading',
    loaded: 'loaded',
    error: 'error'
};
let _completionCallback = null;
let pluginStatus = status.unavailable;
let pluginURL = null;
const triggerPluginCompletionEvent = function (error) {
    if (error && typeof error === 'string' && error.indexOf('NetworkError') > -1) {
        pluginStatus = status.error;
    }
    if (_completionCallback) {
        _completionCallback(error);
    }
};
function sendPluginStateToWorker() {
    evented.fire(new Event('pluginStateChange', {
        pluginStatus,
        pluginURL
    }));
}
const evented = new Evented();
const getRTLTextPluginStatus = function () {
    return pluginStatus;
};
const registerForPluginStateChange = function (callback) {
    callback({
        pluginStatus,
        pluginURL
    });
    evented.on('pluginStateChange', callback);
    return callback;
};
const setRTLTextPlugin = function (url, callback, deferred = false) {
    if (pluginStatus === status.deferred || pluginStatus === status.loading || pluginStatus === status.loaded) {
        throw new Error('setRTLTextPlugin cannot be called multiple times.');
    }
    pluginURL = exported$1.resolveURL(url);
    pluginStatus = status.deferred;
    _completionCallback = callback;
    sendPluginStateToWorker();
    if (!deferred) {
        downloadRTLTextPlugin();
    }
};
const downloadRTLTextPlugin = function () {
    if (pluginStatus !== status.deferred || !pluginURL) {
        throw new Error('rtl-text-plugin cannot be downloaded unless a pluginURL is specified');
    }
    pluginStatus = status.loading;
    sendPluginStateToWorker();
    if (pluginURL) {
        getArrayBuffer({ url: pluginURL }, error => {
            if (error) {
                triggerPluginCompletionEvent(error);
            } else {
                pluginStatus = status.loaded;
                sendPluginStateToWorker();
            }
        });
    }
};
const plugin = {
    applyArabicShaping: null,
    processBidirectionalText: null,
    processStyledBidirectionalText: null,
    isLoaded() {
        return pluginStatus === status.loaded || plugin.applyArabicShaping != null;
    },
    isLoading() {
        return pluginStatus === status.loading;
    },
    setState(state) {
        pluginStatus = state.pluginStatus;
        pluginURL = state.pluginURL;
    },
    isParsed() {
        return plugin.applyArabicShaping != null && plugin.processBidirectionalText != null && plugin.processStyledBidirectionalText != null;
    },
    getPluginURL() {
        return pluginURL;
    }
};
const lazyLoadRTLTextPlugin = function () {
    if (!plugin.isLoading() && !plugin.isLoaded() && getRTLTextPluginStatus() === 'deferred') {
        downloadRTLTextPlugin();
    }
};

class EvaluationParameters {
    constructor(zoom, options) {
        this.zoom = zoom;
        if (options) {
            this.now = options.now;
            this.fadeDuration = options.fadeDuration;
            this.zoomHistory = options.zoomHistory;
            this.transition = options.transition;
        } else {
            this.now = 0;
            this.fadeDuration = 0;
            this.zoomHistory = new ZoomHistory();
            this.transition = {};
        }
    }
    isSupportedScript(str) {
        return isStringInSupportedScript(str, plugin.isLoaded());
    }
    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: 0.5,
            toScale: 1,
            t: 1 - (1 - t) * fraction
        };
    }
}

class PropertyValue {
    constructor(property, value) {
        this.property = property;
        this.value = value;
        this.expression = normalizePropertyExpression(value === undefined ? property.specification.default : value, property.specification);
    }
    isDataDriven() {
        return this.expression.kind === 'source' || this.expression.kind === 'composite';
    }
    possiblyEvaluate(parameters, canonical, availableImages) {
        return this.property.possiblyEvaluate(this, parameters, canonical, availableImages);
    }
}
class TransitionablePropertyValue {
    constructor(property) {
        this.property = property;
        this.value = new PropertyValue(property, undefined);
    }
    transitioned(parameters, prior) {
        return new TransitioningPropertyValue(this.property, this.value, prior, extend$1({}, parameters.transition, this.transition), parameters.now);
    }
    untransitioned() {
        return new TransitioningPropertyValue(this.property, this.value, null, {}, 0);
    }
}
class Transitionable {
    constructor(properties) {
        this._properties = properties;
        this._values = Object.create(properties.defaultTransitionablePropertyValues);
    }
    getValue(name) {
        return clone$2(this._values[name].value.value);
    }
    setValue(name, value) {
        if (!this._values.hasOwnProperty(name)) {
            this._values[name] = new TransitionablePropertyValue(this._values[name].property);
        }
        this._values[name].value = new PropertyValue(this._values[name].property, value === null ? undefined : clone$2(value));
    }
    getTransition(name) {
        return clone$2(this._values[name].transition);
    }
    setTransition(name, value) {
        if (!this._values.hasOwnProperty(name)) {
            this._values[name] = new TransitionablePropertyValue(this._values[name].property);
        }
        this._values[name].transition = clone$2(value) || undefined;
    }
    serialize() {
        const result = {};
        for (const property of Object.keys(this._values)) {
            const value = this.getValue(property);
            if (value !== undefined) {
                result[property] = value;
            }
            const transition = this.getTransition(property);
            if (transition !== undefined) {
                result[`${ property }-transition`] = 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;
    }
}
class TransitioningPropertyValue {
    constructor(property, value, prior, transition, now) {
        const delay = transition.delay || 0;
        const duration = transition.duration || 0;
        now = now || 0;
        this.property = property;
        this.value = value;
        this.begin = now + delay;
        this.end = this.begin + duration;
        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));
        }
    }
}
class Transitioning {
    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;
    }
}
class Layout {
    constructor(properties) {
        this._properties = properties;
        this._values = Object.create(properties.defaultPropertyValues);
    }
    getValue(name) {
        return clone$2(this._values[name].value);
    }
    setValue(name, value) {
        this._values[name] = new PropertyValue(this._values[name].property, value === null ? undefined : clone$2(value));
    }
    serialize() {
        const result = {};
        for (const property of Object.keys(this._values)) {
            const value = this.getValue(property);
            if (value !== undefined) {
                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;
    }
}
class PossiblyEvaluatedPropertyValue {
    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);
    }
}
class PossiblyEvaluated {
    constructor(properties) {
        this._properties = properties;
        this._values = Object.create(properties.defaultPossiblyEvaluatedValues);
    }
    get(name) {
        return this._values[name];
    }
}
class DataConstantProperty {
    constructor(specification) {
        this.specification = specification;
    }
    possiblyEvaluate(value, parameters) {
        return value.expression.evaluate(parameters);
    }
    interpolate(a, b, t) {
        const interp = interpolate[this.specification.type];
        if (interp) {
            return interp(a, b, t);
        } else {
            return a;
        }
    }
}
class DataDrivenProperty {
    constructor(specification, overrides) {
        this.specification = specification;
        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 === undefined || b.value.value === undefined) {
            return new PossiblyEvaluatedPropertyValue(this, {
                kind: 'constant',
                value: undefined
            }, a.parameters);
        }
        const interp = interpolate[this.specification.type];
        if (interp) {
            return new PossiblyEvaluatedPropertyValue(this, {
                kind: 'constant',
                value: interp(a.value.value, b.value.value, t)
            }, 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);
        }
    }
}
class CrossFadedDataDrivenProperty extends DataDrivenProperty {
    possiblyEvaluate(value, parameters, canonical, availableImages) {
        if (value.value === undefined) {
            return new PossiblyEvaluatedPropertyValue(this, {
                kind: 'constant',
                value: undefined
            }, parameters);
        } else if (value.expression.kind === 'constant') {
            const evaluatedValue = value.expression.evaluate(parameters, null, {}, canonical, availableImages);
            const isImageExpression = value.property.specification.type === 'resolvedImage';
            const constantValue = isImageExpression && 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) {
        const z = parameters.zoom;
        return z > parameters.zoomHistory.lastIntegerZoom ? {
            from: min,
            to: mid,
            other: max
        } : {
            from: max,
            to: mid,
            other: min
        };
    }
    interpolate(a) {
        return a;
    }
}
class CrossFadedProperty {
    constructor(specification) {
        this.specification = specification;
    }
    possiblyEvaluate(value, parameters, canonical, availableImages) {
        if (value.value === undefined) {
            return undefined;
        } 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) {
        const z = parameters.zoom;
        return z > parameters.zoomHistory.lastIntegerZoom ? {
            from: min,
            to: mid
        } : {
            from: max,
            to: mid
        };
    }
    interpolate(a) {
        return a;
    }
}
class ColorRampProperty {
    constructor(specification) {
        this.specification = specification;
    }
    possiblyEvaluate(value, parameters, canonical, availableImages) {
        return !!value.expression.evaluate(parameters, null, {}, canonical, availableImages);
    }
    interpolate() {
        return false;
    }
}
class Properties {
    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, undefined);
            const defaultTransitionablePropertyValue = this.defaultTransitionablePropertyValues[property] = new TransitionablePropertyValue(prop);
            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);

function packUint8ToFloat(a, b) {
    a = clamp(Math.floor(a), 0, 255);
    b = clamp(Math.floor(b), 0, 255);
    return 256 * a + b;
}

const viewTypes = {
    'Int8': Int8Array,
    'Uint8': Uint8Array,
    'Int16': Int16Array,
    'Uint16': Uint16Array,
    'Int32': Int32Array,
    'Uint32': Uint32Array,
    'Float32': Float32Array
};
class Struct {
    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;
class StructArray {
    constructor() {
        this.isTransferred = false;
        this.capacity = -1;
        this.resize(0);
    }
    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;
    }
    _trim() {
        if (this.length !== this.capacity) {
            this.capacity = this.length;
            this.arrayBuffer = this.arrayBuffer.slice(0, this.length * this.bytesPerElement);
            this._refreshViews();
        }
    }
    clear() {
        this.length = 0;
    }
    resize(n) {
        this.reserve(n);
        this.length = n;
    }
    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);
        }
    }
    _refreshViews() {
        throw new Error('_refreshViews() must be implemented by each concrete StructArray layout');
    }
}
function createLayout(members, alignment = 1) {
    let offset = 0;
    let maxSize = 0;
    const layoutMembers = 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
        };
    });
    const size = align$1(offset, Math.max(maxSize, alignment));
    return {
        members: layoutMembers,
        size,
        alignment
    };
}
function sizeOf(type) {
    return viewTypes[type].BYTES_PER_ELEMENT;
}
function align$1(offset, size) {
    return Math.ceil(offset / size) * size;
}

class StructArrayLayout2i4 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);
class StructArrayLayout4i8 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);
class StructArrayLayout2i4ub1f12 extends StructArray {
    _refreshViews() {
        this.uint8 = new Uint8Array(this.arrayBuffer);
        this.int16 = new Int16Array(this.arrayBuffer);
        this.float32 = new Float32Array(this.arrayBuffer);
    }
    emplaceBack(v0, v1, v2, v3, v4, v5, v6) {
        const i = this.length;
        this.resize(i + 1);
        return this.emplace(i, v0, v1, v2, v3, v4, v5, v6);
    }
    emplace(i, v0, v1, v2, v3, v4, v5, v6) {
        const o2 = i * 6;
        const o1 = i * 12;
        const o4 = i * 3;
        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;
        this.float32[o4 + 2] = v6;
        return i;
    }
}
StructArrayLayout2i4ub1f12.prototype.bytesPerElement = 12;
register('StructArrayLayout2i4ub1f12', StructArrayLayout2i4ub1f12);
class StructArrayLayout2f8 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);
class StructArrayLayout10ui20 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);
class StructArrayLayout8ui16 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);
class StructArrayLayout4i4ui4i24 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);
class StructArrayLayout3f12 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);
class StructArrayLayout1ul4 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);
class StructArrayLayout2i4f1i1ul2ui32 extends StructArray {
    _refreshViews() {
        this.uint8 = new Uint8Array(this.arrayBuffer);
        this.int16 = new Int16Array(this.arrayBuffer);
        this.float32 = new Float32Array(this.arrayBuffer);
        this.uint32 = new Uint32Array(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 * 16;
        const o4 = i * 8;
        this.int16[o2 + 0] = v0;
        this.int16[o2 + 1] = v1;
        this.float32[o4 + 1] = v2;
        this.float32[o4 + 2] = v3;
        this.float32[o4 + 3] = v4;
        this.float32[o4 + 4] = v5;
        this.int16[o2 + 10] = v6;
        this.uint32[o4 + 6] = v7;
        this.uint16[o2 + 14] = v8;
        this.uint16[o2 + 15] = v9;
        return i;
    }
}
StructArrayLayout2i4f1i1ul2ui32.prototype.bytesPerElement = 32;
register('StructArrayLayout2i4f1i1ul2ui32', StructArrayLayout2i4f1i1ul2ui32);
class StructArrayLayout2i2i2i12 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);
class StructArrayLayout2f1f2i16 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);
class StructArrayLayout2ub2f12 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 o1 = i * 12;
        const o4 = i * 3;
        this.uint8[o1 + 0] = v0;
        this.uint8[o1 + 1] = v1;
        this.float32[o4 + 1] = v2;
        this.float32[o4 + 2] = v3;
        return i;
    }
}
StructArrayLayout2ub2f12.prototype.bytesPerElement = 12;
register('StructArrayLayout2ub2f12', StructArrayLayout2ub2f12);
class StructArrayLayout3ui6 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);
class StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48 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);
class StructArrayLayout8i15ui1ul4f68 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 * 34;
        const o4 = i * 17;
        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.float32[o4 + 15] = v26;
        this.float32[o4 + 16] = v27;
        return i;
    }
}
StructArrayLayout8i15ui1ul4f68.prototype.bytesPerElement = 68;
register('StructArrayLayout8i15ui1ul4f68', StructArrayLayout8i15ui1ul4f68);
class StructArrayLayout1f4 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);
class StructArrayLayout3i6 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);
class StructArrayLayout1ul3ui12 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, 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 * 3;
        const o2 = i * 6;
        this.uint32[o4 + 0] = v0;
        this.uint16[o2 + 2] = v1;
        this.uint16[o2 + 3] = v2;
        this.uint16[o2 + 4] = v3;
        return i;
    }
}
StructArrayLayout1ul3ui12.prototype.bytesPerElement = 12;
register('StructArrayLayout1ul3ui12', StructArrayLayout1ul3ui12);
class StructArrayLayout2ui4 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);
class StructArrayLayout1ui2 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);
class StructArrayLayout4f16 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);
class CollisionBoxStruct 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.float32[this._pos4 + 1];
    }
    get y1() {
        return this._structArray.float32[this._pos4 + 2];
    }
    get x2() {
        return this._structArray.float32[this._pos4 + 3];
    }
    get y2() {
        return this._structArray.float32[this._pos4 + 4];
    }
    get padding() {
        return this._structArray.int16[this._pos2 + 10];
    }
    get featureIndex() {
        return this._structArray.uint32[this._pos4 + 6];
    }
    get sourceLayerIndex() {
        return this._structArray.uint16[this._pos2 + 14];
    }
    get bucketIndex() {
        return this._structArray.uint16[this._pos2 + 15];
    }
    get anchorPoint() {
        return new pointGeometry(this.anchorPointX, this.anchorPointY);
    }
}
CollisionBoxStruct.prototype.size = 32;
class CollisionBoxArray extends StructArrayLayout2i4f1i1ul2ui32 {
    get(index) {
        return new CollisionBoxStruct(this, index);
    }
}
register('CollisionBoxArray', CollisionBoxArray);
class PlacedSymbolStruct 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;
class PlacedSymbolArray extends StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48 {
    get(index) {
        return new PlacedSymbolStruct(this, index);
    }
}
register('PlacedSymbolArray', PlacedSymbolArray);
class SymbolInstanceStruct 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 textOffset0() {
        return this._structArray.float32[this._pos4 + 14];
    }
    get textOffset1() {
        return this._structArray.float32[this._pos4 + 15];
    }
    get collisionCircleDiameter() {
        return this._structArray.float32[this._pos4 + 16];
    }
}
SymbolInstanceStruct.prototype.size = 68;
class SymbolInstanceArray extends StructArrayLayout8i15ui1ul4f68 {
    get(index) {
        return new SymbolInstanceStruct(this, index);
    }
}
register('SymbolInstanceArray', SymbolInstanceArray);
class GlyphOffsetArray extends StructArrayLayout1f4 {
    getoffsetX(index) {
        return this.float32[index * 1 + 0];
    }
}
register('GlyphOffsetArray', GlyphOffsetArray);
class SymbolLineVertexArray 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);
class FeatureIndexStruct 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];
    }
    get layoutVertexArrayOffset() {
        return this._structArray.uint16[this._pos2 + 4];
    }
}
FeatureIndexStruct.prototype.size = 12;
class FeatureIndexArray extends StructArrayLayout1ul3ui12 {
    get(index) {
        return new FeatureIndexStruct(this, index);
    }
}
register('FeatureIndexArray', FeatureIndexArray);
class FillExtrusionCentroidStruct extends Struct {
    get a_centroid_pos0() {
        return this._structArray.uint16[this._pos2 + 0];
    }
    get a_centroid_pos1() {
        return this._structArray.uint16[this._pos2 + 1];
    }
}
FillExtrusionCentroidStruct.prototype.size = 4;
class FillExtrusionCentroidArray extends StructArrayLayout2ui4 {
    get(index) {
        return new FillExtrusionCentroidStruct(this, index);
    }
}
register('FillExtrusionCentroidArray', FillExtrusionCentroidArray);

const patternAttributes = createLayout([
    {
        name: 'a_pattern_to',
        components: 4,
        type: 'Uint16'
    },
    {
        name: 'a_pattern_from',
        components: 4,
        type: 'Uint16'
    },
    {
        name: 'a_pixel_ratio_to',
        components: 1,
        type: 'Uint16'
    },
    {
        name: 'a_pixel_ratio_from',
        components: 1,
        type: 'Uint16'
    }
]);

const dashAttributes = createLayout([
    {
        name: 'a_dash_to',
        components: 4,
        type: 'Uint16'
    },
    {
        name: 'a_dash_from',
        components: 4,
        type: 'Uint16'
    }
]);

var murmurhash3_gc = createCommonjsModule(function (module) {
function murmurhash3_32_gc(key, seed) {
    var remainder, bytes, h1, h1b, c1, c2, k1, i;
    remainder = key.length & 3;
    bytes = key.length - remainder;
    h1 = seed;
    c1 = 3432918353;
    c2 = 461845907;
    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;
}
{
    module.exports = murmurhash3_32_gc;
}
});

var murmurhash2_gc = createCommonjsModule(function (module) {
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;
}
});

var murmurhashJs = murmurhash3_gc;
var murmur3_1 = murmurhash3_gc;
var murmur2_1 = murmurhash2_gc;
murmurhashJs.murmur3 = murmur3_1;
murmurhashJs.murmur2 = murmur2_1;

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) {
        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(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 <= MAX_SAFE_INTEGER) {
        return numValue;
    }
    return murmurhashJs(String(value));
}
function sort(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(ids, positions, left, j);
            left = j + 1;
        } else {
            sort(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);

class Uniform {
    constructor(context, location) {
        this.gl = context.gl;
        this.location = location;
    }
}
class Uniform1i 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);
        }
    }
}
class Uniform1f 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);
        }
    }
}
class Uniform2f 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]);
        }
    }
}
class Uniform3f 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]);
        }
    }
}
class Uniform4f 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]);
        }
    }
}
class UniformColor extends Uniform {
    constructor(context, location) {
        super(context, location);
        this.current = Color$1.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);
        }
    }
}
const emptyMat4 = new Float32Array(16);
class UniformMatrix4f 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;
            }
        }
    }
}
const emptyMat3 = new Float32Array(9);
class UniformMatrix3f extends Uniform {
    constructor(context, location) {
        super(context, location);
        this.current = emptyMat3;
    }
    set(v) {
        for (let i = 0; i < 9; i++) {
            if (v[i] !== this.current[i]) {
                this.current = v;
                this.gl.uniformMatrix3fv(this.location, false, v);
                break;
            }
        }
    }
}

function packColor(color) {
    return [
        packUint8ToFloat(255 * color.r, 255 * color.g),
        packUint8ToFloat(255 * color.b, 255 * color.a)
    ];
}
class ConstantBinder {
    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);
    }
}
class CrossFadedConstantBinder {
    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.tl.concat(posFrom.br);
        this.patternTo = posTo.tl.concat(posTo.br);
    }
    setUniform(uniform, globals, currentValue, uniformName) {
        const pos = uniformName === 'u_pattern_to' || uniformName === 'u_dash_to' ? this.patternTo : uniformName === 'u_pattern_from' || uniformName === 'u_dash_from' ? this.patternFrom : uniformName === 'u_pixel_ratio_to' ? this.pixelRatioTo : uniformName === 'u_pixel_ratio_from' ? this.pixelRatioFrom : null;
        if (pos)
            uniform.set(pos);
    }
    getBinding(context, location, name) {
        return name === 'u_pattern_from' || name === 'u_pattern_to' || name === 'u_dash_from' || name === 'u_dash_to' ? new Uniform4f(context, location) : new Uniform1f(context, location);
    }
}
class SourceExpressionBinder {
    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, imagePositions, canonical, formattedSection) {
        const start = this.paintVertexArray.length;
        const value = this.expression.evaluate(new EvaluationParameters(0), feature, {}, canonical, [], formattedSection);
        this.paintVertexArray.resize(newLength);
        this._setPaintValue(start, newLength, value);
    }
    updatePaintArray(start, end, feature, featureState) {
        const value = this.expression.evaluate({ zoom: 0 }, 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 && this.paintVertexArray.arrayBuffer) {
            if (this.paintVertexBuffer && 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();
        }
    }
}
class CompositeExpressionBinder {
    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, imagePositions, canonical, formattedSection) {
        const min = this.expression.evaluate(new EvaluationParameters(this.zoom), feature, {}, canonical, [], formattedSection);
        const max = this.expression.evaluate(new EvaluationParameters(this.zoom + 1), feature, {}, canonical, [], formattedSection);
        const start = this.paintVertexArray.length;
        this.paintVertexArray.resize(newLength);
        this._setPaintValue(start, newLength, min, max);
    }
    updatePaintArray(start, end, feature, featureState) {
        const min = this.expression.evaluate({ zoom: this.zoom }, feature, featureState);
        const max = this.expression.evaluate({ zoom: this.zoom + 1 }, 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 && this.paintVertexArray.arrayBuffer) {
            if (this.paintVertexBuffer && 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(this.expression.interpolationFactor(currentZoom, this.zoom, this.zoom + 1), 0, 1);
        uniform.set(factor);
    }
    getBinding(context, location, _) {
        return new Uniform1f(context, location);
    }
}
class CrossFadedCompositeBinder {
    constructor(expression, names, type, useIntegerZoom, zoom, PaintVertexArray, layerId) {
        this.expression = expression;
        this.type = type;
        this.useIntegerZoom = useIntegerZoom;
        this.zoom = zoom;
        this.layerId = layerId;
        this.paintVertexAttributes = (type === 'array' ? dashAttributes : patternAttributes).members;
        for (let i = 0; i < names.length; ++i) {
        }
        this.zoomInPaintVertexArray = new PaintVertexArray();
        this.zoomOutPaintVertexArray = new PaintVertexArray();
    }
    populatePaintArray(length, feature, imagePositions) {
        const start = this.zoomInPaintVertexArray.length;
        this.zoomInPaintVertexArray.resize(length);
        this.zoomOutPaintVertexArray.resize(length);
        this._setPaintValues(start, length, feature.patterns && feature.patterns[this.layerId], imagePositions);
    }
    updatePaintArray(start, end, feature, featureState, imagePositions) {
        this._setPaintValues(start, end, feature.patterns && feature.patterns[this.layerId], imagePositions);
    }
    _setPaintValues(start, end, patterns, positions) {
        if (!positions || !patterns)
            return;
        const {min, mid, max} = patterns;
        const imageMin = positions[min];
        const imageMid = positions[mid];
        const imageMax = positions[max];
        if (!imageMin || !imageMid || !imageMax)
            return;
        for (let i = start; i < end; i++) {
            this._setPaintValue(this.zoomInPaintVertexArray, i, imageMid, imageMin);
            this._setPaintValue(this.zoomOutPaintVertexArray, i, imageMid, imageMax);
        }
    }
    _setPaintValue(array, i, posA, posB) {
        array.emplace(i, posA.tl[0], posA.tl[1], posA.br[0], posA.br[1], posB.tl[0], posB.tl[1], posB.br[0], posB.br[1], posA.pixelRatio, posB.pixelRatio);
    }
    upload(context) {
        if (this.zoomInPaintVertexArray && this.zoomInPaintVertexArray.arrayBuffer && this.zoomOutPaintVertexArray && this.zoomOutPaintVertexArray.arrayBuffer) {
            this.zoomInPaintVertexBuffer = context.createVertexBuffer(this.zoomInPaintVertexArray, this.paintVertexAttributes, this.expression.isStateDependent);
            this.zoomOutPaintVertexBuffer = context.createVertexBuffer(this.zoomOutPaintVertexArray, this.paintVertexAttributes, this.expression.isStateDependent);
        }
    }
    destroy() {
        if (this.zoomOutPaintVertexBuffer)
            this.zoomOutPaintVertexBuffer.destroy();
        if (this.zoomInPaintVertexBuffer)
            this.zoomInPaintVertexBuffer.destroy();
    }
}
class ProgramConfiguration {
    constructor(layer, zoom, filterProperties = () => true) {
        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';
            const sourceException = String(property) === 'line-dasharray' && layer.layout.get('line-cap').value.kind !== 'constant';
            if (expression.kind === 'constant' && !sourceException) {
                this.binders[property] = isCrossFaded ? new CrossFadedConstantBinder(expression.value, names) : new ConstantBinder(expression.value, names, type);
                keys.push(`/u_${ property }`);
            } else if (expression.kind === 'source' || sourceException || isCrossFaded) {
                const StructArrayLayout = layoutType(property, type, 'source');
                this.binders[property] = isCrossFaded ? new CrossFadedCompositeBinder(expression, names, 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, imagePositions, canonical, formattedSection) {
        for (const property in this.binders) {
            const binder = this.binders[property];
            if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedCompositeBinder)
                binder.populatePaintArray(newLength, feature, imagePositions, canonical, formattedSection);
        }
    }
    setConstantPatternPositions(posTo, posFrom) {
        for (const property in this.binders) {
            const binder = this.binders[property];
            if (binder instanceof CrossFadedConstantBinder)
                binder.setConstantPatternPositions(posTo, posFrom);
        }
    }
    updatePaintArrays(featureStates, featureMap, vtLayer, layer, imagePositions) {
        let dirty = false;
        for (const id in featureStates) {
            const positions = featureMap.getPositions(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 CrossFadedCompositeBinder) && binder.expression.isStateDependent === true) {
                        const value = layer.paint.get(property);
                        binder.expression = value.value;
                        binder.updatePaintArray(pos.start, pos.end, feature, featureStates[id], imagePositions);
                        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 || binder instanceof CrossFadedCompositeBinder) {
                for (let i = 0; i < binder.paintVertexAttributes.length; i++) {
                    result.push(binder.paintVertexAttributes[i].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 CrossFadedCompositeBinder) {
                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 CrossFadedCompositeBinder)
                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 CrossFadedCompositeBinder)
                binder.destroy();
        }
    }
}
class ProgramConfigurationSet {
    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, imagePositions, canonical, formattedSection) {
        for (const key in this.programConfigurations) {
            this.programConfigurations[key].populatePaintArrays(length, feature, imagePositions, canonical, formattedSection);
        }
        if (feature.id !== undefined) {
            this._featureMap.add(feature.id, index, this._bufferOffset, length);
        }
        this._bufferOffset = length;
        this.needsUpload = true;
    }
    updatePaintArrays(featureStates, vtLayer, layers, imagePositions) {
        for (const layer of layers) {
            this.needsUpload = this.programConfigurations[layer.id].updatePaintArrays(featureStates, this._featureMap, vtLayer, layer, imagePositions) || 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();
        }
    }
}
const attributeNameExceptions = {
    '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-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'
    ],
    'line-dasharray': [
        'dash_to',
        'dash_from'
    ]
};
function paintAttributeNames(property, type) {
    return attributeNameExceptions[property] || [property.replace(`${ type }-`, '').replace(/-/g, '_')];
}
const propertyExceptions = {
    'line-pattern': {
        'source': StructArrayLayout10ui20,
        'composite': StructArrayLayout10ui20
    },
    'fill-pattern': {
        'source': StructArrayLayout10ui20,
        'composite': StructArrayLayout10ui20
    },
    'fill-extrusion-pattern': {
        'source': StructArrayLayout10ui20,
        'composite': StructArrayLayout10ui20
    },
    'line-dasharray': {
        'source': StructArrayLayout8ui16,
        'composite': StructArrayLayout8ui16
    }
};
const defaultLayouts = {
    'color': {
        'source': StructArrayLayout2f8,
        'composite': StructArrayLayout4f16
    },
    'number': {
        'source': StructArrayLayout1f4,
        'composite': StructArrayLayout2f8
    }
};
function layoutType(property, type, binderType) {
    const layoutException = propertyExceptions[property];
    return layoutException && layoutException[binderType] || defaultLayouts[type][binderType];
}
register('ConstantBinder', ConstantBinder);
register('CrossFadedConstantBinder', CrossFadedConstantBinder);
register('SourceExpressionBinder', SourceExpressionBinder);
register('CrossFadedCompositeBinder', CrossFadedCompositeBinder);
register('CompositeExpressionBinder', CompositeExpressionBinder);
register('ProgramConfiguration', ProgramConfiguration, { omit: ['_buffers'] });
register('ProgramConfigurationSet', ProgramConfigurationSet);

const TRANSITION_SUFFIX = '-transition';
class StyleLayer extends Evented {
    constructor(layer, properties) {
        super();
        this.id = layer.id;
        this.type = layer.type;
        this._featureFilter = {
            filter: () => true,
            needGeometry: false
        };
        if (layer.type === 'custom')
            return;
        layer = layer;
        this.metadata = layer.metadata;
        this.minzoom = layer.minzoom;
        this.maxzoom = layer.maxzoom;
        if (layer.type !== 'background' && layer.type !== 'sky') {
            this.source = layer.source;
            this.sourceLayer = layer['source-layer'];
            this.filter = layer.filter;
        }
        if (properties.layout) {
            this._unevaluatedLayout = new Layout(properties.layout);
        }
        if (properties.paint) {
            this._transitionablePaint = new Transitionable(properties.paint);
            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);
        }
    }
    getCrossfadeParameters() {
        return this._crossfadeParameters;
    }
    getLayoutProperty(name) {
        if (name === 'visibility') {
            return this.visibility;
        }
        return this._unevaluatedLayout.getValue(name);
    }
    setLayoutProperty(name, value, options = {}) {
        if (value !== null && value !== undefined) {
            const key = `layers.${ this.id }.layout.${ name }`;
            if (this._validate(validateLayoutProperty, key, name, value, options)) {
                return;
            }
        }
        if (name === 'visibility') {
            this.visibility = value;
            return;
        }
        this._unevaluatedLayout.setValue(name, value);
    }
    getPaintProperty(name) {
        if (endsWith(name, TRANSITION_SUFFIX)) {
            return this._transitionablePaint.getTransition(name.slice(0, -TRANSITION_SUFFIX.length));
        } else {
            return this._transitionablePaint.getValue(name);
        }
    }
    setPaintProperty(name, value, options = {}) {
        if (value !== null && value !== undefined) {
            const key = `layers.${ this.id }.paint.${ name }`;
            if (this._validate(validatePaintProperty, key, name, value, options)) {
                return false;
            }
        }
        if (endsWith(name, TRANSITION_SUFFIX)) {
            this._transitionablePaint.setTransition(name.slice(0, -TRANSITION_SUFFIX.length), value || undefined);
            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;
            const isDataDriven = newValue.isDataDriven();
            return isDataDriven || wasDataDriven || isCrossFadedProperty || this._handleOverridablePaintPropertyUpdate(name, oldValue, newValue);
        }
    }
    _handleSpecialPaintPropertyUpdate(_) {
    }
    getProgramIds() {
        return null;
    }
    getProgramConfiguration(_) {
        return null;
    }
    _handleOverridablePaintPropertyUpdate(name, oldValue, newValue) {
        return false;
    }
    isHidden(zoom) {
        if (this.minzoom && zoom < this.minzoom)
            return true;
        if (this.maxzoom && zoom >= this.maxzoom)
            return true;
        return this.visibility === 'none';
    }
    updateTransitions(parameters) {
        this._transitioningPaint = this._transitionablePaint.transitioned(parameters, this._transitioningPaint);
    }
    hasTransition() {
        return this._transitioningPaint.hasTransition();
    }
    recalculate(parameters, availableImages) {
        if (parameters.getCrossfadeParameters) {
            this._crossfadeParameters = parameters.getCrossfadeParameters();
        }
        if (this._unevaluatedLayout) {
            this.layout = this._unevaluatedLayout.possiblyEvaluate(parameters, undefined, availableImages);
        }
        this.paint = this._transitioningPaint.possiblyEvaluate(parameters, undefined, 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 && this._unevaluatedLayout.serialize(),
            'paint': this._transitionablePaint && this._transitionablePaint.serialize()
        };
        if (this.visibility) {
            output.layout = output.layout || {};
            output.layout.visibility = this.visibility;
        }
        return filterObject(output, (value, key) => {
            return value !== undefined && !(key === 'layout' && !Object.keys(value).length) && !(key === 'paint' && !Object.keys(value).length);
        });
    }
    _validate(validate, key, name, value, options = {}) {
        if (options && options.validate === false) {
            return false;
        }
        return emitValidationErrors(this, validate.call(validateStyle, {
            key,
            layerType: this.type,
            objectKey: name,
            value,
            styleSpec: spec,
            style: {
                glyphs: true,
                sprite: true
            }
        }));
    }
    is3D() {
        return false;
    }
    isSky() {
        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;
    }
}

const layout$5 = createLayout([{
        name: 'a_pos',
        components: 2,
        type: 'Int16'
    }], 4);
const {members: members$4, size: size$4, alignment: alignment$4} = layout$5;

class SegmentVector {
    constructor(segments = []) {
        this.segments = segments;
    }
    prepareSegment(numVertices, layoutVertexArray, indexArray, sortKey) {
        let segment = 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 }`);
        if (!segment || segment.vertexLength + numVertices > SegmentVector.MAX_VERTEX_ARRAY_LENGTH || segment.sortKey !== sortKey) {
            segment = {
                vertexOffset: layoutVertexArray.length,
                primitiveOffset: indexArray.length,
                vertexLength: 0,
                primitiveLength: 0
            };
            if (sortKey !== undefined)
                segment.sortKey = sortKey;
            this.segments.push(segment);
        }
        return segment;
    }
    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
            }]);
    }
}
SegmentVector.MAX_VERTEX_ARRAY_LENGTH = Math.pow(2, 16) - 1;
register('SegmentVector', SegmentVector);

var EXTENT = 8192;

const BITS = 15;
const MAX = Math.pow(2, BITS - 1) - 1;
const MIN = -MAX - 1;
function loadGeometry(feature) {
    const scale = EXTENT / feature.extent;
    const geometry = feature.loadGeometry();
    for (let r = 0; r < geometry.length; r++) {
        const ring = geometry[r];
        for (let p = 0; p < ring.length; p++) {
            const point = ring[p];
            const x = Math.round(point.x * scale);
            const y = Math.round(point.y * scale);
            point.x = clamp(x, MIN, MAX);
            point.y = clamp(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;
}

function toEvaluationFeature(feature, needGeometry) {
    return {
        type: feature.type,
        id: feature.id,
        properties: feature.properties,
        geometry: needGeometry ? loadGeometry(feature) : []
    };
}

function addCircleVertex(layoutVertexArray, x, y, extrudeX, extrudeY) {
    layoutVertexArray.emplaceBack(x * 2 + (extrudeX + 1) / 2, y * 2 + (extrudeY + 1) / 2);
}
class CircleBucket {
    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.hasPattern = false;
        this.layoutVertexArray = new StructArrayLayout2i4();
        this.indexArray = new StructArrayLayout3ui6();
        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;
        if (styleLayer.type === 'circle') {
            circleSortKey = styleLayer.layout.get('circle-sort-key');
        }
        for (const {feature, id, index, sourceLayerIndex} of features) {
            const needGeometry = this.layers[0]._featureFilter.needGeometry;
            const evaluationFeature = toEvaluationFeature(feature, needGeometry);
            if (!this.layers[0]._featureFilter.filter(new EvaluationParameters(this.zoom), evaluationFeature, canonical))
                continue;
            const sortKey = circleSortKey ? circleSortKey.evaluate(evaluationFeature, {}, canonical) : undefined;
            const bucketFeature = {
                id,
                properties: feature.properties,
                type: feature.type,
                sourceLayerIndex,
                index,
                geometry: needGeometry ? evaluationFeature.geometry : loadGeometry(feature),
                patterns: {},
                sortKey
            };
            bucketFeatures.push(bucketFeature);
        }
        if (circleSortKey) {
            bucketFeatures.sort((a, b) => {
                return 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);
            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) {
        for (const ring of geometry) {
            for (const point of ring) {
                const x = point.x;
                const y = point.y;
                if (x < 0 || x >= EXTENT || y < 0 || y >= EXTENT)
                    continue;
                const segment = this.segments.prepareSegment(4, this.layoutVertexArray, this.indexArray, feature.sortKey);
                const index = segment.vertexLength;
                addCircleVertex(this.layoutVertexArray, x, y, -1, -1);
                addCircleVertex(this.layoutVertexArray, x, y, 1, -1);
                addCircleVertex(this.layoutVertexArray, x, y, 1, 1);
                addCircleVertex(this.layoutVertexArray, x, y, -1, 1);
                this.indexArray.emplaceBack(index, index + 1, index + 2);
                this.indexArray.emplaceBack(index, index + 3, index + 2);
                segment.vertexLength += 4;
                segment.primitiveLength += 2;
            }
        }
        this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, {}, canonical);
    }
}
register('CircleBucket', CircleBucket, { omit: ['layers'] });

function polygonIntersectsPolygon(polygonA, polygonB) {
    for (let i = 0; i < polygonA.length; i++) {
        if (polygonContainsPoint(polygonB, polygonA[i]))
            return true;
    }
    for (let i = 0; i < polygonB.length; i++) {
        if (polygonContainsPoint(polygonA, polygonB[i]))
            return true;
    }
    if (lineIntersectsLine(polygonA, polygonB))
        return true;
    return false;
}
function polygonIntersectsBufferedPoint(polygon, point, radius) {
    if (polygonContainsPoint(polygon, point))
        return true;
    if (pointIntersectsBufferedLine(point, polygon, radius))
        return true;
    return false;
}
function polygonIntersectsMultiPolygon(polygon, multiPolygon) {
    if (polygon.length === 1) {
        return multiPolygonContainsPoint(multiPolygon, polygon[0]);
    }
    for (let m = 0; m < multiPolygon.length; m++) {
        const ring = multiPolygon[m];
        for (let n = 0; n < ring.length; n++) {
            if (polygonContainsPoint(polygon, ring[n]))
                return true;
        }
    }
    for (let i = 0; i < polygon.length; i++) {
        if (multiPolygonContainsPoint(multiPolygon, polygon[i]))
            return true;
    }
    for (let k = 0; k < multiPolygon.length; k++) {
        if (lineIntersectsLine(polygon, multiPolygon[k]))
            return true;
    }
    return false;
}
function polygonIntersectsBufferedMultiLine(polygon, multiLine, radius) {
    for (let i = 0; i < multiLine.length; i++) {
        const line = multiLine[i];
        if (polygon.length >= 3) {
            for (let k = 0; k < line.length; k++) {
                if (polygonContainsPoint(polygon, line[k]))
                    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 (let j = 0; j < lineB.length; j++) {
            if (pointIntersectsBufferedLine(lineB[j], lineA, radius))
                return true;
        }
    }
    for (let k = 0; k < lineA.length; k++) {
        if (pointIntersectsBufferedLine(lineA[k], 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 (let k = 0; k < rings.length; k++) {
        ring = rings[k];
        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 pointGeometry(boxX1, boxY1),
        new pointGeometry(boxX1, boxY2),
        new pointGeometry(boxX2, boxY2),
        new pointGeometry(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]);
}

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]);
}
function translate$1(queryGeometry, translate, translateAnchor, bearing, pixelsToTileUnits) {
    if (!translate[0] && !translate[1]) {
        return queryGeometry;
    }
    const pt = pointGeometry.convert(translate)._mult(pixelsToTileUnits);
    if (translateAnchor === 'viewport') {
        pt._rotate(-bearing);
    }
    const translated = [];
    for (let i = 0; i < queryGeometry.length; i++) {
        const point = queryGeometry[i];
        translated.push(point.sub(pt));
    }
    return translated;
}
function tilespaceTranslate(translate, translateAnchor, bearing, pixelsToTileUnits) {
    const pt = pointGeometry.convert(translate)._mult(pixelsToTileUnits);
    if (translateAnchor === 'viewport') {
        pt._rotate(-bearing);
    }
    return pt;
}

const layout$4 = new Properties({ 'circle-sort-key': new DataDrivenProperty(spec['layout_circle']['circle-sort-key']) });
const paint$9 = new Properties({
    'circle-radius': new DataDrivenProperty(spec['paint_circle']['circle-radius']),
    'circle-color': new DataDrivenProperty(spec['paint_circle']['circle-color']),
    'circle-blur': new DataDrivenProperty(spec['paint_circle']['circle-blur']),
    'circle-opacity': new DataDrivenProperty(spec['paint_circle']['circle-opacity']),
    'circle-translate': new DataConstantProperty(spec['paint_circle']['circle-translate']),
    'circle-translate-anchor': new DataConstantProperty(spec['paint_circle']['circle-translate-anchor']),
    'circle-pitch-scale': new DataConstantProperty(spec['paint_circle']['circle-pitch-scale']),
    'circle-pitch-alignment': new DataConstantProperty(spec['paint_circle']['circle-pitch-alignment']),
    'circle-stroke-width': new DataDrivenProperty(spec['paint_circle']['circle-stroke-width']),
    'circle-stroke-color': new DataDrivenProperty(spec['paint_circle']['circle-stroke-color']),
    'circle-stroke-opacity': new DataDrivenProperty(spec['paint_circle']['circle-stroke-opacity'])
});
var properties$9 = {
    paint: paint$9,
    layout: layout$4
};

var EPSILON = 0.000001;
var ARRAY_TYPE = typeof Float32Array !== 'undefined' ? Float32Array : Array;
if (!Math.hypot)
    Math.hypot = function () {
        var y = 0, i = arguments.length;
        while (i--) {
            y += arguments[i] * arguments[i];
        }
        return Math.sqrt(y);
    };

function create$4() {
    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;
}
function fromMat4(out, a) {
    out[0] = a[0];
    out[1] = a[1];
    out[2] = a[2];
    out[3] = a[4];
    out[4] = a[5];
    out[5] = a[6];
    out[6] = a[8];
    out[7] = a[9];
    out[8] = a[10];
    return out;
}
function fromRotation(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;
}

function create$3() {
    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;
}
function clone$1(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;
}
function identity$1(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;
}
function invert(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;
}
function multiply$2(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;
}
function translate(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;
}
function scale$2(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;
}
function rotateX$1(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;
}
function rotateY$1(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;
}
function rotateZ$1(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;
}
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;
}
function fromQuat(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[1] = yx + wz;
    out[2] = zx - wy;
    out[3] = 0;
    out[4] = yx - wz;
    out[5] = 1 - xx - zz;
    out[6] = zy + wx;
    out[7] = 0;
    out[8] = zx + wy;
    out[9] = zy - wx;
    out[10] = 1 - xx - yy;
    out[11] = 0;
    out[12] = 0;
    out[13] = 0;
    out[14] = 0;
    out[15] = 1;
    return out;
}
function perspectiveNO(out, fovy, aspect, near, far) {
    var f = 1 / Math.tan(fovy / 2), nf;
    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) {
        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;
}
var perspective = perspectiveNO;
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;
}
var ortho = orthoNO;
var mul$2 = multiply$2;

function create$2() {
    var out = new ARRAY_TYPE(3);
    if (ARRAY_TYPE != Float32Array) {
        out[0] = 0;
        out[1] = 0;
        out[2] = 0;
    }
    return out;
}
function clone(a) {
    var out = new ARRAY_TYPE(3);
    out[0] = a[0];
    out[1] = a[1];
    out[2] = a[2];
    return out;
}
function length$2(a) {
    var x = a[0];
    var y = a[1];
    var z = a[2];
    return Math.hypot(x, y, z);
}
function fromValues(x, y, z) {
    var out = new ARRAY_TYPE(3);
    out[0] = x;
    out[1] = y;
    out[2] = z;
    return out;
}
function copy(out, a) {
    out[0] = a[0];
    out[1] = a[1];
    out[2] = a[2];
    return out;
}
function add(out, a, b) {
    out[0] = a[0] + b[0];
    out[1] = a[1] + b[1];
    out[2] = a[2] + b[2];
    return out;
}
function subtract(out, a, b) {
    out[0] = a[0] - b[0];
    out[1] = a[1] - b[1];
    out[2] = a[2] - b[2];
    return out;
}
function multiply$1(out, a, b) {
    out[0] = a[0] * b[0];
    out[1] = a[1] * b[1];
    out[2] = a[2] * b[2];
    return out;
}
function divide(out, a, b) {
    out[0] = a[0] / b[0];
    out[1] = a[1] / b[1];
    out[2] = a[2] / b[2];
    return out;
}
function scale$1(out, a, b) {
    out[0] = a[0] * b;
    out[1] = a[1] * b;
    out[2] = a[2] * b;
    return out;
}
function scaleAndAdd(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;
}
function normalize$2(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;
}
function dot$2(a, b) {
    return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
function cross(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;
}
function transformMat4$1(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;
}
function transformMat3(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;
}
function transformQuat(out, a, q) {
    var qx = q[0], qy = q[1], qz = q[2], qw = q[3];
    var x = a[0], y = a[1], z = a[2];
    var uvx = qy * z - qz * y, uvy = qz * x - qx * z, uvz = qx * y - qy * x;
    var uuvx = qy * uvz - qz * uvy, uuvy = qz * uvx - qx * uvz, uuvz = qx * uvy - qy * uvx;
    var w2 = qw * 2;
    uvx *= w2;
    uvy *= w2;
    uvz *= w2;
    uuvx *= 2;
    uuvy *= 2;
    uuvz *= 2;
    out[0] = x + uvx + uuvx;
    out[1] = y + uvy + uuvy;
    out[2] = z + uvz + uuvz;
    return out;
}
function exactEquals$2(a, b) {
    return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
}
var sub = subtract;
var mul$1 = multiply$1;
var div = divide;
var len = length$2;
(function () {
    var vec = create$2();
    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;
    };
})();

function create$1() {
    var out = new ARRAY_TYPE(4);
    if (ARRAY_TYPE != Float32Array) {
        out[0] = 0;
        out[1] = 0;
        out[2] = 0;
        out[3] = 0;
    }
    return out;
}
function multiply(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;
}
function scale(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;
}
function length$1(a) {
    var x = a[0];
    var y = a[1];
    var z = a[2];
    var w = a[3];
    return Math.hypot(x, y, z, w);
}
function normalize$1(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;
}
function dot$1(a, b) {
    return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
}
function transformMat4(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;
}
function exactEquals$1(a, b) {
    return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
}
var mul = multiply;
(function () {
    var vec = create$1();
    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;
    };
})();

function create() {
    var out = new ARRAY_TYPE(4);
    if (ARRAY_TYPE != Float32Array) {
        out[0] = 0;
        out[1] = 0;
        out[2] = 0;
    }
    out[3] = 1;
    return out;
}
function identity(out) {
    out[0] = 0;
    out[1] = 0;
    out[2] = 0;
    out[3] = 1;
    return out;
}
function setAxisAngle(out, axis, rad) {
    rad = rad * 0.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;
}
function rotateX(out, a, rad) {
    rad *= 0.5;
    var ax = a[0], ay = a[1], az = a[2], aw = a[3];
    var bx = Math.sin(rad), bw = Math.cos(rad);
    out[0] = ax * bw + aw * bx;
    out[1] = ay * bw + az * bx;
    out[2] = az * bw - ay * bx;
    out[3] = aw * bw - ax * bx;
    return out;
}
function rotateY(out, a, rad) {
    rad *= 0.5;
    var ax = a[0], ay = a[1], az = a[2], aw = a[3];
    var by = Math.sin(rad), bw = Math.cos(rad);
    out[0] = ax * bw - az * by;
    out[1] = ay * bw + aw * by;
    out[2] = az * bw + ax * by;
    out[3] = aw * bw - ay * by;
    return out;
}
function rotateZ(out, a, rad) {
    rad *= 0.5;
    var ax = a[0], ay = a[1], az = a[2], aw = a[3];
    var bz = Math.sin(rad), bw = Math.cos(rad);
    out[0] = ax * bw + ay * bz;
    out[1] = ay * bw - ax * bz;
    out[2] = az * bw + aw * bz;
    out[3] = aw * bw - az * bz;
    return 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, sinom, scale0, scale1;
    cosom = ax * bx + ay * by + az * bz + aw * bw;
    if (cosom < 0) {
        cosom = -cosom;
        bx = -bx;
        by = -by;
        bz = -bz;
        bw = -bw;
    }
    if (1 - cosom > EPSILON) {
        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;
}
function conjugate(out, a) {
    out[0] = -a[0];
    out[1] = -a[1];
    out[2] = -a[2];
    out[3] = a[3];
    return out;
}
function fromMat3(out, m) {
    var fTrace = m[0] + m[4] + m[8];
    var fRoot;
    if (fTrace > 0) {
        fRoot = Math.sqrt(fTrace + 1);
        out[3] = 0.5 * fRoot;
        fRoot = 0.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] = 0.5 * fRoot;
        fRoot = 0.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;
}
var length = length$1;
var normalize = normalize$1;
var exactEquals = exactEquals$1;
(function () {
    var tmpvec3 = create$2();
    var xUnitVec3 = fromValues(1, 0, 0);
    var yUnitVec3 = fromValues(0, 1, 0);
    return function (out, a, b) {
        var dot = dot$2(a, b);
        if (dot < -0.999999) {
            cross(tmpvec3, xUnitVec3, a);
            if (len(tmpvec3) < 0.000001)
                cross(tmpvec3, yUnitVec3, a);
            normalize$2(tmpvec3, tmpvec3);
            setAxisAngle(out, tmpvec3, Math.PI);
            return out;
        } else if (dot > 0.999999) {
            out[0] = 0;
            out[1] = 0;
            out[2] = 0;
            out[3] = 1;
            return out;
        } else {
            cross(tmpvec3, a, b);
            out[0] = tmpvec3[0];
            out[1] = tmpvec3[1];
            out[2] = tmpvec3[2];
            out[3] = 1 + dot;
            return normalize(out, out);
        }
    };
})();
(function () {
    var temp1 = create();
    var temp2 = create();
    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$4();
    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(out, fromMat3(out, matr));
    };
})();

class CircleStyleLayer extends StyleLayer {
    constructor(layer) {
        super(layer, properties$9);
    }
    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, zoom, transform, pixelPosMatrix, elevationHelper) {
        const alignWithMap = this.paint.get('circle-pitch-alignment') === 'map';
        if (alignWithMap && queryGeometry.queryGeometry.isAboveHorizon)
            return false;
        const translation = tilespaceTranslate(this.paint.get('circle-translate'), this.paint.get('circle-translate-anchor'), transform.angle, queryGeometry.pixelToTileUnitsFactor);
        const radius = this.paint.get('circle-radius').evaluate(feature, featureState);
        const stroke = this.paint.get('circle-stroke-width').evaluate(feature, featureState);
        const size = radius + stroke;
        const transformedSize = alignWithMap ? size * queryGeometry.pixelToTileUnitsFactor : size;
        for (const ring of geometry) {
            for (const point of ring) {
                const translatedPoint = point.add(translation);
                const z = elevationHelper && transform.elevation ? transform.elevation.exaggeration() * elevationHelper.getElevationAt(translatedPoint.x, translatedPoint.y, true) : 0;
                const transformedPoint = alignWithMap ? translatedPoint : projectPoint(translatedPoint, z, pixelPosMatrix);
                const transformedPolygon = alignWithMap ? queryGeometry.tilespaceRays.map(r => intersectAtHeight(r, z)) : queryGeometry.queryGeometry.screenGeometry;
                let adjustedSize = transformedSize;
                const projectedCenter = transformMat4([], [
                    point.x,
                    point.y,
                    z,
                    1
                ], pixelPosMatrix);
                if (this.paint.get('circle-pitch-scale') === 'viewport' && this.paint.get('circle-pitch-alignment') === 'map') {
                    adjustedSize *= projectedCenter[3] / transform.cameraToCenterDistance;
                } else if (this.paint.get('circle-pitch-scale') === 'map' && this.paint.get('circle-pitch-alignment') === 'viewport') {
                    adjustedSize *= transform.cameraToCenterDistance / projectedCenter[3];
                }
                if (polygonIntersectsBufferedPoint(transformedPolygon, transformedPoint, adjustedSize))
                    return true;
            }
        }
        return false;
    }
    getProgramIds() {
        return ['circle'];
    }
    getProgramConfiguration(zoom) {
        return new ProgramConfiguration(this, zoom);
    }
}
function projectPoint(p, z, pixelPosMatrix) {
    const point = transformMat4([], [
        p.x,
        p.y,
        z,
        1
    ], pixelPosMatrix);
    return new pointGeometry(point[0] / point[3], point[1] / point[3]);
}
const origin = fromValues(0, 0, 0);
const up = fromValues(0, 0, 1);
function intersectAtHeight(r, z) {
    const intersectionPt = create$2();
    origin[2] = z;
    r.intersectsPlane(origin, up, intersectionPt);
    return new pointGeometry(intersectionPt[0], intersectionPt[1]);
}

class HeatmapBucket extends CircleBucket {
}
register('HeatmapBucket', HeatmapBucket, { omit: ['layers'] });

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');
    }
    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;
    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;
}
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);
    }
}
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);
    }
}
register('AlphaImage', AlphaImage);
register('RGBAImage', RGBAImage);

const paint$8 = new Properties({
    'heatmap-radius': new DataDrivenProperty(spec['paint_heatmap']['heatmap-radius']),
    'heatmap-weight': new DataDrivenProperty(spec['paint_heatmap']['heatmap-weight']),
    'heatmap-intensity': new DataConstantProperty(spec['paint_heatmap']['heatmap-intensity']),
    'heatmap-color': new ColorRampProperty(spec['paint_heatmap']['heatmap-color']),
    'heatmap-opacity': new DataConstantProperty(spec['paint_heatmap']['heatmap-opacity'])
});
var properties$8 = { paint: paint$8 };

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
    });
    const renderPixel = (stride, index, progress) => {
        evaluationGlobals[params.evaluationKey] = progress;
        const pxColor = params.expression.evaluate(evaluationGlobals);
        image.data[stride + index + 0] = Math.floor(pxColor.r * 255 / pxColor.a);
        image.data[stride + index + 1] = Math.floor(pxColor.g * 255 / pxColor.a);
        image.data[stride + index + 2] = Math.floor(pxColor.b * 255 / pxColor.a);
        image.data[stride + index + 3] = Math.floor(pxColor.a * 255);
    };
    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;
}

class HeatmapStyleLayer extends StyleLayer {
    createBucket(options) {
        return new HeatmapBucket(options);
    }
    constructor(layer) {
        super(layer, properties$8);
        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.heatmapFbo) {
            this.heatmapFbo.destroy();
            this.heatmapFbo = null;
        }
    }
    queryRadius() {
        return 0;
    }
    queryIntersectsFeature() {
        return false;
    }
    hasOffscreenPass() {
        return this.paint.get('heatmap-opacity') !== 0 && this.visibility !== 'none';
    }
    getProgramIds() {
        return [
            'heatmap',
            'heatmapTexture'
        ];
    }
    getProgramConfiguration(zoom) {
        return new ProgramConfiguration(this, zoom);
    }
}

const paint$7 = new Properties({
    'hillshade-illumination-direction': new DataConstantProperty(spec['paint_hillshade']['hillshade-illumination-direction']),
    'hillshade-illumination-anchor': new DataConstantProperty(spec['paint_hillshade']['hillshade-illumination-anchor']),
    'hillshade-exaggeration': new DataConstantProperty(spec['paint_hillshade']['hillshade-exaggeration']),
    'hillshade-shadow-color': new DataConstantProperty(spec['paint_hillshade']['hillshade-shadow-color']),
    'hillshade-highlight-color': new DataConstantProperty(spec['paint_hillshade']['hillshade-highlight-color']),
    'hillshade-accent-color': new DataConstantProperty(spec['paint_hillshade']['hillshade-accent-color'])
});
var properties$7 = { paint: paint$7 };

class HillshadeStyleLayer extends StyleLayer {
    constructor(layer) {
        super(layer, properties$7);
    }
    hasOffscreenPass() {
        return this.paint.get('hillshade-exaggeration') !== 0 && this.visibility !== 'none';
    }
    getProgramIds() {
        return [
            'hillshade',
            'hillshadePrepare'
        ];
    }
    getProgramConfiguration(zoom) {
        return new ProgramConfiguration(this, zoom);
    }
}

const layout$3 = createLayout([{
        name: 'a_pos',
        components: 2,
        type: 'Int16'
    }], 4);
const {members: members$3, size: size$3, alignment: alignment$3} = layout$3;

var earcut_1 = earcut;
var _default$1 = earcut;
function earcut(data, holeIndices, dim) {
    dim = dim || 2;
    var hasHoles = holeIndices && holeIndices.length, outerLen = hasHoles ? holeIndices[0] * dim : data.length, outerNode = linkedList(data, 0, outerLen, dim, true), triangles = [];
    if (!outerNode || outerNode.next === outerNode.prev)
        return triangles;
    var minX, minY, maxX, maxY, x, y, invSize;
    if (hasHoles)
        outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
    if (data.length > 80 * dim) {
        minX = maxX = data[0];
        minY = maxY = data[1];
        for (var i = dim; i < outerLen; i += dim) {
            x = data[i];
            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, dim, minX, minY, invSize, 0);
    return triangles;
}
function linkedList(data, start, end, dim, clockwise) {
    var i, last;
    if (clockwise === signedArea$2(data, start, end, dim) > 0) {
        for (i = start; i < end; i += dim)
            last = insertNode(i, data[i], data[i + 1], last);
    } else {
        for (i = end - dim; i >= start; i -= dim)
            last = insertNode(i, data[i], data[i + 1], last);
    }
    if (last && equals(last, last.next)) {
        removeNode(last);
        last = last.next;
    }
    return last;
}
function filterPoints(start, end) {
    if (!start)
        return start;
    if (!end)
        end = start;
    var p = start, again;
    do {
        again = false;
        if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
            removeNode(p);
            p = end = p.prev;
            if (p === p.next)
                break;
            again = true;
        } else {
            p = p.next;
        }
    } while (again || p !== end);
    return end;
}
function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
    if (!ear)
        return;
    if (!pass && invSize)
        indexCurve(ear, minX, minY, invSize);
    var stop = ear, prev, next;
    while (ear.prev !== ear.next) {
        prev = ear.prev;
        next = ear.next;
        if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
            triangles.push(prev.i / dim | 0);
            triangles.push(ear.i / dim | 0);
            triangles.push(next.i / dim | 0);
            removeNode(ear);
            ear = next.next;
            stop = next.next;
            continue;
        }
        ear = next;
        if (ear === stop) {
            if (!pass) {
                earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
            } else if (pass === 1) {
                ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
                earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
            } else if (pass === 2) {
                splitEarcut(ear, triangles, dim, minX, minY, invSize);
            }
            break;
        }
    }
}
function isEar(ear) {
    var a = ear.prev, b = ear, c = ear.next;
    if (area(a, b, c) >= 0)
        return false;
    var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
    var x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy;
    var p = c.next;
    while (p !== a) {
        if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && 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;
}
function isEarHashed(ear, minX, minY, invSize) {
    var a = ear.prev, b = ear, c = ear.next;
    if (area(a, b, c) >= 0)
        return false;
    var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
    var x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy;
    var minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize);
    var p = ear.prevZ, n = ear.nextZ;
    while (p && p.z >= minZ && n && n.z <= maxZ) {
        if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0)
            return false;
        p = p.prevZ;
        if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0)
            return false;
        n = n.nextZ;
    }
    while (p && p.z >= minZ) {
        if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0)
            return false;
        p = p.prevZ;
    }
    while (n && n.z <= maxZ) {
        if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && 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;
}
function cureLocalIntersections(start, triangles, dim) {
    var p = start;
    do {
        var a = p.prev, b = p.next.next;
        if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
            triangles.push(a.i / dim | 0);
            triangles.push(p.i / dim | 0);
            triangles.push(b.i / dim | 0);
            removeNode(p);
            removeNode(p.next);
            p = start = b;
        }
        p = p.next;
    } while (p !== start);
    return filterPoints(p);
}
function splitEarcut(start, triangles, dim, minX, minY, invSize) {
    var a = start;
    do {
        var b = a.next.next;
        while (b !== a.prev) {
            if (a.i !== b.i && isValidDiagonal(a, b)) {
                var c = splitPolygon(a, b);
                a = filterPoints(a, a.next);
                c = filterPoints(c, c.next);
                earcutLinked(a, triangles, dim, minX, minY, invSize, 0);
                earcutLinked(c, triangles, dim, minX, minY, invSize, 0);
                return;
            }
            b = b.next;
        }
        a = a.next;
    } while (a !== start);
}
function eliminateHoles(data, holeIndices, outerNode, dim) {
    var queue = [], i, len, start, end, list;
    for (i = 0, len = holeIndices.length; i < len; i++) {
        start = holeIndices[i] * dim;
        end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
        list = linkedList(data, start, end, dim, false);
        if (list === list.next)
            list.steiner = true;
        queue.push(getLeftmost(list));
    }
    queue.sort(compareX);
    for (i = 0; i < queue.length; i++) {
        outerNode = eliminateHole(queue[i], outerNode);
    }
    return outerNode;
}
function compareX(a, b) {
    return a.x - b.x;
}
function eliminateHole(hole, outerNode) {
    var bridge = findHoleBridge(hole, outerNode);
    if (!bridge) {
        return outerNode;
    }
    var bridgeReverse = splitPolygon(bridge, hole);
    filterPoints(bridgeReverse, bridgeReverse.next);
    return filterPoints(bridge, bridge.next);
}
function findHoleBridge(hole, outerNode) {
    var p = outerNode, hx = hole.x, hy = hole.y, qx = -Infinity, m;
    do {
        if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
            var 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 !== outerNode);
    if (!m)
        return null;
    var stop = m, mx = m.x, my = m.y, tanMin = Infinity, tan;
    p = m;
    do {
        if (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)) {
            tan = Math.abs(hy - p.y) / (hx - p.x);
            if (locallyInside(p, hole) && (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;
}
function sectorContainsSector(m, p) {
    return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
}
function indexCurve(start, minX, minY, invSize) {
    var p = start;
    do {
        if (p.z === 0)
            p.z = zOrder(p.x, p.y, minX, minY, invSize);
        p.prevZ = p.prev;
        p.nextZ = p.next;
        p = p.next;
    } while (p !== start);
    p.prevZ.nextZ = null;
    p.prevZ = null;
    sortLinked(p);
}
function sortLinked(list) {
    var i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1;
    do {
        p = list;
        list = null;
        tail = null;
        numMerges = 0;
        while (p) {
            numMerges++;
            q = p;
            pSize = 0;
            for (i = 0; i < inSize; i++) {
                pSize++;
                q = q.nextZ;
                if (!q)
                    break;
            }
            qSize = inSize;
            while (pSize > 0 || qSize > 0 && q) {
                if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
                    e = p;
                    p = p.nextZ;
                    pSize--;
                } else {
                    e = q;
                    q = q.nextZ;
                    qSize--;
                }
                if (tail)
                    tail.nextZ = e;
                else
                    list = e;
                e.prevZ = tail;
                tail = e;
            }
            p = q;
        }
        tail.nextZ = null;
        inSize *= 2;
    } while (numMerges > 1);
    return list;
}
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;
}
function getLeftmost(start) {
    var 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;
}
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);
}
function isValidDiagonal(a, b) {
    return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && (area(a.prev, a, b.prev) || area(a, b.prev, b)) || equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0);
}
function area(p, q, r) {
    return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
}
function equals(p1, p2) {
    return p1.x === p2.x && p1.y === p2.y;
}
function intersects(p1, q1, p2, q2) {
    var o1 = sign(area(p1, q1, p2));
    var o2 = sign(area(p1, q1, q2));
    var o3 = sign(area(p2, q2, p1));
    var o4 = sign(area(p2, q2, q1));
    if (o1 !== o2 && o3 !== o4)
        return true;
    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;
}
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);
}
function sign(num) {
    return num > 0 ? 1 : num < 0 ? -1 : 0;
}
function intersectsPolygon(a, b) {
    var p = a;
    do {
        if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b))
            return true;
        p = p.next;
    } while (p !== a);
    return false;
}
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;
}
function middleInside(a, b) {
    var p = a, inside = false, px = (a.x + b.x) / 2, py = (a.y + b.y) / 2;
    do {
        if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)
            inside = !inside;
        p = p.next;
    } while (p !== a);
    return inside;
}
function splitPolygon(a, b) {
    var a2 = new Node(a.i, a.x, a.y), b2 = new Node(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;
}
function insertNode(i, x, y, last) {
    var p = new Node(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;
}
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;
}
function Node(i, x, y) {
    this.i = i;
    this.x = x;
    this.y = y;
    this.prev = null;
    this.next = null;
    this.z = 0;
    this.prevZ = null;
    this.nextZ = null;
    this.steiner = false;
}
earcut.deviation = function (data, holeIndices, dim, triangles) {
    var hasHoles = holeIndices && holeIndices.length;
    var outerLen = hasHoles ? holeIndices[0] * dim : data.length;
    var polygonArea = Math.abs(signedArea$2(data, 0, outerLen, dim));
    if (hasHoles) {
        for (var i = 0, len = holeIndices.length; i < len; i++) {
            var start = holeIndices[i] * dim;
            var end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
            polygonArea -= Math.abs(signedArea$2(data, start, end, dim));
        }
    }
    var trianglesArea = 0;
    for (i = 0; i < triangles.length; i += 3) {
        var a = triangles[i] * dim;
        var b = triangles[i + 1] * dim;
        var c = triangles[i + 2] * dim;
        trianglesArea += Math.abs((data[a] - data[c]) * (data[b + 1] - data[a + 1]) - (data[a] - data[b]) * (data[c + 1] - data[a + 1]));
    }
    return polygonArea === 0 && trianglesArea === 0 ? 0 : Math.abs((trianglesArea - polygonArea) / polygonArea);
};
function signedArea$2(data, start, end, dim) {
    var sum = 0;
    for (var i = start, j = end - dim; i < end; i += dim) {
        sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
        j = i;
    }
    return sum;
}
earcut.flatten = function (data) {
    var dim = data[0][0].length, result = {
            vertices: [],
            holes: [],
            dimensions: dim
        }, holeIndex = 0;
    for (var i = 0; i < data.length; i++) {
        for (var j = 0; j < data[i].length; j++) {
            for (var d = 0; d < dim; d++)
                result.vertices.push(data[i][j][d]);
        }
        if (i > 0) {
            holeIndex += data[i - 1].length;
            result.holes.push(holeIndex);
        }
    }
    return result;
};
earcut_1.default = _default$1;

function quickselect(arr, k, left, right, compare) {
    quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare$1);
}
function quickselectStep(arr, k, left, right, compare) {
    while (right > left) {
        if (right - left > 600) {
            var n = right - left + 1;
            var m = k - left + 1;
            var z = Math.log(n);
            var s = 0.5 * Math.exp(2 * z / 3);
            var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
            var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
            var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
            quickselectStep(arr, k, newLeft, newRight, compare);
        }
        var t = arr[k];
        var i = left;
        var j = right;
        swap(arr, left, k);
        if (compare(arr[right], t) > 0)
            swap(arr, left, right);
        while (i < j) {
            swap(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(arr, left, j);
        else {
            j++;
            swap(arr, j, right);
        }
        if (j <= k)
            left = j + 1;
        if (k <= j)
            right = j - 1;
    }
}
function swap(arr, i, j) {
    var tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}
function defaultCompare$1(a, b) {
    return a < b ? -1 : a > b ? 1 : 0;
}

function classifyRings$2(rings, maxRings) {
    const len = rings.length;
    if (len <= 1)
        return [rings];
    const polygons = [];
    let polygon, ccw;
    for (let i = 0; i < len; i++) {
        const area = calculateSignedArea(rings[i]);
        if (area === 0)
            continue;
        rings[i].area = Math.abs(area);
        if (ccw === undefined)
            ccw = area < 0;
        if (ccw === area < 0) {
            if (polygon)
                polygons.push(polygon);
            polygon = [rings[i]];
        } else {
            polygon.push(rings[i]);
        }
    }
    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;
}

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, zoom, options) {
    const patterns = options.patternDependencies;
    for (const layer of layers) {
        const patternProperty = layer.paint.get(`${ type }-pattern`);
        const patternPropertyValue = patternProperty.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 && min.name ? min.name : min;
            mid = mid && mid.name ? mid.name : mid;
            max = max && max.name ? max.name : max;
            patterns[min] = true;
            patterns[mid] = true;
            patterns[max] = true;
            patternFeature.patterns[layer.id] = {
                min,
                mid,
                max
            };
        }
    }
    return patternFeature;
}

const EARCUT_MAX_RINGS$1 = 500;
class FillBucket {
    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.hasPattern = false;
        this.patternFeatures = [];
        this.layoutVertexArray = new StructArrayLayout2i4();
        this.indexArray = new StructArrayLayout3ui6();
        this.indexArray2 = new StructArrayLayout2ui4();
        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.hasPattern = hasPattern('fill', this.layers, options);
        const fillSortKey = this.layers[0].layout.get('fill-sort-key');
        const bucketFeatures = [];
        for (const {feature, id, index, sourceLayerIndex} of features) {
            const needGeometry = this.layers[0]._featureFilter.needGeometry;
            const evaluationFeature = toEvaluationFeature(feature, needGeometry);
            if (!this.layers[0]._featureFilter.filter(new EvaluationParameters(this.zoom), evaluationFeature, canonical))
                continue;
            const sortKey = fillSortKey ? fillSortKey.evaluate(evaluationFeature, {}, canonical, options.availableImages) : undefined;
            const bucketFeature = {
                id,
                properties: feature.properties,
                type: feature.type,
                sourceLayerIndex,
                index,
                geometry: needGeometry ? evaluationFeature.geometry : loadGeometry(feature),
                patterns: {},
                sortKey
            };
            bucketFeatures.push(bucketFeature);
        }
        if (fillSortKey) {
            bucketFeatures.sort((a, b) => {
                return a.sortKey - b.sortKey;
            });
        }
        for (const bucketFeature of bucketFeatures) {
            const {geometry, index, sourceLayerIndex} = bucketFeature;
            if (this.hasPattern) {
                const patternFeature = addPatternDependencies('fill', this.layers, bucketFeature, this.zoom, options);
                this.patternFeatures.push(patternFeature);
            } else {
                this.addFeature(bucketFeature, geometry, index, canonical, {});
            }
            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);
        }
    }
    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) {
        for (const polygon of classifyRings$2(geometry, EARCUT_MAX_RINGS$1)) {
            let numVertices = 0;
            for (const ring of polygon) {
                numVertices += ring.length;
            }
            const triangleSegment = this.segments.prepareSegment(numVertices, this.layoutVertexArray, this.indexArray);
            const triangleIndex = triangleSegment.vertexLength;
            const flattened = [];
            const holeIndices = [];
            for (const ring of polygon) {
                if (ring.length === 0) {
                    continue;
                }
                if (ring !== polygon[0]) {
                    holeIndices.push(flattened.length / 2);
                }
                const lineSegment = this.segments2.prepareSegment(ring.length, this.layoutVertexArray, this.indexArray2);
                const lineIndex = lineSegment.vertexLength;
                this.layoutVertexArray.emplaceBack(ring[0].x, ring[0].y);
                this.indexArray2.emplaceBack(lineIndex + ring.length - 1, lineIndex);
                flattened.push(ring[0].x);
                flattened.push(ring[0].y);
                for (let i = 1; i < ring.length; i++) {
                    this.layoutVertexArray.emplaceBack(ring[i].x, ring[i].y);
                    this.indexArray2.emplaceBack(lineIndex + i - 1, lineIndex + i);
                    flattened.push(ring[i].x);
                    flattened.push(ring[i].y);
                }
                lineSegment.vertexLength += ring.length;
                lineSegment.primitiveLength += ring.length;
            }
            const indices = earcut_1(flattened, holeIndices);
            for (let i = 0; i < indices.length; i += 3) {
                this.indexArray.emplaceBack(triangleIndex + indices[i], triangleIndex + indices[i + 1], triangleIndex + indices[i + 2]);
            }
            triangleSegment.vertexLength += numVertices;
            triangleSegment.primitiveLength += indices.length / 3;
        }
        this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, imagePositions, canonical);
    }
}
register('FillBucket', FillBucket, {
    omit: [
        'layers',
        'patternFeatures'
    ]
});

const layout$2 = new Properties({ 'fill-sort-key': new DataDrivenProperty(spec['layout_fill']['fill-sort-key']) });
const paint$6 = new Properties({
    'fill-antialias': new DataConstantProperty(spec['paint_fill']['fill-antialias']),
    'fill-opacity': new DataDrivenProperty(spec['paint_fill']['fill-opacity']),
    'fill-color': new DataDrivenProperty(spec['paint_fill']['fill-color']),
    'fill-outline-color': new DataDrivenProperty(spec['paint_fill']['fill-outline-color']),
    'fill-translate': new DataConstantProperty(spec['paint_fill']['fill-translate']),
    'fill-translate-anchor': new DataConstantProperty(spec['paint_fill']['fill-translate-anchor']),
    'fill-pattern': new CrossFadedDataDrivenProperty(spec['paint_fill']['fill-pattern'])
});
var properties$6 = {
    paint: paint$6,
    layout: layout$2
};

class FillStyleLayer extends StyleLayer {
    constructor(layer) {
        super(layer, properties$6);
    }
    getProgramIds() {
        const pattern = this.paint.get('fill-pattern');
        const image = pattern && pattern.constantOr(1);
        const ids = [image ? 'fillPattern' : 'fill'];
        if (this.paint.get('fill-antialias')) {
            ids.push(image && !this.getPaintProperty('fill-outline-color') ? 'fillOutlinePattern' : 'fillOutline');
        }
        return ids;
    }
    getProgramConfiguration(zoom) {
        return new ProgramConfiguration(this, zoom);
    }
    recalculate(parameters, availableImages) {
        super.recalculate(parameters, availableImages);
        const outlineColor = this.paint._values['fill-outline-color'];
        if (outlineColor.value.kind === 'constant' && outlineColor.value.value === undefined) {
            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, feature, featureState, geometry, zoom, transform) {
        if (queryGeometry.queryGeometry.isAboveHorizon)
            return false;
        const translatedPolygon = translate$1(queryGeometry.tilespaceGeometry, this.paint.get('fill-translate'), this.paint.get('fill-translate-anchor'), transform.angle, queryGeometry.pixelToTileUnitsFactor);
        return polygonIntersectsMultiPolygon(translatedPolygon, geometry);
    }
    isTileClipped() {
        return true;
    }
}

const fillExtrusionAttributes = createLayout([{
        name: 'a_pos_normal_ed',
        components: 4,
        type: 'Int16'
    }]);
const centroidAttributes = createLayout([{
        name: 'a_centroid_pos',
        components: 2,
        type: 'Uint16'
    }]);
const {members: members$2, size: size$2, alignment: alignment$2} = fillExtrusionAttributes;

var vectortilefeature$1 = VectorTileFeature$3;
function VectorTileFeature$3(pbf, end, extent, keys, values) {
    this.properties = {};
    this.extent = extent;
    this.type = 0;
    this._pbf = pbf;
    this._geometry = -1;
    this._keys = keys;
    this._values = values;
    pbf.readFields(readFeature$1, this, end);
}
function readFeature$1(tag, feature, pbf) {
    if (tag == 1)
        feature.id = pbf.readVarint();
    else if (tag == 2)
        readTag$1(pbf, feature);
    else if (tag == 3)
        feature.type = pbf.readVarint();
    else if (tag == 4)
        feature._geometry = pbf.pos;
}
function readTag$1(pbf, feature) {
    var end = pbf.readVarint() + pbf.pos;
    while (pbf.pos < end) {
        var key = feature._keys[pbf.readVarint()], value = feature._values[pbf.readVarint()];
        feature.properties[key] = value;
    }
}
VectorTileFeature$3.types = [
    'Unknown',
    'Point',
    'LineString',
    'Polygon'
];
VectorTileFeature$3.prototype.loadGeometry = function () {
    var pbf = this._pbf;
    pbf.pos = this._geometry;
    var end = pbf.readVarint() + pbf.pos, cmd = 1, length = 0, x = 0, y = 0, lines = [], line;
    while (pbf.pos < end) {
        if (length <= 0) {
            var cmdLen = pbf.readVarint();
            cmd = cmdLen & 7;
            length = cmdLen >> 3;
        }
        length--;
        if (cmd === 1 || cmd === 2) {
            x += pbf.readSVarint();
            y += pbf.readSVarint();
            if (cmd === 1) {
                if (line)
                    lines.push(line);
                line = [];
            }
            line.push(new pointGeometry(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;
};
VectorTileFeature$3.prototype.bbox = function () {
    var pbf = this._pbf;
    pbf.pos = this._geometry;
    var end = pbf.readVarint() + pbf.pos, cmd = 1, length = 0, x = 0, y = 0, x1 = Infinity, x2 = -Infinity, y1 = Infinity, y2 = -Infinity;
    while (pbf.pos < end) {
        if (length <= 0) {
            var cmdLen = pbf.readVarint();
            cmd = cmdLen & 7;
            length = cmdLen >> 3;
        }
        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
    ];
};
VectorTileFeature$3.prototype.toGeoJSON = function (x, y, z) {
    var size = this.extent * Math.pow(2, z), x0 = this.extent * x, y0 = this.extent * y, coords = this.loadGeometry(), type = VectorTileFeature$3.types[this.type], i, j;
    function project(line) {
        for (var j = 0; j < line.length; j++) {
            var p = line[j], y2 = 180 - (p.y + y0) * 360 / size;
            line[j] = [
                (p.x + x0) * 360 / size - 180,
                360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90
            ];
        }
    }
    switch (this.type) {
    case 1:
        var points = [];
        for (i = 0; i < coords.length; i++) {
            points[i] = coords[i][0];
        }
        coords = points;
        project(coords);
        break;
    case 2:
        for (i = 0; i < coords.length; i++) {
            project(coords[i]);
        }
        break;
    case 3:
        coords = classifyRings$1(coords);
        for (i = 0; i < coords.length; i++) {
            for (j = 0; j < coords[i].length; j++) {
                project(coords[i][j]);
            }
        }
        break;
    }
    if (coords.length === 1) {
        coords = coords[0];
    } else {
        type = 'Multi' + type;
    }
    var result = {
        type: 'Feature',
        geometry: {
            type: type,
            coordinates: coords
        },
        properties: this.properties
    };
    if ('id' in this) {
        result.id = this.id;
    }
    return result;
};
function classifyRings$1(rings) {
    var len = rings.length;
    if (len <= 1)
        return [rings];
    var polygons = [], polygon, ccw;
    for (var i = 0; i < len; i++) {
        var area = signedArea$1(rings[i]);
        if (area === 0)
            continue;
        if (ccw === undefined)
            ccw = area < 0;
        if (ccw === area < 0) {
            if (polygon)
                polygons.push(polygon);
            polygon = [rings[i]];
        } else {
            polygon.push(rings[i]);
        }
    }
    if (polygon)
        polygons.push(polygon);
    return polygons;
}
function signedArea$1(ring) {
    var sum = 0;
    for (var 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$1 = VectorTileLayer$3;
function VectorTileLayer$3(pbf, end) {
    this.version = 1;
    this.name = null;
    this.extent = 4096;
    this.length = 0;
    this._pbf = pbf;
    this._keys = [];
    this._values = [];
    this._features = [];
    pbf.readFields(readLayer$1, this, end);
    this.length = this._features.length;
}
function readLayer$1(tag, layer, pbf) {
    if (tag === 15)
        layer.version = pbf.readVarint();
    else if (tag === 1)
        layer.name = pbf.readString();
    else if (tag === 5)
        layer.extent = pbf.readVarint();
    else if (tag === 2)
        layer._features.push(pbf.pos);
    else if (tag === 3)
        layer._keys.push(pbf.readString());
    else if (tag === 4)
        layer._values.push(readValueMessage$1(pbf));
}
function readValueMessage$1(pbf) {
    var value = null, end = pbf.readVarint() + pbf.pos;
    while (pbf.pos < end) {
        var tag = pbf.readVarint() >> 3;
        value = tag === 1 ? pbf.readString() : tag === 2 ? pbf.readFloat() : tag === 3 ? pbf.readDouble() : tag === 4 ? pbf.readVarint64() : tag === 5 ? pbf.readVarint() : tag === 6 ? pbf.readSVarint() : tag === 7 ? pbf.readBoolean() : null;
    }
    return value;
}
VectorTileLayer$3.prototype.feature = function (i) {
    if (i < 0 || i >= this._features.length)
        throw new Error('feature index out of bounds');
    this._pbf.pos = this._features[i];
    var end = this._pbf.readVarint() + this._pbf.pos;
    return new vectortilefeature$1(this._pbf, end, this.extent, this._keys, this._values);
};

var vectortile$1 = VectorTile$3;
function VectorTile$3(pbf, end) {
    this.layers = pbf.readFields(readTile$1, {}, end);
}
function readTile$1(tag, layers, pbf) {
    if (tag === 3) {
        var layer = new vectortilelayer$1(pbf, pbf.readVarint() + pbf.pos);
        if (layer.length)
            layers[layer.name] = layer;
    }
}

var VectorTile$2 = vectortile$1;
var VectorTileFeature$2 = vectortilefeature$1;
var VectorTileLayer$2 = vectortilelayer$1;

var vectorTile$1 = {
	VectorTile: VectorTile$2,
	VectorTileFeature: VectorTileFeature$2,
	VectorTileLayer: VectorTileLayer$2
};

const vectorTileFeatureTypes$2 = vectorTile$1.VectorTileFeature.types;
const EARCUT_MAX_RINGS = 500;
const FACTOR = Math.pow(2, 13);
const ELEVATION_SCALE = 7;
const ELEVATION_OFFSET = 450;
function addVertex$1(vertexArray, x, y, nxRatio, nySign, normalUp, top, e) {
    vertexArray.emplaceBack((x << 1) + top, (y << 1) + normalUp, (Math.floor(nxRatio * FACTOR) << 1) + nySign, Math.round(e));
}
class PartMetadata {
    constructor() {
        this.acc = new pointGeometry(0, 0);
        this.polyCount = [];
    }
    startRing(p) {
        this.currentPolyCount = {
            edges: 0,
            top: 0
        };
        this.polyCount.push(this.currentPolyCount);
        if (this.min)
            return;
        this.min = new pointGeometry(p.x, p.y);
        this.max = new pointGeometry(p.x, p.y);
    }
    append(p, prev) {
        this.currentPolyCount.edges++;
        this.acc._add(p);
        let checkBorders = !!this.borders;
        const min = this.min, max = this.max;
        if (p.x < min.x) {
            min.x = p.x;
            checkBorders = true;
        } else if (p.x > max.x) {
            max.x = p.x;
            checkBorders = true;
        }
        if (p.y < min.y) {
            min.y = p.y;
            checkBorders = true;
        } else if (p.y > max.y) {
            max.y = p.y;
            checkBorders = true;
        }
        if (((p.x === 0 || p.x === EXTENT) && p.x === prev.x) !== ((p.y === 0 || p.y === EXTENT) && p.y === prev.y)) {
            this.processBorderOverlap(p, prev);
        }
        if (checkBorders)
            this.checkBorderIntersection(p, prev);
    }
    checkBorderIntersection(p, prev) {
        if (prev.x < 0 !== p.x < 0) {
            this.addBorderIntersection(0, number(prev.y, p.y, (0 - prev.x) / (p.x - prev.x)));
        }
        if (prev.x > EXTENT !== p.x > EXTENT) {
            this.addBorderIntersection(1, number(prev.y, p.y, (EXTENT - prev.x) / (p.x - prev.x)));
        }
        if (prev.y < 0 !== p.y < 0) {
            this.addBorderIntersection(2, number(prev.x, p.x, (0 - prev.y) / (p.y - prev.y)));
        }
        if (prev.y > EXTENT !== p.y > EXTENT) {
            this.addBorderIntersection(3, number(prev.x, p.x, (EXTENT - prev.y) / (p.y - prev.y)));
        }
    }
    addBorderIntersection(index, i) {
        if (!this.borders) {
            this.borders = [
                [
                    Number.MAX_VALUE,
                    -Number.MAX_VALUE
                ],
                [
                    Number.MAX_VALUE,
                    -Number.MAX_VALUE
                ],
                [
                    Number.MAX_VALUE,
                    -Number.MAX_VALUE
                ],
                [
                    Number.MAX_VALUE,
                    -Number.MAX_VALUE
                ]
            ];
        }
        const b = this.borders[index];
        if (i < b[0])
            b[0] = i;
        if (i > b[1])
            b[1] = i;
    }
    processBorderOverlap(p, prev) {
        if (p.x === prev.x) {
            if (p.y === prev.y)
                return;
            const index = p.x === 0 ? 0 : 1;
            this.addBorderIntersection(index, prev.y);
            this.addBorderIntersection(index, p.y);
        } else {
            const index = p.y === 0 ? 2 : 3;
            this.addBorderIntersection(index, prev.x);
            this.addBorderIntersection(index, p.x);
        }
    }
    centroid() {
        const count = this.polyCount.reduce((acc, p) => acc + p.edges, 0);
        return count !== 0 ? this.acc.div(count)._round() : new pointGeometry(0, 0);
    }
    span() {
        return new pointGeometry(this.max.x - this.min.x, this.max.y - this.min.y);
    }
    intersectsCount() {
        return this.borders.reduce((acc, p) => acc + +(p[0] !== Number.MAX_VALUE), 0);
    }
}
class FillExtrusionBucket {
    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.hasPattern = false;
        this.layoutVertexArray = new StructArrayLayout4i8();
        this.centroidVertexArray = new FillExtrusionCentroidArray();
        this.indexArray = new StructArrayLayout3ui6();
        this.programConfigurations = new ProgramConfigurationSet(options.layers, options.zoom);
        this.segments = new SegmentVector();
        this.stateDependentLayerIds = this.layers.filter(l => l.isStateDependent()).map(l => l.id);
        this.enableTerrain = options.enableTerrain;
    }
    populate(features, options, canonical) {
        this.features = [];
        this.hasPattern = hasPattern('fill-extrusion', this.layers, options);
        this.featuresOnBorder = [];
        this.borders = [
            [],
            [],
            [],
            []
        ];
        this.borderDone = [
            false,
            false,
            false,
            false
        ];
        this.tileToMeter = tileToMeter(canonical);
        for (const {feature, id, index, sourceLayerIndex} of features) {
            const needGeometry = this.layers[0]._featureFilter.needGeometry;
            const evaluationFeature = toEvaluationFeature(feature, needGeometry);
            if (!this.layers[0]._featureFilter.filter(new EvaluationParameters(this.zoom), evaluationFeature, canonical))
                continue;
            const bucketFeature = {
                id,
                sourceLayerIndex,
                index,
                geometry: needGeometry ? evaluationFeature.geometry : loadGeometry(feature),
                properties: feature.properties,
                type: feature.type,
                patterns: {}
            };
            const vertexArrayOffset = this.layoutVertexArray.length;
            if (this.hasPattern) {
                this.features.push(addPatternDependencies('fill-extrusion', this.layers, bucketFeature, this.zoom, options));
            } else {
                this.addFeature(bucketFeature, bucketFeature.geometry, index, canonical, {});
            }
            options.featureIndex.insert(feature, bucketFeature.geometry, index, sourceLayerIndex, this.index, vertexArrayOffset);
        }
        this.sortBorders();
    }
    addFeatures(options, canonical, imagePositions) {
        for (const feature of this.features) {
            const {geometry} = feature;
            this.addFeature(feature, geometry, feature.index, canonical, imagePositions);
        }
        this.sortBorders();
    }
    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$2);
            this.indexBuffer = context.createIndexBuffer(this.indexArray);
        }
        this.programConfigurations.upload(context);
        this.uploaded = true;
    }
    uploadCentroid(context) {
        if (this.centroidVertexArray.length === 0)
            return;
        if (!this.centroidVertexBuffer) {
            this.centroidVertexBuffer = context.createVertexBuffer(this.centroidVertexArray, centroidAttributes.members, true);
        } else if (this.needsCentroidUpdate) {
            this.centroidVertexBuffer.updateData(this.centroidVertexArray);
        }
        this.needsCentroidUpdate = false;
    }
    destroy() {
        if (!this.layoutVertexBuffer)
            return;
        this.layoutVertexBuffer.destroy();
        if (this.centroidVertexBuffer)
            this.centroidVertexBuffer.destroy();
        this.indexBuffer.destroy();
        this.programConfigurations.destroy();
        this.segments.destroy();
    }
    addFeature(feature, geometry, index, canonical, imagePositions) {
        const flatRoof = this.enableTerrain && feature.properties && vectorTileFeatureTypes$2[feature.type] === 'Polygon';
        const metadata = flatRoof ? new PartMetadata() : null;
        for (const polygon of classifyRings$2(geometry, EARCUT_MAX_RINGS)) {
            let numVertices = 0;
            let segment = this.segments.prepareSegment(4, this.layoutVertexArray, this.indexArray);
            if (polygon.length === 0 || isEntirelyOutside(polygon[0])) {
                continue;
            }
            for (let i = 0; i < polygon.length; i++) {
                const ring = polygon[i];
                if (ring.length === 0) {
                    continue;
                }
                numVertices += ring.length;
                let edgeDistance = 0;
                if (metadata)
                    metadata.startRing(ring[0]);
                for (let p = 0; p < ring.length; p++) {
                    const p1 = ring[p];
                    if (p >= 1) {
                        const p2 = ring[p - 1];
                        if (!isBoundaryEdge(p1, p2)) {
                            if (metadata)
                                metadata.append(p1, p2);
                            if (segment.vertexLength + 4 > SegmentVector.MAX_VERTEX_ARRAY_LENGTH) {
                                segment = this.segments.prepareSegment(4, this.layoutVertexArray, this.indexArray);
                            }
                            const d = p1.sub(p2)._perp();
                            const nxRatio = d.x / (Math.abs(d.x) + Math.abs(d.y));
                            const nySign = d.y > 0 ? 1 : 0;
                            const dist = p2.dist(p1);
                            if (edgeDistance + dist > 32768)
                                edgeDistance = 0;
                            addVertex$1(this.layoutVertexArray, p1.x, p1.y, nxRatio, nySign, 0, 0, edgeDistance);
                            addVertex$1(this.layoutVertexArray, p1.x, p1.y, nxRatio, nySign, 0, 1, edgeDistance);
                            edgeDistance += dist;
                            addVertex$1(this.layoutVertexArray, p2.x, p2.y, nxRatio, nySign, 0, 0, edgeDistance);
                            addVertex$1(this.layoutVertexArray, p2.x, p2.y, nxRatio, nySign, 0, 1, edgeDistance);
                            const bottomRight = segment.vertexLength;
                            this.indexArray.emplaceBack(bottomRight, bottomRight + 2, bottomRight + 1);
                            this.indexArray.emplaceBack(bottomRight + 1, bottomRight + 2, bottomRight + 3);
                            segment.vertexLength += 4;
                            segment.primitiveLength += 2;
                        }
                    }
                }
            }
            if (segment.vertexLength + numVertices > SegmentVector.MAX_VERTEX_ARRAY_LENGTH) {
                segment = this.segments.prepareSegment(numVertices, this.layoutVertexArray, this.indexArray);
            }
            if (vectorTileFeatureTypes$2[feature.type] !== 'Polygon')
                continue;
            const flattened = [];
            const holeIndices = [];
            const triangleIndex = segment.vertexLength;
            for (let i = 0; i < polygon.length; i++) {
                const ring = polygon[i];
                if (ring.length === 0) {
                    continue;
                }
                if (ring !== polygon[0]) {
                    holeIndices.push(flattened.length / 2);
                }
                for (let i = 0; i < ring.length; i++) {
                    const p = ring[i];
                    addVertex$1(this.layoutVertexArray, p.x, p.y, 0, 0, 1, 1, 0);
                    flattened.push(p.x);
                    flattened.push(p.y);
                    if (metadata)
                        metadata.currentPolyCount.top++;
                }
            }
            const indices = earcut_1(flattened, holeIndices);
            for (let j = 0; j < indices.length; j += 3) {
                this.indexArray.emplaceBack(triangleIndex + indices[j], triangleIndex + indices[j + 2], triangleIndex + indices[j + 1]);
            }
            segment.primitiveLength += indices.length / 3;
            segment.vertexLength += numVertices;
        }
        if (metadata && metadata.polyCount.length > 0) {
            if (metadata.borders) {
                metadata.vertexArrayOffset = this.centroidVertexArray.length;
                const borders = metadata.borders;
                const index = this.featuresOnBorder.push(metadata) - 1;
                for (let i = 0; i < 4; i++) {
                    if (borders[i][0] !== Number.MAX_VALUE) {
                        this.borders[i].push(index);
                    }
                }
            }
            this.encodeCentroid(metadata.borders ? undefined : metadata.centroid(), metadata);
        }
        this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, imagePositions, canonical);
    }
    sortBorders() {
        for (let i = 0; i < 4; i++) {
            this.borders[i].sort((a, b) => this.featuresOnBorder[a].borders[i][0] - this.featuresOnBorder[b].borders[i][0]);
        }
    }
    encodeCentroid(c, metadata, append = true) {
        let x, y;
        if (c) {
            if (c.y !== 0) {
                const span = metadata.span()._mult(this.tileToMeter);
                x = (Math.max(c.x, 1) << 3) + Math.min(7, Math.round(span.x / 10));
                y = (Math.max(c.y, 1) << 3) + Math.min(7, Math.round(span.y / 10));
            } else {
                x = Math.ceil((c.x + ELEVATION_OFFSET) * ELEVATION_SCALE);
                y = 0;
            }
        } else {
            x = 0;
            y = +append;
        }
        let offset = append ? this.centroidVertexArray.length : metadata.vertexArrayOffset;
        for (const polyInfo of metadata.polyCount) {
            if (append) {
                this.centroidVertexArray.resize(this.centroidVertexArray.length + polyInfo.edges * 4 + polyInfo.top);
            }
            for (let i = 0; i < polyInfo.edges * 2; i++) {
                this.centroidVertexArray.emplace(offset++, 0, y);
                this.centroidVertexArray.emplace(offset++, x, y);
            }
            for (let i = 0; i < polyInfo.top; i++) {
                this.centroidVertexArray.emplace(offset++, x, y);
            }
        }
    }
}
register('FillExtrusionBucket', FillExtrusionBucket, {
    omit: [
        'layers',
        'features'
    ]
});
register('PartMetadata', PartMetadata);
function isBoundaryEdge(p1, p2) {
    return p1.x === p2.x && (p1.x < 0 || p1.x > EXTENT) || p1.y === p2.y && (p1.y < 0 || p1.y > EXTENT);
}
function isEntirelyOutside(ring) {
    return ring.every(p => p.x <= 0) || ring.every(p => p.x >= EXTENT) || ring.every(p => p.y <= 0) || ring.every(p => p.y >= EXTENT);
}
function tileToMeter(canonical) {
    const circumferenceAtEquator = 40075017;
    const mercatorY = canonical.y / (1 << canonical.z);
    const exp = Math.exp(Math.PI * (1 - 2 * mercatorY));
    return circumferenceAtEquator * 2 * exp / (exp * exp + 1) / EXTENT / (1 << canonical.z);
}

const paint$5 = new Properties({
    'fill-extrusion-opacity': new DataConstantProperty(spec['paint_fill-extrusion']['fill-extrusion-opacity']),
    'fill-extrusion-color': new DataDrivenProperty(spec['paint_fill-extrusion']['fill-extrusion-color']),
    'fill-extrusion-translate': new DataConstantProperty(spec['paint_fill-extrusion']['fill-extrusion-translate']),
    'fill-extrusion-translate-anchor': new DataConstantProperty(spec['paint_fill-extrusion']['fill-extrusion-translate-anchor']),
    'fill-extrusion-pattern': new CrossFadedDataDrivenProperty(spec['paint_fill-extrusion']['fill-extrusion-pattern']),
    'fill-extrusion-height': new DataDrivenProperty(spec['paint_fill-extrusion']['fill-extrusion-height']),
    'fill-extrusion-base': new DataDrivenProperty(spec['paint_fill-extrusion']['fill-extrusion-base']),
    'fill-extrusion-vertical-gradient': new DataConstantProperty(spec['paint_fill-extrusion']['fill-extrusion-vertical-gradient'])
});
var properties$5 = { paint: paint$5 };

class FillExtrusionStyleLayer extends StyleLayer {
    constructor(layer) {
        super(layer, properties$5);
    }
    createBucket(parameters) {
        return new FillExtrusionBucket(parameters);
    }
    queryRadius() {
        return translateDistance(this.paint.get('fill-extrusion-translate'));
    }
    is3D() {
        return true;
    }
    getProgramIds() {
        const patternProperty = this.paint.get('fill-extrusion-pattern');
        const image = patternProperty.constantOr(1);
        return [image ? 'fillExtrusionPattern' : 'fillExtrusion'];
    }
    getProgramConfiguration(zoom) {
        return new ProgramConfiguration(this, zoom);
    }
    queryIntersectsFeature(queryGeometry, feature, featureState, geometry, zoom, transform, pixelPosMatrix, elevationHelper, layoutVertexArrayOffset) {
        const translation = tilespaceTranslate(this.paint.get('fill-extrusion-translate'), this.paint.get('fill-extrusion-translate-anchor'), transform.angle, queryGeometry.pixelToTileUnitsFactor);
        const height = this.paint.get('fill-extrusion-height').evaluate(feature, featureState);
        const base = this.paint.get('fill-extrusion-base').evaluate(feature, featureState);
        const centroid = [
            0,
            0
        ];
        const terrainVisible = elevationHelper && transform.elevation;
        const exaggeration = transform.elevation ? transform.elevation.exaggeration() : 1;
        if (terrainVisible) {
            const centroidVertexArray = queryGeometry.tile.getBucket(this).centroidVertexArray;
            const centroidOffset = layoutVertexArrayOffset + 1;
            if (centroidOffset < centroidVertexArray.length) {
                const centroidVertexObject = centroidVertexArray.get(centroidOffset);
                centroid[0] = centroidVertexObject.a_centroid_pos0;
                centroid[1] = centroidVertexObject.a_centroid_pos1;
            }
        }
        const isHidden = centroid[0] === 0 && centroid[1] === 1;
        if (isHidden)
            return false;
        const demSampler = terrainVisible ? elevationHelper : null;
        const projected = projectExtrusion(geometry, base, height, translation, pixelPosMatrix, demSampler, centroid, exaggeration, transform.center.lat);
        const projectedBase = projected[0];
        const projectedTop = projected[1];
        const screenQuery = queryGeometry.queryGeometry;
        const projectedQueryGeometry = screenQuery.isPointQuery() ? screenQuery.screenBounds : screenQuery.screenGeometry;
        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 baseB = ringBase[p + 1];
            const face = [
                topA,
                topB,
                baseB,
                baseA,
                topA
            ];
            if (polygonIntersectsPolygon(projectedQueryGeometry, face)) {
                closestDistance = Math.min(closestDistance, getIntersectionDistance(projectedQueryGeometry, face));
            }
        }
    }
    return closestDistance === Infinity ? false : closestDistance;
}
function projectExtrusion(geometry, zBase, zTop, translation, m, demSampler, centroid, exaggeration, lat) {
    if (demSampler) {
        return projectExtrusion3D(geometry, zBase, zTop, translation, m, demSampler, centroid, exaggeration, lat);
    } else {
        return projectExtrusion2D(geometry, zBase, zTop, translation, m);
    }
}
function projectExtrusion2D(geometry, zBase, zTop, translation, 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 + translation.x;
            const y = p.y + translation.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 = Math.max(sW + baseWZ, 0.00001);
            const topX = sX + topXZ;
            const topY = sY + topYZ;
            const topZ = sZ + topZZ;
            const topW = Math.max(sW + topWZ, 0.00001);
            const b = new pointGeometry(baseX / baseW, baseY / baseW);
            b.z = baseZ / baseW;
            ringBase.push(b);
            const t = new pointGeometry(topX / topW, topY / topW);
            t.z = topZ / topW;
            ringTop.push(t);
        }
        projectedBase.push(ringBase);
        projectedTop.push(ringTop);
    }
    return [
        projectedBase,
        projectedTop
    ];
}
function projectExtrusion3D(geometry, zBase, zTop, translation, m, demSampler, centroid, exaggeration, lat) {
    const projectedBase = [];
    const projectedTop = [];
    const v = [
        0,
        0,
        0,
        1
    ];
    for (const r of geometry) {
        const ringBase = [];
        const ringTop = [];
        for (const p of r) {
            const x = p.x + translation.x;
            const y = p.y + translation.y;
            const heightOffset = getTerrainHeightOffset(x, y, zBase, zTop, demSampler, centroid, exaggeration, lat);
            v[0] = x;
            v[1] = y;
            v[2] = heightOffset.base;
            v[3] = 1;
            transformMat4(v, v, m);
            v[3] = Math.max(v[3], 0.00001);
            const base = toPoint([
                v[0] / v[3],
                v[1] / v[3],
                v[2] / v[3]
            ]);
            v[0] = x;
            v[1] = y;
            v[2] = heightOffset.top;
            v[3] = 1;
            transformMat4(v, v, m);
            v[3] = Math.max(v[3], 0.00001);
            const top = toPoint([
                v[0] / v[3],
                v[1] / v[3],
                v[2] / v[3]
            ]);
            ringBase.push(base);
            ringTop.push(top);
        }
        projectedBase.push(ringBase);
        projectedTop.push(ringTop);
    }
    return [
        projectedBase,
        projectedTop
    ];
}
function toPoint(v) {
    const p = new pointGeometry(v[0], v[1]);
    p.z = v[2];
    return p;
}
function getTerrainHeightOffset(x, y, zBase, zTop, demSampler, centroid, exaggeration, lat) {
    const ele = exaggeration * demSampler.getElevationAt(x, y, true, true);
    const flatRoof = centroid[0] !== 0;
    const centroidElevation = flatRoof ? centroid[1] === 0 ? exaggeration * elevationFromUint16(centroid[0]) : exaggeration * flatElevation(demSampler, centroid, lat) : ele;
    return {
        base: ele + (zBase === 0) ? -1 : zBase,
        top: flatRoof ? Math.max(centroidElevation + zTop, ele + zBase + 2) : ele + zTop
    };
}
function elevationFromUint16(n) {
    return n / ELEVATION_SCALE - ELEVATION_OFFSET;
}
function flatElevation(demSampler, centroid, lat) {
    const posX = Math.floor(centroid[0] / 8);
    const posY = Math.floor(centroid[1] / 8);
    const spanX = 10 * (centroid[0] - posX * 8);
    const spanY = 10 * (centroid[1] - posY * 8);
    const z = demSampler.getElevationAt(posX, posY, true, true);
    const meterToDEM = demSampler.getMeterToDEM(lat);
    const wX = Math.floor(0.5 * (spanX * meterToDEM - 1));
    const wY = Math.floor(0.5 * (spanY * meterToDEM - 1));
    const posPx = demSampler.tileCoordToPixel(posX, posY);
    const offsetX = 2 * wX + 1;
    const offsetY = 2 * wY + 1;
    const corners = fourSample(demSampler, posPx.x - wX, posPx.y - wY, offsetX, offsetY);
    const diffX = Math.abs(corners[0] - corners[1]);
    const diffY = Math.abs(corners[2] - corners[3]);
    const diffZ = Math.abs(corners[0] - corners[2]);
    const diffW = Math.abs(corners[1] - corners[3]);
    const diffSumX = diffX + diffY;
    const diffSumY = diffZ + diffW;
    const slopeX = Math.min(0.25, meterToDEM * 0.5 * diffSumX / offsetX);
    const slopeY = Math.min(0.25, meterToDEM * 0.5 * diffSumY / offsetY);
    return z + Math.max(slopeX * spanX, slopeY * spanY);
}
function fourSample(demSampler, posX, posY, offsetX, offsetY) {
    return [
        demSampler.getElevationAtPixel(posX, posY, true),
        demSampler.getElevationAtPixel(posX + offsetY, posY, true),
        demSampler.getElevationAtPixel(posX, posY + offsetY, true),
        demSampler.getElevationAtPixel(posX + offsetX, posY + offsetY, true)
    ];
}

const lineLayoutAttributes = createLayout([
    {
        name: 'a_pos_normal',
        components: 2,
        type: 'Int16'
    },
    {
        name: 'a_data',
        components: 4,
        type: 'Uint8'
    },
    {
        name: 'a_linesofar',
        components: 1,
        type: 'Float32'
    }
], 4);
const {members: members$1, size: size$1, alignment: alignment$1} = lineLayoutAttributes;

const lineLayoutAttributesExt = createLayout([
    {
        name: 'a_uv_x',
        components: 1,
        type: 'Float32'
    },
    {
        name: 'a_split_index',
        components: 1,
        type: 'Float32'
    }
]);
const {members, size, alignment} = lineLayoutAttributesExt;

var vectortilefeature = VectorTileFeature$1;
function VectorTileFeature$1(pbf, end, extent, keys, values) {
    this.properties = {};
    this.extent = extent;
    this.type = 0;
    this._pbf = pbf;
    this._geometry = -1;
    this._keys = keys;
    this._values = values;
    pbf.readFields(readFeature, this, end);
}
function readFeature(tag, feature, pbf) {
    if (tag == 1)
        feature.id = pbf.readVarint();
    else if (tag == 2)
        readTag(pbf, feature);
    else if (tag == 3)
        feature.type = pbf.readVarint();
    else if (tag == 4)
        feature._geometry = pbf.pos;
}
function readTag(pbf, feature) {
    var end = pbf.readVarint() + pbf.pos;
    while (pbf.pos < end) {
        var key = feature._keys[pbf.readVarint()], value = feature._values[pbf.readVarint()];
        feature.properties[key] = value;
    }
}
VectorTileFeature$1.types = [
    'Unknown',
    'Point',
    'LineString',
    'Polygon'
];
VectorTileFeature$1.prototype.loadGeometry = function () {
    var pbf = this._pbf;
    pbf.pos = this._geometry;
    var end = pbf.readVarint() + pbf.pos, cmd = 1, length = 0, x = 0, y = 0, lines = [], line;
    while (pbf.pos < end) {
        if (length <= 0) {
            var cmdLen = pbf.readVarint();
            cmd = cmdLen & 7;
            length = cmdLen >> 3;
        }
        length--;
        if (cmd === 1 || cmd === 2) {
            x += pbf.readSVarint();
            y += pbf.readSVarint();
            if (cmd === 1) {
                if (line)
                    lines.push(line);
                line = [];
            }
            line.push(new pointGeometry(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;
};
VectorTileFeature$1.prototype.bbox = function () {
    var pbf = this._pbf;
    pbf.pos = this._geometry;
    var end = pbf.readVarint() + pbf.pos, cmd = 1, length = 0, x = 0, y = 0, x1 = Infinity, x2 = -Infinity, y1 = Infinity, y2 = -Infinity;
    while (pbf.pos < end) {
        if (length <= 0) {
            var cmdLen = pbf.readVarint();
            cmd = cmdLen & 7;
            length = cmdLen >> 3;
        }
        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
    ];
};
VectorTileFeature$1.prototype.toGeoJSON = function (x, y, z) {
    var size = this.extent * Math.pow(2, z), x0 = this.extent * x, y0 = this.extent * y, coords = this.loadGeometry(), type = VectorTileFeature$1.types[this.type], i, j;
    function project(line) {
        for (var j = 0; j < line.length; j++) {
            var p = line[j];
            line[j] = [
                (p.x + x0) * 360 / size - 180,
                90 - (p.y + y0) * 360 / size
            ];
        }
    }
    switch (this.type) {
    case 1:
        var points = [];
        for (i = 0; i < coords.length; i++) {
            points[i] = coords[i][0];
        }
        coords = points;
        project(coords);
        break;
    case 2:
        for (i = 0; i < coords.length; i++) {
            project(coords[i]);
        }
        break;
    case 3:
        coords = classifyRings(coords);
        for (i = 0; i < coords.length; i++) {
            for (j = 0; j < coords[i].length; j++) {
                project(coords[i][j]);
            }
        }
        break;
    }
    if (coords.length === 1) {
        coords = coords[0];
    } else {
        type = 'Multi' + type;
    }
    var result = {
        type: 'Feature',
        geometry: {
            type: type,
            coordinates: coords
        },
        properties: this.properties
    };
    if ('id' in this) {
        result.id = this.id;
    }
    return result;
};
function classifyRings(rings) {
    var len = rings.length;
    if (len <= 1)
        return [rings];
    var polygons = [], polygon, ccw;
    for (var i = 0; i < len; i++) {
        var area = signedArea(rings[i]);
        if (area === 0)
            continue;
        if (ccw === undefined)
            ccw = area < 0;
        if (ccw === area < 0) {
            if (polygon)
                polygons.push(polygon);
            polygon = [rings[i]];
        } else {
            polygon.push(rings[i]);
        }
    }
    if (polygon)
        polygons.push(polygon);
    return polygons;
}
function signedArea(ring) {
    var sum = 0;
    for (var 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 = VectorTileLayer$1;
function VectorTileLayer$1(pbf, end) {
    this.version = 1;
    this.name = null;
    this.extent = 4096;
    this.length = 0;
    this._pbf = pbf;
    this._keys = [];
    this._values = [];
    this._features = [];
    pbf.readFields(readLayer, this, end);
    this.length = this._features.length;
}
function readLayer(tag, layer, pbf) {
    if (tag === 15)
        layer.version = pbf.readVarint();
    else if (tag === 1)
        layer.name = pbf.readString();
    else if (tag === 5)
        layer.extent = pbf.readVarint();
    else if (tag === 2)
        layer._features.push(pbf.pos);
    else if (tag === 3)
        layer._keys.push(pbf.readString());
    else if (tag === 4)
        layer._values.push(readValueMessage(pbf));
}
function readValueMessage(pbf) {
    var value = null, end = pbf.readVarint() + pbf.pos;
    while (pbf.pos < end) {
        var tag = pbf.readVarint() >> 3;
        value = tag === 1 ? pbf.readString() : tag === 2 ? pbf.readFloat() : tag === 3 ? pbf.readDouble() : tag === 4 ? pbf.readVarint64() : tag === 5 ? pbf.readVarint() : tag === 6 ? pbf.readSVarint() : tag === 7 ? pbf.readBoolean() : null;
    }
    return value;
}
VectorTileLayer$1.prototype.feature = function (i) {
    if (i < 0 || i >= this._features.length)
        throw new Error('feature index out of bounds');
    this._pbf.pos = this._features[i];
    var end = this._pbf.readVarint() + this._pbf.pos;
    return new vectortilefeature(this._pbf, end, this.extent, this._keys, this._values);
};

var vectortile = VectorTile$1;
function VectorTile$1(pbf, end) {
    this.layers = pbf.readFields(readTile, {}, end);
}
function readTile(tag, layers, pbf) {
    if (tag === 3) {
        var layer = new vectortilelayer(pbf, pbf.readVarint() + pbf.pos);
        if (layer.length)
            layers[layer.name] = layer;
    }
}

var VectorTile = vectortile;
var VectorTileFeature = vectortilefeature;
var VectorTileLayer = vectortilelayer;

var vectorTile = {
	VectorTile: VectorTile,
	VectorTileFeature: VectorTileFeature,
	VectorTileLayer: VectorTileLayer
};

const vectorTileFeatureTypes$1 = vectorTile.VectorTileFeature.types;
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;
class LineBucket {
    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.hasPattern = false;
        this.patternFeatures = [];
        this.lineClipsArray = [];
        this.gradients = {};
        this.layers.forEach(layer => {
            this.gradients[layer.id] = {};
        });
        this.layoutVertexArray = new StructArrayLayout2i4ub1f12();
        this.layoutVertexArray2 = new StructArrayLayout2f8();
        this.indexArray = new StructArrayLayout3ui6();
        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.hasPattern = hasPattern('line', this.layers, options);
        const lineSortKey = this.layers[0].layout.get('line-sort-key');
        const bucketFeatures = [];
        for (const {feature, id, index, sourceLayerIndex} of features) {
            const needGeometry = this.layers[0]._featureFilter.needGeometry;
            const evaluationFeature = toEvaluationFeature(feature, needGeometry);
            if (!this.layers[0]._featureFilter.filter(new EvaluationParameters(this.zoom), evaluationFeature, canonical))
                continue;
            const sortKey = lineSortKey ? lineSortKey.evaluate(evaluationFeature, {}, canonical) : undefined;
            const bucketFeature = {
                id,
                properties: feature.properties,
                type: feature.type,
                sourceLayerIndex,
                index,
                geometry: needGeometry ? evaluationFeature.geometry : loadGeometry(feature),
                patterns: {},
                sortKey
            };
            bucketFeatures.push(bucketFeature);
        }
        if (lineSortKey) {
            bucketFeatures.sort((a, b) => {
                return a.sortKey - b.sortKey;
            });
        }
        const {lineAtlas, featureIndex} = options;
        const hasFeatureDashes = this.addConstantDashes(lineAtlas);
        for (const bucketFeature of bucketFeatures) {
            const {geometry, index, sourceLayerIndex} = bucketFeature;
            if (hasFeatureDashes) {
                this.addFeatureDashes(bucketFeature, lineAtlas);
            }
            if (this.hasPattern) {
                const patternBucketFeature = addPatternDependencies('line', this.layers, bucketFeature, this.zoom, options);
                this.patternFeatures.push(patternBucketFeature);
            } else {
                this.addFeature(bucketFeature, geometry, index, canonical, lineAtlas.positions);
            }
            const feature = features[index].feature;
            featureIndex.insert(feature, geometry, index, sourceLayerIndex, this.index);
        }
    }
    addConstantDashes(lineAtlas) {
        let hasFeatureDashes = false;
        for (const layer of this.layers) {
            const dashPropertyValue = layer.paint.get('line-dasharray').value;
            const capPropertyValue = layer.layout.get('line-cap').value;
            if (dashPropertyValue.kind !== 'constant' || capPropertyValue.kind !== 'constant') {
                hasFeatureDashes = true;
            } else {
                const constCap = capPropertyValue.value;
                const constDash = dashPropertyValue.value;
                if (!constDash)
                    continue;
                lineAtlas.addDash(constDash.from, constCap);
                lineAtlas.addDash(constDash.to, constCap);
                if (constDash.other)
                    lineAtlas.addDash(constDash.other, constCap);
            }
        }
        return hasFeatureDashes;
    }
    addFeatureDashes(feature, lineAtlas) {
        const zoom = this.zoom;
        for (const layer of this.layers) {
            const dashPropertyValue = layer.paint.get('line-dasharray').value;
            const capPropertyValue = layer.layout.get('line-cap').value;
            if (dashPropertyValue.kind === 'constant' && capPropertyValue.kind === 'constant')
                continue;
            let minDashArray, midDashArray, maxDashArray, minCap, midCap, maxCap;
            if (dashPropertyValue.kind === 'constant') {
                const constDash = dashPropertyValue.value;
                if (!constDash)
                    continue;
                minDashArray = constDash.other || constDash.to;
                midDashArray = constDash.to;
                maxDashArray = constDash.from;
            } else {
                minDashArray = dashPropertyValue.evaluate({ zoom: zoom - 1 }, feature);
                midDashArray = dashPropertyValue.evaluate({ zoom }, feature);
                maxDashArray = dashPropertyValue.evaluate({ zoom: zoom + 1 }, feature);
            }
            if (capPropertyValue.kind === 'constant') {
                minCap = midCap = maxCap = capPropertyValue.value;
            } else {
                minCap = capPropertyValue.evaluate({ zoom: zoom - 1 }, feature);
                midCap = capPropertyValue.evaluate({ zoom }, feature);
                maxCap = capPropertyValue.evaluate({ zoom: zoom + 1 }, feature);
            }
            lineAtlas.addDash(minDashArray, minCap);
            lineAtlas.addDash(midDashArray, midCap);
            lineAtlas.addDash(maxDashArray, maxCap);
            const min = lineAtlas.getKey(minDashArray, minCap);
            const mid = lineAtlas.getKey(midDashArray, midCap);
            const max = lineAtlas.getKey(maxDashArray, maxCap);
            feature.patterns[layer.id] = {
                min,
                mid,
                max
            };
        }
    }
    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);
        }
    }
    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 && feature.properties.hasOwnProperty('mapbox_clip_start') && feature.properties.hasOwnProperty('mapbox_clip_end')) {
            const start = +feature.properties['mapbox_clip_start'];
            const end = +feature.properties['mapbox_clip_end'];
            return {
                start,
                end
            };
        }
    }
    addFeature(feature, geometry, index, canonical, imagePositions) {
        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');
        const roundLimit = layout.get('line-round-limit');
        this.lineClips = this.lineFeatureClips(feature);
        for (const line of geometry) {
            this.addLine(line, feature, join, cap, miterLimit, roundLimit);
        }
        this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, imagePositions, canonical);
    }
    addLine(vertices, feature, join, cap, miterLimit, roundLimit) {
        this.distance = 0;
        this.scaledDistance = 0;
        this.totalDistance = 0;
        this.lineSoFar = 0;
        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 = vectorTileFeatureTypes$1[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 < (isPolygon ? 3 : 2))
            return;
        if (join === 'bevel')
            miterLimit = 1.05;
        const sharpCornerOffset = this.overscaling <= 16 ? SHARP_CORNER_OFFSET * EXTENT / (512 * this.overscaling) : 0;
        const segment = this.segments.prepareSegment(len * 10, this.layoutVertexArray, this.indexArray);
        let currentVertex;
        let prevVertex = undefined;
        let nextVertex = undefined;
        let prevNormal = undefined;
        let nextNormal = undefined;
        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] : undefined : 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 = 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 !== 0.5) {
                            const t2 = t - 0.5;
                            const A = 1.0904 + cosAngle * (-3.2452 + cosAngle * (3.55645 - cosAngle * 1.43519));
                            const B = 0.848013 + cosAngle * (-1.06021 + cosAngle * 0.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;
                if (!prevVertex) {
                    this.addCurrentVertex(currentVertex, joinNormal, offset, offset, segment);
                }
                this.addCurrentVertex(currentVertex, joinNormal, 0, 0, segment);
                if (prevVertex) {
                    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;
                }
            }
        }
    }
    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);
    }
    addHalfVertex({x, y}, extrudeX, extrudeY, round, up, dir, segment) {
        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, 0, this.lineSoFar);
        if (this.lineClips) {
            this.layoutVertexArray2.emplaceBack(this.scaledDistance, this.lineClipsArray.length);
        }
        const e = segment.vertexLength++;
        if (this.e1 >= 0 && this.e2 >= 0) {
            this.indexArray.emplaceBack(this.e1, this.e2, e);
            segment.primitiveLength++;
        }
        if (up) {
            this.e2 = e;
        } else {
            this.e1 = e;
        }
    }
    updateScaledDistance() {
        if (this.lineClips) {
            const featureShare = this.lineClips.end - this.lineClips.start;
            const totalFeatureLength = this.totalDistance / featureShare;
            this.scaledDistance = this.distance / this.totalDistance;
            this.lineSoFar = totalFeatureLength * this.lineClips.start + this.distance;
        } else {
            this.lineSoFar = this.distance;
        }
    }
    updateDistance(prev, next) {
        this.distance += prev.dist(next);
        this.updateScaledDistance();
    }
}
register('LineBucket', LineBucket, {
    omit: [
        'layers',
        'patternFeatures'
    ]
});

const layout$1 = new Properties({
    'line-cap': new DataDrivenProperty(spec['layout_line']['line-cap']),
    'line-join': new DataDrivenProperty(spec['layout_line']['line-join']),
    'line-miter-limit': new DataConstantProperty(spec['layout_line']['line-miter-limit']),
    'line-round-limit': new DataConstantProperty(spec['layout_line']['line-round-limit']),
    'line-sort-key': new DataDrivenProperty(spec['layout_line']['line-sort-key'])
});
const paint$4 = new Properties({
    'line-opacity': new DataDrivenProperty(spec['paint_line']['line-opacity']),
    'line-color': new DataDrivenProperty(spec['paint_line']['line-color']),
    'line-translate': new DataConstantProperty(spec['paint_line']['line-translate']),
    'line-translate-anchor': new DataConstantProperty(spec['paint_line']['line-translate-anchor']),
    'line-width': new DataDrivenProperty(spec['paint_line']['line-width']),
    'line-gap-width': new DataDrivenProperty(spec['paint_line']['line-gap-width']),
    'line-offset': new DataDrivenProperty(spec['paint_line']['line-offset']),
    'line-blur': new DataDrivenProperty(spec['paint_line']['line-blur']),
    'line-dasharray': new CrossFadedDataDrivenProperty(spec['paint_line']['line-dasharray']),
    'line-pattern': new CrossFadedDataDrivenProperty(spec['paint_line']['line-pattern']),
    'line-gradient': new ColorRampProperty(spec['paint_line']['line-gradient'])
});
var properties$4 = {
    paint: paint$4,
    layout: layout$1
};

class LineFloorwidthProperty 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$1({}, globals, { zoom: Math.floor(globals.zoom) });
        return super.evaluate(value, globals, feature, featureState);
    }
}
const lineFloorwidthProperty = new LineFloorwidthProperty(properties$4.paint.properties['line-width'].specification);
lineFloorwidthProperty.useIntegerZoom = true;
class LineStyleLayer extends StyleLayer {
    constructor(layer) {
        super(layer, properties$4);
        this.gradientVersion = 0;
    }
    _handleSpecialPaintPropertyUpdate(name) {
        if (name === 'line-gradient') {
            const expression = this._transitionablePaint._values['line-gradient'].value.expression;
            this.stepInterpolant = expression._styleExpression && expression._styleExpression.expression instanceof Step$1;
            this.gradientVersion = (this.gradientVersion + 1) % 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);
    }
    getProgramIds() {
        const dasharray = this.paint.get('line-dasharray');
        const patternProperty = this.paint.get('line-pattern');
        const image = patternProperty.constantOr(1);
        const gradient = this.paint.get('line-gradient');
        const programId = image ? 'linePattern' : dasharray ? 'lineSDF' : gradient ? 'lineGradient' : 'line';
        return [programId];
    }
    getProgramConfiguration(zoom) {
        return new ProgramConfiguration(this, zoom);
    }
    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, zoom, transform) {
        if (queryGeometry.queryGeometry.isAboveHorizon)
            return false;
        const translatedPolygon = translate$1(queryGeometry.tilespaceGeometry, this.paint.get('line-translate'), this.paint.get('line-translate-anchor'), transform.angle, queryGeometry.pixelToTileUnitsFactor);
        const halfWidth = queryGeometry.pixelToTileUnitsFactor / 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 * queryGeometry.pixelToTileUnitsFactor);
        }
        return polygonIntersectsBufferedMultiLine(translatedPolygon, geometry, halfWidth);
    }
    isTileClipped() {
        return true;
    }
}
function getLineWidth(lineWidth, lineGapWidth) {
    if (lineGapWidth > 0) {
        return lineGapWidth + 2 * lineWidth;
    } else {
        return lineWidth;
    }
}
function offsetLine(rings, offset) {
    const newRings = [];
    const zero = new pointGeometry(0, 0);
    for (let k = 0; k < rings.length; k++) {
        const ring = rings[k];
        const newRing = [];
        for (let i = 0; i < ring.length; i++) {
            const a = ring[i - 1];
            const b = ring[i];
            const c = ring[i + 1];
            const aToB = i === 0 ? zero : b.sub(a)._unit()._perp();
            const bToC = i === ring.length - 1 ? zero : c.sub(b)._unit()._perp();
            const extrude = aToB._add(bToC)._unit();
            const cosHalfAngle = extrude.x * bToC.x + extrude.y * bToC.y;
            extrude._mult(1 / cosHalfAngle);
            newRing.push(extrude._mult(offset)._add(b));
        }
        newRings.push(newRing);
    }
    return newRings;
}

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'
    }
]);
const collisionVertexAttributesExt = createLayout([
    {
        name: 'a_size_scale',
        components: 1,
        type: 'Float32'
    },
    {
        name: 'a_padding',
        components: 2,
        type: 'Float32'
    }
]);
createLayout([
    {
        type: 'Int16',
        name: 'anchorPointX'
    },
    {
        type: 'Int16',
        name: 'anchorPointY'
    },
    {
        type: 'Float32',
        name: 'x1'
    },
    {
        type: 'Float32',
        name: 'y1'
    },
    {
        type: 'Float32',
        name: 'x2'
    },
    {
        type: 'Float32',
        name: 'y2'
    },
    {
        type: 'Int16',
        name: 'padding'
    },
    {
        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_2f',
        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',
        components: 2,
        name: 'textOffset'
    },
    {
        type: 'Float32',
        name: 'collisionCircleDiameter'
    }
]);
createLayout([{
        type: 'Float32',
        name: 'offsetX'
    }]);
createLayout([
    {
        type: 'Int16',
        name: 'x'
    },
    {
        type: 'Int16',
        name: 'y'
    },
    {
        type: 'Int16',
        name: 'tileUnitDistanceFromAnchor'
    }
]);

var ONE_EM = 24;

const SIZE_PACK_FACTOR = 128;
function getSizeData(tileZoom, value) {
    const {expression} = value;
    if (expression.kind === 'constant') {
        const layoutSize = expression.evaluate(new EvaluationParameters(tileZoom + 1));
        return {
            kind: 'constant',
            layoutSize
        };
    } 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
            };
        }
        const minSize = expression.evaluate(new EvaluationParameters(minZoom));
        const maxSize = expression.evaluate(new EvaluationParameters(maxZoom));
        return {
            kind: 'camera',
            minZoom,
            maxZoom,
            minSize,
            maxSize,
            interpolationType
        };
    }
}
function evaluateSizeForFeature(sizeData, {uSize, uSizeT}, {lowerSize, upperSize}) {
    if (sizeData.kind === 'source') {
        return lowerSize / SIZE_PACK_FACTOR;
    } else if (sizeData.kind === 'composite') {
        return number(lowerSize / SIZE_PACK_FACTOR, upperSize / SIZE_PACK_FACTOR, 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(Interpolate$1.interpolationFactor(interpolationType, zoom, minZoom, maxZoom), 0, 1);
        if (sizeData.kind === 'camera') {
            uSize = number(sizeData.minSize, sizeData.maxSize, t);
        } else {
            uSizeT = t;
        }
    }
    return {
        uSizeT,
        uSize
    };
}

var symbolSize = /*#__PURE__*/Object.freeze({
__proto__: null,
getSizeData: getSizeData,
evaluateSizeForFeature: evaluateSizeForFeature,
evaluateSizeForZoom: evaluateSizeForZoom,
SIZE_PACK_FACTOR: SIZE_PACK_FACTOR
});

function transformText(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 (plugin.applyArabicShaping) {
        text = plugin.applyArabicShaping(text);
    }
    return text;
}
function transformText$1 (text, layer, feature) {
    text.sections.forEach(section => {
        section.text = transformText(section.text, layer, feature);
    });
    return text;
}

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);
}

const verticalizedCharacterMap = {
    '!': '\uFE15',
    '#': '\uFF03',
    '$': '\uFF04',
    '%': '\uFF05',
    '&': '\uFF06',
    '(': '\uFE35',
    ')': '\uFE36',
    '*': '\uFF0A',
    '+': '\uFF0B',
    ',': '\uFE10',
    '-': '\uFE32',
    '.': '\u30FB',
    '/': '\uFF0F',
    ':': '\uFE13',
    ';': '\uFE14',
    '<': '\uFE3F',
    '=': '\uFF1D',
    '>': '\uFE40',
    '?': '\uFE16',
    '@': '\uFF20',
    '[': '\uFE47',
    '\\': '\uFF3C',
    ']': '\uFE48',
    '^': '\uFF3E',
    '_': '︳',
    '`': '\uFF40',
    '{': '\uFE37',
    '|': '\u2015',
    '}': '\uFE38',
    '~': '\uFF5E',
    '\xA2': '\uFFE0',
    '\xA3': '\uFFE1',
    '\xA5': '\uFFE5',
    '\xA6': '\uFFE4',
    '\xAC': '\uFFE2',
    '\xAF': '\uFFE3',
    '\u2013': '\uFE32',
    '\u2014': '\uFE31',
    '\u2018': '\uFE43',
    '\u2019': '\uFE44',
    '\u201C': '\uFE41',
    '\u201D': '\uFE42',
    '\u2026': '\uFE19',
    '\u2027': '\u30FB',
    '\u20A9': '\uFFE6',
    '\u3001': '\uFE11',
    '\u3002': '\uFE12',
    '\u3008': '\uFE3F',
    '\u3009': '\uFE40',
    '\u300A': '\uFE3D',
    '\u300B': '\uFE3E',
    '\u300C': '\uFE41',
    '\u300D': '\uFE42',
    '\u300E': '\uFE43',
    '\u300F': '\uFE44',
    '\u3010': '\uFE3B',
    '\u3011': '\uFE3C',
    '\u3014': '\uFE39',
    '\u3015': '\uFE3A',
    '\u3016': '\uFE17',
    '\u3017': '\uFE18',
    '\uFF01': '\uFE15',
    '\uFF08': '\uFE35',
    '\uFF09': '\uFE36',
    '\uFF0C': '\uFE10',
    '\uFF0D': '\uFE32',
    '\uFF0E': '\u30FB',
    '\uFF1A': '\uFE13',
    '\uFF1B': '\uFE14',
    '\uFF1C': '\uFE3F',
    '\uFF1E': '\uFE40',
    '\uFF1F': '\uFE16',
    '\uFF3B': '\uFE47',
    '\uFF3D': '\uFE48',
    '＿': '︳',
    '\uFF5B': '\uFE37',
    '\uFF5C': '\u2015',
    '\uFF5D': '\uFE38',
    '\uFF5F': '\uFE35',
    '\uFF60': '\uFE36',
    '\uFF61': '\uFE12',
    '\uFF62': '\uFE41',
    '\uFF63': '\uFE42'
};
function verticalizePunctuation(input) {
    let output = '';
    for (let i = 0; i < input.length; i++) {
        const nextCharCode = input.charCodeAt(i + 1) || null;
        const prevCharCode = input.charCodeAt(i - 1) || null;
        const canReplacePunctuation = (!nextCharCode || !charHasRotatedVerticalOrientation(nextCharCode) || verticalizedCharacterMap[input[i + 1]]) && (!prevCharCode || !charHasRotatedVerticalOrientation(prevCharCode) || verticalizedCharacterMap[input[i - 1]]);
        if (canReplacePunctuation && verticalizedCharacterMap[input[i]]) {
            output += verticalizedCharacterMap[input[i]];
        } else {
            output += input[i];
        }
    }
    return output;
}

var read = function (buffer, offset, isLE, mLen, nBytes) {
    var e, m;
    var eLen = nBytes * 8 - mLen - 1;
    var eMax = (1 << eLen) - 1;
    var eBias = eMax >> 1;
    var nBits = -7;
    var i = isLE ? nBytes - 1 : 0;
    var d = isLE ? -1 : 1;
    var s = buffer[offset + i];
    i += d;
    e = s & (1 << -nBits) - 1;
    s >>= -nBits;
    nBits += eLen;
    for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {
    }
    m = e & (1 << -nBits) - 1;
    e >>= -nBits;
    nBits += mLen;
    for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {
    }
    if (e === 0) {
        e = 1 - eBias;
    } else if (e === eMax) {
        return m ? NaN : (s ? -1 : 1) * Infinity;
    } else {
        m = m + Math.pow(2, mLen);
        e = e - eBias;
    }
    return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
};
var write = function (buffer, value, offset, isLE, mLen, nBytes) {
    var e, m, c;
    var eLen = nBytes * 8 - mLen - 1;
    var eMax = (1 << eLen) - 1;
    var eBias = eMax >> 1;
    var rt = mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0;
    var i = isLE ? 0 : nBytes - 1;
    var d = isLE ? 1 : -1;
    var s = value < 0 || value === 0 && 1 / value < 0 ? 1 : 0;
    value = Math.abs(value);
    if (isNaN(value) || value === Infinity) {
        m = isNaN(value) ? 1 : 0;
        e = eMax;
    } else {
        e = Math.floor(Math.log(value) / Math.LN2);
        if (value * (c = Math.pow(2, -e)) < 1) {
            e--;
            c *= 2;
        }
        if (e + eBias >= 1) {
            value += rt / c;
        } else {
            value += rt * Math.pow(2, 1 - eBias);
        }
        if (value * c >= 2) {
            e++;
            c /= 2;
        }
        if (e + eBias >= eMax) {
            m = 0;
            e = eMax;
        } else if (e + eBias >= 1) {
            m = (value * c - 1) * Math.pow(2, mLen);
            e = e + eBias;
        } else {
            m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
            e = 0;
        }
    }
    for (; mLen >= 8; buffer[offset + i] = m & 255, i += d, m /= 256, mLen -= 8) {
    }
    e = e << mLen | m;
    eLen += mLen;
    for (; eLen > 0; buffer[offset + i] = e & 255, i += d, e /= 256, eLen -= 8) {
    }
    buffer[offset + i - d] |= s * 128;
};

var ieee754 = {
	read: read,
	write: write
};

var pbf = Pbf;

function Pbf(buf) {
    this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0);
    this.pos = 0;
    this.type = 0;
    this.length = this.buf.length;
}
Pbf.Varint = 0;
Pbf.Fixed64 = 1;
Pbf.Bytes = 2;
Pbf.Fixed32 = 5;
var SHIFT_LEFT_32 = (1 << 16) * (1 << 16), SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32;
var TEXT_DECODER_MIN_LENGTH = 12;
var utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8');
Pbf.prototype = {
    destroy: function () {
        this.buf = null;
    },
    readFields: function (readField, result, end) {
        end = end || this.length;
        while (this.pos < end) {
            var val = this.readVarint(), tag = val >> 3, startPos = this.pos;
            this.type = val & 7;
            readField(tag, result, this);
            if (this.pos === startPos)
                this.skip(val);
        }
        return result;
    },
    readMessage: function (readField, result) {
        return this.readFields(readField, result, this.readVarint() + this.pos);
    },
    readFixed32: function () {
        var val = readUInt32(this.buf, this.pos);
        this.pos += 4;
        return val;
    },
    readSFixed32: function () {
        var val = readInt32(this.buf, this.pos);
        this.pos += 4;
        return val;
    },
    readFixed64: function () {
        var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
        this.pos += 8;
        return val;
    },
    readSFixed64: function () {
        var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
        this.pos += 8;
        return val;
    },
    readFloat: function () {
        var val = ieee754.read(this.buf, this.pos, true, 23, 4);
        this.pos += 4;
        return val;
    },
    readDouble: function () {
        var val = ieee754.read(this.buf, this.pos, true, 52, 8);
        this.pos += 8;
        return val;
    },
    readVarint: function (isSigned) {
        var buf = this.buf, val, b;
        b = buf[this.pos++];
        val = b & 127;
        if (b < 128)
            return val;
        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);
    },
    readVarint64: function () {
        return this.readVarint(true);
    },
    readSVarint: function () {
        var num = this.readVarint();
        return num % 2 === 1 ? (num + 1) / -2 : num / 2;
    },
    readBoolean: function () {
        return Boolean(this.readVarint());
    },
    readString: function () {
        var end = this.readVarint() + this.pos;
        var pos = this.pos;
        this.pos = end;
        if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) {
            return readUtf8TextDecoder(this.buf, pos, end);
        }
        return readUtf8(this.buf, pos, end);
    },
    readBytes: function () {
        var end = this.readVarint() + this.pos, buffer = this.buf.subarray(this.pos, end);
        this.pos = end;
        return buffer;
    },
    readPackedVarint: function (arr, isSigned) {
        if (this.type !== Pbf.Bytes)
            return arr.push(this.readVarint(isSigned));
        var end = readPackedEnd(this);
        arr = arr || [];
        while (this.pos < end)
            arr.push(this.readVarint(isSigned));
        return arr;
    },
    readPackedSVarint: function (arr) {
        if (this.type !== Pbf.Bytes)
            return arr.push(this.readSVarint());
        var end = readPackedEnd(this);
        arr = arr || [];
        while (this.pos < end)
            arr.push(this.readSVarint());
        return arr;
    },
    readPackedBoolean: function (arr) {
        if (this.type !== Pbf.Bytes)
            return arr.push(this.readBoolean());
        var end = readPackedEnd(this);
        arr = arr || [];
        while (this.pos < end)
            arr.push(this.readBoolean());
        return arr;
    },
    readPackedFloat: function (arr) {
        if (this.type !== Pbf.Bytes)
            return arr.push(this.readFloat());
        var end = readPackedEnd(this);
        arr = arr || [];
        while (this.pos < end)
            arr.push(this.readFloat());
        return arr;
    },
    readPackedDouble: function (arr) {
        if (this.type !== Pbf.Bytes)
            return arr.push(this.readDouble());
        var end = readPackedEnd(this);
        arr = arr || [];
        while (this.pos < end)
            arr.push(this.readDouble());
        return arr;
    },
    readPackedFixed32: function (arr) {
        if (this.type !== Pbf.Bytes)
            return arr.push(this.readFixed32());
        var end = readPackedEnd(this);
        arr = arr || [];
        while (this.pos < end)
            arr.push(this.readFixed32());
        return arr;
    },
    readPackedSFixed32: function (arr) {
        if (this.type !== Pbf.Bytes)
            return arr.push(this.readSFixed32());
        var end = readPackedEnd(this);
        arr = arr || [];
        while (this.pos < end)
            arr.push(this.readSFixed32());
        return arr;
    },
    readPackedFixed64: function (arr) {
        if (this.type !== Pbf.Bytes)
            return arr.push(this.readFixed64());
        var end = readPackedEnd(this);
        arr = arr || [];
        while (this.pos < end)
            arr.push(this.readFixed64());
        return arr;
    },
    readPackedSFixed64: function (arr) {
        if (this.type !== Pbf.Bytes)
            return arr.push(this.readSFixed64());
        var end = readPackedEnd(this);
        arr = arr || [];
        while (this.pos < end)
            arr.push(this.readSFixed64());
        return arr;
    },
    skip: function (val) {
        var 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);
    },
    writeTag: function (tag, type) {
        this.writeVarint(tag << 3 | type);
    },
    realloc: function (min) {
        var length = this.length || 16;
        while (length < this.pos + min)
            length *= 2;
        if (length !== this.length) {
            var buf = new Uint8Array(length);
            buf.set(this.buf);
            this.buf = buf;
            this.length = length;
        }
    },
    finish: function () {
        this.length = this.pos;
        this.pos = 0;
        return this.buf.subarray(0, this.length);
    },
    writeFixed32: function (val) {
        this.realloc(4);
        writeInt32(this.buf, val, this.pos);
        this.pos += 4;
    },
    writeSFixed32: function (val) {
        this.realloc(4);
        writeInt32(this.buf, val, this.pos);
        this.pos += 4;
    },
    writeFixed64: function (val) {
        this.realloc(8);
        writeInt32(this.buf, val & -1, this.pos);
        writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
        this.pos += 8;
    },
    writeSFixed64: function (val) {
        this.realloc(8);
        writeInt32(this.buf, val & -1, this.pos);
        writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
        this.pos += 8;
    },
    writeVarint: function (val) {
        val = +val || 0;
        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;
    },
    writeSVarint: function (val) {
        this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);
    },
    writeBoolean: function (val) {
        this.writeVarint(Boolean(val));
    },
    writeString: function (str) {
        str = String(str);
        this.realloc(str.length * 4);
        this.pos++;
        var startPos = this.pos;
        this.pos = writeUtf8(this.buf, str, this.pos);
        var len = this.pos - startPos;
        if (len >= 128)
            makeRoomForExtraLength(startPos, len, this);
        this.pos = startPos - 1;
        this.writeVarint(len);
        this.pos += len;
    },
    writeFloat: function (val) {
        this.realloc(4);
        ieee754.write(this.buf, val, this.pos, true, 23, 4);
        this.pos += 4;
    },
    writeDouble: function (val) {
        this.realloc(8);
        ieee754.write(this.buf, val, this.pos, true, 52, 8);
        this.pos += 8;
    },
    writeBytes: function (buffer) {
        var len = buffer.length;
        this.writeVarint(len);
        this.realloc(len);
        for (var i = 0; i < len; i++)
            this.buf[this.pos++] = buffer[i];
    },
    writeRawMessage: function (fn, obj) {
        this.pos++;
        var startPos = this.pos;
        fn(obj, this);
        var len = this.pos - startPos;
        if (len >= 128)
            makeRoomForExtraLength(startPos, len, this);
        this.pos = startPos - 1;
        this.writeVarint(len);
        this.pos += len;
    },
    writeMessage: function (tag, fn, obj) {
        this.writeTag(tag, Pbf.Bytes);
        this.writeRawMessage(fn, obj);
    },
    writePackedVarint: function (tag, arr) {
        if (arr.length)
            this.writeMessage(tag, writePackedVarint, arr);
    },
    writePackedSVarint: function (tag, arr) {
        if (arr.length)
            this.writeMessage(tag, writePackedSVarint, arr);
    },
    writePackedBoolean: function (tag, arr) {
        if (arr.length)
            this.writeMessage(tag, writePackedBoolean, arr);
    },
    writePackedFloat: function (tag, arr) {
        if (arr.length)
            this.writeMessage(tag, writePackedFloat, arr);
    },
    writePackedDouble: function (tag, arr) {
        if (arr.length)
            this.writeMessage(tag, writePackedDouble, arr);
    },
    writePackedFixed32: function (tag, arr) {
        if (arr.length)
            this.writeMessage(tag, writePackedFixed32, arr);
    },
    writePackedSFixed32: function (tag, arr) {
        if (arr.length)
            this.writeMessage(tag, writePackedSFixed32, arr);
    },
    writePackedFixed64: function (tag, arr) {
        if (arr.length)
            this.writeMessage(tag, writePackedFixed64, arr);
    },
    writePackedSFixed64: function (tag, arr) {
        if (arr.length)
            this.writeMessage(tag, writePackedSFixed64, arr);
    },
    writeBytesField: function (tag, buffer) {
        this.writeTag(tag, Pbf.Bytes);
        this.writeBytes(buffer);
    },
    writeFixed32Field: function (tag, val) {
        this.writeTag(tag, Pbf.Fixed32);
        this.writeFixed32(val);
    },
    writeSFixed32Field: function (tag, val) {
        this.writeTag(tag, Pbf.Fixed32);
        this.writeSFixed32(val);
    },
    writeFixed64Field: function (tag, val) {
        this.writeTag(tag, Pbf.Fixed64);
        this.writeFixed64(val);
    },
    writeSFixed64Field: function (tag, val) {
        this.writeTag(tag, Pbf.Fixed64);
        this.writeSFixed64(val);
    },
    writeVarintField: function (tag, val) {
        this.writeTag(tag, Pbf.Varint);
        this.writeVarint(val);
    },
    writeSVarintField: function (tag, val) {
        this.writeTag(tag, Pbf.Varint);
        this.writeSVarint(val);
    },
    writeStringField: function (tag, str) {
        this.writeTag(tag, Pbf.Bytes);
        this.writeString(str);
    },
    writeFloatField: function (tag, val) {
        this.writeTag(tag, Pbf.Fixed32);
        this.writeFloat(val);
    },
    writeDoubleField: function (tag, val) {
        this.writeTag(tag, Pbf.Fixed64);
        this.writeDouble(val);
    },
    writeBooleanField: function (tag, val) {
        this.writeVarintField(tag, Boolean(val));
    }
};
function readVarintRemainder(l, s, p) {
    var buf = p.buf, 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');
}
function readPackedEnd(pbf) {
    return pbf.type === Pbf.Bytes ? pbf.readVarint() + pbf.pos : pbf.pos + 1;
}
function toNum(low, high, isSigned) {
    if (isSigned) {
        return high * 4294967296 + (low >>> 0);
    }
    return (high >>> 0) * 4294967296 + (low >>> 0);
}
function writeBigVarint(val, pbf) {
    var 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 >= 18446744073709552000 || val < -18446744073709552000) {
        throw new Error('Given varint doesn\'t fit into 10 bytes');
    }
    pbf.realloc(10);
    writeBigVarintLow(low, high, pbf);
    writeBigVarintHigh(high, 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;
}
function writeBigVarintHigh(high, pbf) {
    var 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;
}
function makeRoomForExtraLength(startPos, len, pbf) {
    var extraLen = len <= 16383 ? 1 : len <= 2097151 ? 2 : len <= 268435455 ? 3 : Math.floor(Math.log(len) / (Math.LN2 * 7));
    pbf.realloc(extraLen);
    for (var i = pbf.pos - 1; i >= startPos; i--)
        pbf.buf[i + extraLen] = pbf.buf[i];
}
function writePackedVarint(arr, pbf) {
    for (var i = 0; i < arr.length; i++)
        pbf.writeVarint(arr[i]);
}
function writePackedSVarint(arr, pbf) {
    for (var i = 0; i < arr.length; i++)
        pbf.writeSVarint(arr[i]);
}
function writePackedFloat(arr, pbf) {
    for (var i = 0; i < arr.length; i++)
        pbf.writeFloat(arr[i]);
}
function writePackedDouble(arr, pbf) {
    for (var i = 0; i < arr.length; i++)
        pbf.writeDouble(arr[i]);
}
function writePackedBoolean(arr, pbf) {
    for (var i = 0; i < arr.length; i++)
        pbf.writeBoolean(arr[i]);
}
function writePackedFixed32(arr, pbf) {
    for (var i = 0; i < arr.length; i++)
        pbf.writeFixed32(arr[i]);
}
function writePackedSFixed32(arr, pbf) {
    for (var i = 0; i < arr.length; i++)
        pbf.writeSFixed32(arr[i]);
}
function writePackedFixed64(arr, pbf) {
    for (var i = 0; i < arr.length; i++)
        pbf.writeFixed64(arr[i]);
}
function writePackedSFixed64(arr, pbf) {
    for (var i = 0; i < arr.length; i++)
        pbf.writeSFixed64(arr[i]);
}
function readUInt32(buf, pos) {
    return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + buf[pos + 3] * 16777216;
}
function writeInt32(buf, val, pos) {
    buf[pos] = val;
    buf[pos + 1] = val >>> 8;
    buf[pos + 2] = val >>> 16;
    buf[pos + 3] = val >>> 24;
}
function readInt32(buf, pos) {
    return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + (buf[pos + 3] << 24);
}
function readUtf8(buf, pos, end) {
    var str = '';
    var i = pos;
    while (i < end) {
        var b0 = buf[i];
        var c = null;
        var bytesPerSequence = b0 > 239 ? 4 : b0 > 223 ? 3 : b0 > 191 ? 2 : 1;
        if (i + bytesPerSequence > end)
            break;
        var 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;
}
function readUtf8TextDecoder(buf, pos, end) {
    return utf8TextDecoder.decode(buf.subarray(pos, end));
}
function writeUtf8(buf, str, pos) {
    for (var 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;
}

const border$1 = 3;
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 + 2 * border$1,
                height: height + 2 * border$1
            }, 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 pbf(data).readFields(readFontstacks, []);
}
const GLYPH_PBF_BORDER = border$1;

function potpack(boxes) {

    // calculate total box area and maximum box width
    let area = 0;
    let maxWidth = 0;

    for (const box of boxes) {
        area += box.w * box.h;
        maxWidth = Math.max(maxWidth, box.w);
    }

    // sort the boxes for insertion by height, descending
    boxes.sort((a, b) => b.h - a.h);

    // aim for a squarish resulting container,
    // slightly adjusted for sub-100% space utilization
    const startWidth = Math.max(Math.ceil(Math.sqrt(area / 0.95)), maxWidth);

    // start with a single empty space, unbounded at the bottom
    const spaces = [{x: 0, y: 0, w: startWidth, h: Infinity}];

    let width = 0;
    let height = 0;

    for (const box of boxes) {
        // look through spaces backwards so that we check smaller spaces first
        for (let i = spaces.length - 1; i >= 0; i--) {
            const space = spaces[i];

            // look for empty spaces that can accommodate the current box
            if (box.w > space.w || box.h > space.h) continue;

            // found the space; add the box to its top-left corner
            // |-------|-------|
            // |  box  |       |
            // |_______|       |
            // |         space |
            // |_______________|
            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) {
                // space matches the box exactly; remove it
                const last = spaces.pop();
                if (i < spaces.length) spaces[i] = last;

            } else if (box.h === space.h) {
                // space matches the box height; update it accordingly
                // |-------|---------------|
                // |  box  | updated space |
                // |_______|_______________|
                space.x += box.w;
                space.w -= box.w;

            } else if (box.w === space.w) {
                // space matches the box width; update it accordingly
                // |---------------|
                // |      box      |
                // |_______________|
                // | updated space |
                // |_______________|
                space.y += box.h;
                space.h -= box.h;

            } else {
                // otherwise the box splits the space into two spaces
                // |-------|-----------|
                // |  box  | new space |
                // |_______|___________|
                // | updated space     |
                // |___________________|
                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, // container width
        h: height, // container height
        fill: (area / (width * height)) || 0 // space utilization
    };
}

const IMAGE_PADDING = 1;
class ImagePosition {
    constructor(paddedRect, {pixelRatio, version, stretchX, stretchY, content}) {
        this.paddedRect = paddedRect;
        this.pixelRatio = pixelRatio;
        this.stretchX = stretchX;
        this.stretchY = stretchY;
        this.content = content;
        this.version = version;
    }
    get tl() {
        return [
            this.paddedRect.x + IMAGE_PADDING,
            this.paddedRect.y + IMAGE_PADDING
        ];
    }
    get br() {
        return [
            this.paddedRect.x + this.paddedRect.w - IMAGE_PADDING,
            this.paddedRect.y + this.paddedRect.h - IMAGE_PADDING
        ];
    }
    get displaySize() {
        return [
            (this.paddedRect.w - IMAGE_PADDING * 2) / this.pixelRatio,
            (this.paddedRect.h - IMAGE_PADDING * 2) / this.pixelRatio
        ];
    }
}
class ImageAtlas {
    constructor(icons, patterns) {
        const iconPositions = {}, patternPositions = {};
        this.haveRenderCallbacks = [];
        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];
            const bin = iconPositions[id].paddedRect;
            RGBAImage.copy(src.data, image, {
                x: 0,
                y: 0
            }, {
                x: bin.x + IMAGE_PADDING,
                y: bin.y + IMAGE_PADDING
            }, src.data);
        }
        for (const id in patterns) {
            const src = patterns[id];
            const bin = patternPositions[id].paddedRect;
            const x = bin.x + IMAGE_PADDING, y = bin.y + IMAGE_PADDING, 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 * IMAGE_PADDING,
                h: src.data.height + 2 * IMAGE_PADDING
            };
            bins.push(bin);
            positions[id] = new ImagePosition(bin, src);
            if (src.hasRenderCallback) {
                this.haveRenderCallbacks.push(id);
            }
        }
    }
    patchUpdatedImages(imageManager, texture) {
        imageManager.dispatchRenderCallbacks(this.haveRenderCallbacks);
        for (const name in imageManager.updatedImages) {
            this.patchUpdatedImage(this.iconPositions[name], imageManager.getImage(name), texture);
            this.patchUpdatedImage(this.patternPositions[name], imageManager.getImage(name), texture);
        }
    }
    patchUpdatedImage(position, image, texture) {
        if (!position || !image)
            return;
        if (position.version === image.version)
            return;
        position.version = image.version;
        const [x, y] = position.tl;
        texture.update(image.data, undefined, {
            x,
            y
        });
    }
}
register('ImagePosition', ImagePosition);
register('ImageAtlas', ImageAtlas);

const WritingMode = {
    horizontal: 1,
    vertical: 2,
    horizontalOnly: 3
};
const SHAPING_DEFAULT_OFFSET = -17;
function isEmpty(positionedLines) {
    for (const line of positionedLines) {
        if (line.positionedGlyphs.length !== 0) {
            return false;
        }
    }
    return true;
}
const PUAbegin = 57344;
const PUAend = 63743;
class SectionOptions {
    constructor() {
        this.scale = 1;
        this.fontStack = '';
        this.imageName = null;
    }
    static forText(scale, fontStack) {
        const textOptions = new SectionOptions();
        textOptions.scale = scale || 1;
        textOptions.fontStack = fontStack;
        return textOptions;
    }
    static forImage(imageName) {
        const imageOptions = new SectionOptions();
        imageOptions.imageName = imageName;
        return imageOptions;
    }
}
class TaggedString {
    constructor() {
        this.text = '';
        this.sectionIndex = [];
        this.sections = [];
        this.imageSectionID = null;
    }
    static fromFeature(text, defaultFontStack) {
        const result = new TaggedString();
        for (let i = 0; i < text.sections.length; i++) {
            const section = text.sections[i];
            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];
    }
    getCharCode(index) {
        return this.text.charCodeAt(index);
    }
    verticalizePunctuation() {
        this.text = verticalizePunctuation(this.text);
    }
    trim() {
        let beginningWhitespace = 0;
        for (let i = 0; i < this.text.length && whitespace[this.text.charCodeAt(i)]; i++) {
            beginningWhitespace++;
        }
        let trailingWhitespace = this.text.length;
        for (let i = this.text.length - 1; i >= 0 && i >= beginningWhitespace && whitespace[this.text.charCodeAt(i)]; i--) {
            trailingWhitespace--;
        }
        this.text = this.text.substring(beginningWhitespace, trailingWhitespace);
        this.sectionIndex = this.sectionIndex.slice(beginningWhitespace, trailingWhitespace);
    }
    substring(start, end) {
        const substring = new TaggedString();
        substring.text = this.text.substring(start, end);
        substring.sectionIndex = this.sectionIndex.slice(start, end);
        substring.sections = this.sections;
        return substring;
    }
    toString() {
        return this.text;
    }
    getMaxScale() {
        return this.sectionIndex.reduce((max, index) => Math.max(max, this.sections[index].scale), 0);
    }
    addTextSection(section, defaultFontStack) {
        this.text += section.text;
        this.sections.push(SectionOptions.forText(section.scale, section.fontStack || defaultFontStack));
        const index = this.sections.length - 1;
        for (let i = 0; i < section.text.length; ++i) {
            this.sectionIndex.push(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 ${ PUAend - PUAbegin + 2 }`);
            return;
        }
        this.text += String.fromCharCode(nextImageSectionCharCode);
        this.sections.push(SectionOptions.forImage(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;
    }
}
function breakLines(input, lineBreakPoints) {
    const lines = [];
    const text = input.text;
    let start = 0;
    for (const lineBreak of lineBreakPoints) {
        lines.push(input.substring(start, lineBreak));
        start = lineBreak;
    }
    if (start < text.length) {
        lines.push(input.substring(start, text.length));
    }
    return lines;
}
function shapeText(text, glyphMap, glyphPositions, imagePositions, defaultFontStack, maxWidth, lineHeight, textAnchor, textJustify, spacing, translate, writingMode, allowVerticalPlacement, symbolPlacement, layoutTextSize, layoutTextSizeThisZoom) {
    const logicalInput = TaggedString.fromFeature(text, defaultFontStack);
    if (writingMode === WritingMode.vertical) {
        logicalInput.verticalizePunctuation();
    }
    let lines;
    const {processBidirectionalText, processStyledBidirectionalText} = plugin;
    if (processBidirectionalText && logicalInput.sections.length === 1) {
        lines = [];
        const untaggedLines = processBidirectionalText(logicalInput.toString(), determineLineBreaks(logicalInput, spacing, maxWidth, glyphMap, imagePositions, symbolPlacement, layoutTextSize));
        for (const line of untaggedLines) {
            const taggedLine = new TaggedString();
            taggedLine.text = line;
            taggedLine.sections = logicalInput.sections;
            for (let i = 0; i < line.length; i++) {
                taggedLine.sectionIndex.push(0);
            }
            lines.push(taggedLine);
        }
    } else if (processStyledBidirectionalText) {
        lines = [];
        const processedLines = processStyledBidirectionalText(logicalInput.text, logicalInput.sectionIndex, determineLineBreaks(logicalInput, spacing, maxWidth, glyphMap, imagePositions, symbolPlacement, layoutTextSize));
        for (const line of processedLines) {
            const taggedLine = new TaggedString();
            taggedLine.text = line[0];
            taggedLine.sectionIndex = line[1];
            taggedLine.sections = logicalInput.sections;
            lines.push(taggedLine);
        }
    } else {
        lines = breakLines(logicalInput, determineLineBreaks(logicalInput, spacing, maxWidth, glyphMap, imagePositions, symbolPlacement, layoutTextSize));
    }
    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;
}
const whitespace = {
    [9]: true,
    [10]: true,
    [11]: true,
    [12]: true,
    [13]: true,
    [32]: true
};
const breakable = {
    [10]: true,
    [32]: true,
    [38]: true,
    [40]: true,
    [41]: true,
    [43]: true,
    [45]: true,
    [47]: true,
    [173]: true,
    [183]: true,
    [8203]: true,
    [8208]: true,
    [8211]: true,
    [8231]: true
};
function getGlyphAdvance(codePoint, section, glyphMap, imagePositions, spacing, layoutTextSize) {
    if (!section.imageName) {
        const positions = glyphMap[section.fontStack];
        const glyph = positions && positions[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 * ONE_EM / layoutTextSize + spacing;
    }
}
function determineAverageLineWidth(logicalInput, spacing, maxWidth, glyphMap, imagePositions, layoutTextSize) {
    let totalWidth = 0;
    for (let index = 0; index < logicalInput.length(); index++) {
        const section = logicalInput.getSection(index);
        totalWidth += getGlyphAdvance(logicalInput.getCharCode(index), section, glyphMap, imagePositions, spacing, layoutTextSize);
    }
    const lineCount = Math.max(1, Math.ceil(totalWidth / maxWidth));
    return totalWidth / lineCount;
}
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 -= 10000;
    }
    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 lineWidth = breakX - potentialBreak.x;
        const breakBadness = calculateBadness(lineWidth, 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);
}
function determineLineBreaks(logicalInput, spacing, maxWidth, glyphMap, imagePositions, symbolPlacement, layoutTextSize) {
    if (symbolPlacement !== 'point')
        return [];
    if (!logicalInput)
        return [];
    const potentialLineBreaks = [];
    const targetWidth = determineAverageLineWidth(logicalInput, spacing, maxWidth, glyphMap, imagePositions, layoutTextSize);
    const hasServerSuggestedBreakpoints = logicalInput.text.indexOf('\u200B') >= 0;
    let currentX = 0;
    for (let i = 0; i < logicalInput.length(); i++) {
        const section = logicalInput.getSection(i);
        const codePoint = logicalInput.getCharCode(i);
        if (!whitespace[codePoint])
            currentX += getGlyphAdvance(codePoint, section, glyphMap, imagePositions, spacing, layoutTextSize);
        if (i < logicalInput.length() - 1) {
            const ideographicBreak = charAllowsIdeographicBreaking(codePoint);
            if (breakable[codePoint] || ideographicBreak || section.imageName) {
                potentialLineBreaks.push(evaluateBreak(i + 1, currentX, targetWidth, potentialLineBreaks, calculatePenalty(codePoint, logicalInput.getCharCode(i + 1), ideographicBreak && hasServerSuggestedBreakpoints), false));
            }
        }
    }
    return leastBadBreaks(evaluateBreak(logicalInput.length(), currentX, targetWidth, potentialLineBreaks, 0, true));
}
function getAnchorAlignment(anchor) {
    let horizontalAlign = 0.5, verticalAlign = 0.5;
    switch (anchor) {
    case 'right':
    case 'top-right':
    case 'bottom-right':
        horizontalAlign = 1;
        break;
    case 'left':
    case 'top-left':
    case 'bottom-left':
        horizontalAlign = 0;
        break;
    }
    switch (anchor) {
    case 'bottom':
    case 'bottom-right':
    case 'bottom-left':
        verticalAlign = 1;
        break;
    case 'top':
    case 'top-right':
    case 'top-left':
        verticalAlign = 0;
        break;
    }
    return {
        horizontalAlign,
        verticalAlign
    };
}
function shapeLines(shaping, glyphMap, glyphPositions, imagePositions, lines, lineHeight, textAnchor, textJustify, writingMode, spacing, allowVerticalPlacement, layoutTextSizeThisZoom) {
    let x = 0;
    let y = SHAPING_DEFAULT_OFFSET;
    let maxLineLength = 0;
    let maxLineHeight = 0;
    const justify = textJustify === 'right' ? 1 : textJustify === 'left' ? 0 : 0.5;
    let lineIndex = 0;
    for (const line of lines) {
        line.trim();
        const lineMaxScale = line.getMaxScale();
        const maxLineOffset = (lineMaxScale - 1) * ONE_EM;
        const positionedLine = {
            positionedGlyphs: [],
            lineOffset: 0
        };
        shaping.positionedLines[lineIndex] = positionedLine;
        const positionedGlyphs = positionedLine.positionedGlyphs;
        let lineOffset = 0;
        if (!line.length()) {
            y += lineHeight;
            ++lineIndex;
            continue;
        }
        for (let i = 0; i < line.length(); i++) {
            const section = line.getSection(i);
            const sectionIndex = line.getSectionIndex(i);
            const codePoint = line.getCharCode(i);
            let baselineOffset = 0;
            let metrics = null;
            let rect = null;
            let imageName = null;
            let verticalAdvance = ONE_EM;
            const vertical = !(writingMode === WritingMode.horizontal || !allowVerticalPlacement && !charHasUprightVerticalOrientation(codePoint) || allowVerticalPlacement && (whitespace[codePoint] || charInComplexShapingScript(codePoint)));
            if (!section.imageName) {
                const positions = glyphPositions[section.fontStack];
                const glyphPosition = positions && positions[codePoint];
                if (glyphPosition && glyphPosition.rect) {
                    rect = glyphPosition.rect;
                    metrics = glyphPosition.metrics;
                } else {
                    const glyphs = glyphMap[section.fontStack];
                    const glyph = glyphs && glyphs[codePoint];
                    if (!glyph)
                        continue;
                    metrics = glyph.metrics;
                }
                baselineOffset = (lineMaxScale - section.scale) * ONE_EM;
            } else {
                const imagePosition = imagePositions[section.imageName];
                if (!imagePosition)
                    continue;
                imageName = section.imageName;
                shaping.iconsInText = shaping.iconsInText || true;
                rect = imagePosition.paddedRect;
                const size = imagePosition.displaySize;
                section.scale = section.scale * ONE_EM / layoutTextSizeThisZoom;
                metrics = {
                    width: size[0],
                    height: size[1],
                    left: IMAGE_PADDING,
                    top: -GLYPH_PBF_BORDER,
                    advance: vertical ? size[1] : size[0],
                    localGlyph: false
                };
                const imageOffset = ONE_EM - size[1] * section.scale;
                baselineOffset = maxLineOffset + imageOffset;
                verticalAdvance = metrics.advance;
                const offset = vertical ? size[0] * section.scale - ONE_EM * lineMaxScale : size[1] * section.scale - ONE_EM * lineMaxScale;
                if (offset > 0 && offset > lineOffset) {
                    lineOffset = offset;
                }
            }
            if (!vertical) {
                positionedGlyphs.push({
                    glyph: codePoint,
                    imageName,
                    x,
                    y: y + baselineOffset,
                    vertical,
                    scale: section.scale,
                    localGlyph: metrics.localGlyph,
                    fontStack: section.fontStack,
                    sectionIndex,
                    metrics,
                    rect
                });
                x += metrics.advance * section.scale + spacing;
            } else {
                shaping.verticalizable = true;
                positionedGlyphs.push({
                    glyph: codePoint,
                    imageName,
                    x,
                    y: y + baselineOffset,
                    vertical,
                    scale: section.scale,
                    localGlyph: metrics.localGlyph,
                    fontStack: section.fontStack,
                    sectionIndex,
                    metrics,
                    rect
                });
                x += verticalAdvance * section.scale + spacing;
            }
        }
        if (positionedGlyphs.length !== 0) {
            const lineLength = x - spacing;
            maxLineLength = Math.max(lineLength, maxLineLength);
            justifyLine(positionedGlyphs, 0, positionedGlyphs.length - 1, justify, lineOffset);
        }
        x = 0;
        const currentLineHeight = lineHeight * lineMaxScale + lineOffset;
        positionedLine.lineOffset = Math.max(lineOffset, maxLineOffset);
        y += currentLineHeight;
        maxLineHeight = Math.max(currentLineHeight, maxLineHeight);
        ++lineIndex;
    }
    const height = y - SHAPING_DEFAULT_OFFSET;
    const {horizontalAlign, verticalAlign} = getAnchorAlignment(textAnchor);
    align(shaping.positionedLines, justify, horizontalAlign, verticalAlign, maxLineLength, maxLineHeight, lineHeight, height, lines.length);
    shaping.top += -verticalAlign * height;
    shaping.bottom = shaping.top + height;
    shaping.left += -horizontalAlign * maxLineLength;
    shaping.right = shaping.left + maxLineLength;
}
function justifyLine(positionedGlyphs, start, end, justify, lineOffset) {
    if (!justify && !lineOffset)
        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;
        positionedGlyphs[j].y += lineOffset;
    }
}
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 - SHAPING_DEFAULT_OFFSET;
    } else {
        shiftY = (-verticalAlign * lineCount + 0.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;
    const y2 = y1 + image.displaySize[1];
    return {
        image,
        top: y1,
        bottom: y2,
        left: x1,
        right: x2
    };
}
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
    };
}

class Anchor extends pointGeometry {
    constructor(x, y, angle, segment) {
        super(x, y);
        this.angle = angle;
        if (segment !== undefined) {
            this.segment = segment;
        }
    }
    clone() {
        return new Anchor(this.x, this.y, this.angle, this.segment);
    }
}
register('Anchor', Anchor);

function checkMaxAngle(line, anchor, labelLength, windowSize, maxAngle) {
    if (anchor.segment === undefined)
        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;
}

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, x = number(a.x, b.x, t), y = number(a.y, b.y, t);
            const anchor = new Anchor(x, y, 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;
    const offset = !isLineContinued ? (shapedLabelLength / 2 + fixedExtraOffset) * boxScale * overscaling % spacing : spacing / 2 * overscaling % spacing;
    return resample(line, offset, 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, 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 = number(a.x, b.x, t), y = 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;
}

function clipLine(lines, x1, y1, x2, y2) {
    const clippedLines = [];
    for (let l = 0; l < lines.length; l++) {
        const line = lines[l];
        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 pointGeometry(x1, p0.y + (p1.y - p0.y) * ((x1 - p0.x) / (p1.x - p0.x)))._round();
            } else if (p1.x < x1) {
                p1 = new pointGeometry(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 pointGeometry(p0.x + (p1.x - p0.x) * ((y1 - p0.y) / (p1.y - p0.y)), y1)._round();
            } else if (p1.y < y1) {
                p1 = new pointGeometry(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 pointGeometry(x2, p0.y + (p1.y - p0.y) * ((x2 - p0.x) / (p1.x - p0.x)))._round();
            } else if (p1.x >= x2) {
                p1 = new pointGeometry(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 pointGeometry(p0.x + (p1.x - p0.x) * ((y2 - p0.y) / (p1.y - p0.y)), y2)._round();
            } else if (p1.y >= y2) {
                p1 = new pointGeometry(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;
}

function loadGlyphRange (fontstack, range, urlTemplate, requestManager, callback) {
    const begin = range * 256;
    const end = begin + 255;
    const request = requestManager.transformRequest(requestManager.normalizeGlyphsURL(urlTemplate).replace('{fontstack}', fontstack).replace('{range}', `${ begin }-${ end }`), ResourceType.Glyphs);
    getArrayBuffer(request, (err, data) => {
        if (err) {
            callback(err);
        } else if (data) {
            const glyphs = {};
            for (const glyph of parseGlyphPBF(data)) {
                glyphs[glyph.id] = glyph;
            }
            callback(null, glyphs);
        }
    });
}

var tinySdf = TinySDF;
var _default = TinySDF;
var INF = 100000000000000000000;
function TinySDF(fontSize, buffer, radius, cutoff, fontFamily, fontWeight) {
    this.fontSize = fontSize || 24;
    this.buffer = buffer === undefined ? 3 : buffer;
    this.cutoff = cutoff || 0.25;
    this.fontFamily = fontFamily || 'sans-serif';
    this.fontWeight = fontWeight || 'normal';
    this.radius = radius || 8;
    var size = this.size = this.fontSize + this.buffer * 2;
    var gridSize = size + this.buffer * 2;
    this.canvas = document.createElement('canvas');
    this.canvas.width = this.canvas.height = size;
    this.ctx = this.canvas.getContext('2d');
    this.ctx.font = this.fontWeight + ' ' + this.fontSize + 'px ' + this.fontFamily;
    this.ctx.textAlign = 'left';
    this.ctx.fillStyle = 'black';
    this.gridOuter = new Float64Array(gridSize * gridSize);
    this.gridInner = new Float64Array(gridSize * gridSize);
    this.f = new Float64Array(gridSize);
    this.z = new Float64Array(gridSize + 1);
    this.v = new Uint16Array(gridSize);
    this.useMetrics = this.ctx.measureText('A').actualBoundingBoxLeft !== undefined;
    this.middle = Math.round(size / 2 * (navigator.userAgent.indexOf('Gecko/') >= 0 ? 1.2 : 1));
}
function prepareGrids(imgData, width, height, glyphWidth, glyphHeight, gridOuter, gridInner) {
    gridOuter.fill(INF, 0, width * height);
    gridInner.fill(0, 0, width * height);
    var offset = (width - glyphWidth) / 2;
    for (var y = 0; y < glyphHeight; y++) {
        for (var x = 0; x < glyphWidth; x++) {
            var j = (y + offset) * width + x + offset;
            var a = imgData.data[4 * (y * glyphWidth + x) + 3] / 255;
            if (a === 1) {
                gridOuter[j] = 0;
                gridInner[j] = INF;
            } else if (a === 0) {
                gridOuter[j] = INF;
                gridInner[j] = 0;
            } else {
                var b = Math.max(0, 0.5 - a);
                var c = Math.max(0, a - 0.5);
                gridOuter[j] = b * b;
                gridInner[j] = c * c;
            }
        }
    }
}
function extractAlpha(alphaChannel, width, height, gridOuter, gridInner, radius, cutoff) {
    for (var i = 0; i < width * height; i++) {
        var d = Math.sqrt(gridOuter[i]) - Math.sqrt(gridInner[i]);
        alphaChannel[i] = Math.round(255 - 255 * (d / radius + cutoff));
    }
}
TinySDF.prototype._draw = function (char, getMetrics) {
    var textMetrics = this.ctx.measureText(char);
    var advance = textMetrics.width;
    var doubleBuffer = 2 * this.buffer;
    var width, glyphWidth, height, glyphHeight, top;
    var imgTop, imgLeft, baselinePosition;
    if (getMetrics && this.useMetrics) {
        top = Math.floor(textMetrics.actualBoundingBoxAscent);
        baselinePosition = this.buffer + Math.ceil(textMetrics.actualBoundingBoxAscent);
        imgTop = this.buffer;
        imgLeft = this.buffer;
        glyphWidth = Math.min(this.size, Math.ceil(textMetrics.actualBoundingBoxRight - textMetrics.actualBoundingBoxLeft));
        glyphHeight = Math.min(this.size - imgTop, Math.ceil(textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent));
        width = glyphWidth + doubleBuffer;
        height = glyphHeight + doubleBuffer;
        this.ctx.textBaseline = 'alphabetic';
    } else {
        width = glyphWidth = this.size;
        height = glyphHeight = this.size;
        top = 19 * this.fontSize / 24;
        imgTop = imgLeft = 0;
        baselinePosition = this.middle;
        this.ctx.textBaseline = 'middle';
    }
    var imgData;
    if (glyphWidth && glyphHeight) {
        this.ctx.clearRect(imgLeft, imgTop, glyphWidth, glyphHeight);
        this.ctx.fillText(char, this.buffer, baselinePosition);
        imgData = this.ctx.getImageData(imgLeft, imgTop, glyphWidth, glyphHeight);
    }
    var alphaChannel = new Uint8ClampedArray(width * height);
    prepareGrids(imgData, width, height, glyphWidth, glyphHeight, this.gridOuter, this.gridInner);
    edt(this.gridOuter, width, height, this.f, this.v, this.z);
    edt(this.gridInner, width, height, this.f, this.v, this.z);
    extractAlpha(alphaChannel, width, height, this.gridOuter, this.gridInner, this.radius, this.cutoff);
    return {
        data: alphaChannel,
        metrics: {
            width: glyphWidth,
            height: glyphHeight,
            sdfWidth: width,
            sdfHeight: height,
            top: top,
            left: 0,
            advance: advance
        }
    };
};
TinySDF.prototype.draw = function (char) {
    return this._draw(char, false).data;
};
TinySDF.prototype.drawWithMetrics = function (char) {
    return this._draw(char, true);
};
function edt(data, width, height, f, v, z) {
    for (var x = 0; x < width; x++)
        edt1d(data, x, width, height, f, v, z);
    for (var y = 0; y < height; y++)
        edt1d(data, y * width, 1, width, f, v, z);
}
function edt1d(grid, offset, stride, length, f, v, z) {
    var q, k, s, r;
    v[0] = 0;
    z[0] = -INF;
    z[1] = INF;
    for (q = 0; q < length; q++)
        f[q] = grid[offset + q * stride];
    for (q = 1, k = 0, s = 0; q < length; q++) {
        do {
            r = v[k];
            s = (f[q] - f[r] + q * q - r * r) / (q - r) / 2;
        } while (s <= z[k] && --k > -1);
        k++;
        v[k] = q;
        z[k] = s;
        z[k + 1] = INF;
    }
    for (q = 0, k = 0; q < length; q++) {
        while (z[k + 1] < q)
            k++;
        r = v[k];
        grid[offset + q * stride] = f[r] + (q - r) * (q - r);
    }
}
tinySdf.default = _default;

const SDF_SCALE = 2;
const LocalGlyphMode = {
    none: 0,
    ideographs: 1,
    all: 2
};
class GlyphManager {
    constructor(requestManager, localGlyphMode, localFontFamily) {
        this.requestManager = requestManager;
        this.localGlyphMode = localGlyphMode;
        this.localFontFamily = localFontFamily;
        this.entries = {};
        this.localGlyphs = {
            '200': {},
            '400': {},
            '500': {},
            '900': {}
        };
    }
    setURL(url) {
        this.url = url;
    }
    getGlyphs(glyphs, callback) {
        const all = [];
        for (const stack in glyphs) {
            for (const id of glyphs[stack]) {
                all.push({
                    stack,
                    id
                });
            }
        }
        asyncAll(all, ({stack, id}, callback) => {
            let entry = this.entries[stack];
            if (!entry) {
                entry = this.entries[stack] = {
                    glyphs: {},
                    requests: {},
                    ranges: {}
                };
            }
            let glyph = entry.glyphs[id];
            if (glyph !== undefined) {
                callback(null, {
                    stack,
                    id,
                    glyph
                });
                return;
            }
            glyph = this._tinySDF(entry, stack, id);
            if (glyph) {
                entry.glyphs[id] = glyph;
                callback(null, {
                    stack,
                    id,
                    glyph
                });
                return;
            }
            const range = Math.floor(id / 256);
            if (range * 256 > 65535) {
                callback(new Error('glyphs > 65535 not supported'));
                return;
            }
            if (entry.ranges[range]) {
                callback(null, {
                    stack,
                    id,
                    glyph
                });
                return;
            }
            let requests = entry.requests[range];
            if (!requests) {
                requests = entry.requests[range] = [];
                GlyphManager.loadGlyphRange(stack, range, this.url, this.requestManager, (err, response) => {
                    if (response) {
                        for (const id in response) {
                            if (!this._doesCharSupportLocalGlyph(+id)) {
                                entry.glyphs[+id] = response[+id];
                            }
                        }
                        entry.ranges[range] = true;
                    }
                    for (const cb of requests) {
                        cb(err, response);
                    }
                    delete entry.requests[range];
                });
            }
            requests.push((err, result) => {
                if (err) {
                    callback(err);
                } else if (result) {
                    callback(null, {
                        stack,
                        id,
                        glyph: result[id] || null
                    });
                }
            });
        }, (err, glyphs) => {
            if (err) {
                callback(err);
            } else if (glyphs) {
                const result = {};
                for (const {stack, id, glyph} of glyphs) {
                    (result[stack] || (result[stack] = {}))[id] = glyph && {
                        id: glyph.id,
                        bitmap: glyph.bitmap.clone(),
                        metrics: glyph.metrics
                    };
                }
                callback(null, result);
            }
        });
    }
    _doesCharSupportLocalGlyph(id) {
        if (this.localGlyphMode === LocalGlyphMode.none) {
            return false;
        } else if (this.localGlyphMode === LocalGlyphMode.all) {
            return !!this.localFontFamily;
        } else {
            return !!this.localFontFamily && (unicodeBlockLookup['CJK Unified Ideographs'](id) || unicodeBlockLookup['Hangul Syllables'](id) || unicodeBlockLookup['Hiragana'](id) || unicodeBlockLookup['Katakana'](id));
        }
    }
    _tinySDF(entry, stack, id) {
        const family = this.localFontFamily;
        if (!family) {
            return;
        }
        if (!this._doesCharSupportLocalGlyph(id)) {
            return;
        }
        let tinySDF = entry.tinySDF;
        if (!tinySDF) {
            let fontWeight = '400';
            if (/bold/i.test(stack)) {
                fontWeight = '900';
            } else if (/medium/i.test(stack)) {
                fontWeight = '500';
            } else if (/light/i.test(stack)) {
                fontWeight = '200';
            }
            tinySDF = entry.tinySDF = new GlyphManager.TinySDF(24 * SDF_SCALE, 3 * SDF_SCALE, 8 * SDF_SCALE, 0.25, family, fontWeight);
        }
        if (this.localGlyphs[tinySDF.fontWeight][id]) {
            return this.localGlyphs[tinySDF.fontWeight][id];
        }
        const {data, metrics} = tinySDF.drawWithMetrics(String.fromCharCode(id));
        const {sdfWidth, sdfHeight, width, height, left, top, advance} = metrics;
        const baselineAdjustment = 27;
        const glyph = this.localGlyphs[tinySDF.fontWeight][id] = {
            id,
            bitmap: new AlphaImage({
                width: sdfWidth,
                height: sdfHeight
            }, data),
            metrics: {
                width: width / SDF_SCALE,
                height: height / SDF_SCALE,
                left: left / SDF_SCALE,
                top: top / SDF_SCALE - baselineAdjustment,
                advance: advance / SDF_SCALE,
                localGlyph: true
            }
        };
        return glyph;
    }
}
GlyphManager.loadGlyphRange = loadGlyphRange;
GlyphManager.TinySDF = tinySdf;

const border = IMAGE_PADDING;
function getIconQuads(shapedIcon, iconRotate, isSDFIcon, hasIconTextFit) {
    const quads = [];
    const image = shapedIcon.image;
    const pixelRatio = image.pixelRatio;
    const imageWidth = image.paddedRect.w - 2 * border;
    const imageHeight = image.paddedRect.h - 2 * border;
    const iconWidth = shapedIcon.right - shapedIcon.left;
    const iconHeight = shapedIcon.bottom - shapedIcon.top;
    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;
        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 = content[2] - content[0] - stretchContentWidth;
        fixedContentHeight = content[3] - content[1] - stretchContentHeight;
    }
    const makeBox = (left, top, right, bottom) => {
        const leftEm = getEmOffset(left.stretch - stretchOffsetX, stretchContentWidth, iconWidth, shapedIcon.left);
        const leftPx = getPxOffset(left.fixed - fixedOffsetX, fixedContentWidth, left.stretch, stretchWidth);
        const topEm = getEmOffset(top.stretch - stretchOffsetY, stretchContentHeight, iconHeight, shapedIcon.top);
        const topPx = getPxOffset(top.fixed - fixedOffsetY, fixedContentHeight, top.stretch, stretchHeight);
        const rightEm = getEmOffset(right.stretch - stretchOffsetX, stretchContentWidth, iconWidth, shapedIcon.left);
        const rightPx = getPxOffset(right.fixed - fixedOffsetX, fixedContentWidth, right.stretch, stretchWidth);
        const bottomEm = getEmOffset(bottom.stretch - stretchOffsetY, stretchContentHeight, iconHeight, shapedIcon.top);
        const bottomPx = getPxOffset(bottom.fixed - fixedOffsetY, fixedContentHeight, bottom.stretch, stretchHeight);
        const tl = new pointGeometry(leftEm, topEm);
        const tr = new pointGeometry(rightEm, topEm);
        const br = new pointGeometry(rightEm, bottomEm);
        const bl = new pointGeometry(leftEm, bottomEm);
        const pixelOffsetTL = new pointGeometry(leftPx / pixelRatio, topPx / pixelRatio);
        const pixelOffsetBR = new pointGeometry(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;
        const subRect = {
            x: image.paddedRect.x + border + x1,
            y: image.paddedRect.y + border + y1,
            w: x2 - x1,
            h: y2 - y1
        };
        const minFontScaleX = fixedContentWidth / pixelRatio / iconWidth;
        const minFontScaleY = fixedContentHeight / pixelRatio / iconHeight;
        return {
            tl,
            tr,
            bl,
            br,
            tex: subRect,
            writingMode: undefined,
            glyphOffset: [
                0,
                0
            ],
            sectionIndex: 0,
            pixelOffsetTL,
            pixelOffsetBR,
            minFontScaleX,
            minFontScaleY,
            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: -border,
            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;
}
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 || {};
            const glyphPadding = 1;
            let rectBuffer = GLYPH_PBF_BORDER + glyphPadding;
            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) * ONE_EM;
                const imageOffset = (ONE_EM - 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 = IMAGE_PADDING / 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 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 * positionedGlyph.scale / (pixelRatio * (positionedGlyph.localGlyph ? SDF_SCALE : 1));
            const y2 = y1 + textureRect.h * positionedGlyph.scale / (pixelRatio * (positionedGlyph.localGlyph ? SDF_SCALE : 1));
            const tl = new pointGeometry(x1, y1);
            const tr = new pointGeometry(x2, y1);
            const bl = new pointGeometry(x1, y2);
            const br = new pointGeometry(x2, y2);
            if (rotateVerticalGlyph) {
                const center = new pointGeometry(-halfAdvance, halfAdvance - SHAPING_DEFAULT_OFFSET);
                const verticalRotation = -Math.PI / 2;
                const xHalfWidthOffsetCorrection = ONE_EM / 2 - halfAdvance;
                const yImageOffsetCorrection = positionedGlyph.imageName ? xHalfWidthOffsetCorrection : 0;
                const halfWidthOffsetCorrection = new pointGeometry(5 - SHAPING_DEFAULT_OFFSET - xHalfWidthOffsetCorrection, -yImageOffsetCorrection);
                const verticalOffsetCorrection = new pointGeometry(...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 pointGeometry(0, 0);
            const pixelOffsetBR = new pointGeometry(0, 0);
            const minFontScaleX = 0;
            const minFontScaleY = 0;
            quads.push({
                tl,
                tr,
                bl,
                br,
                tex: textureRect,
                writingMode: shaping.writingMode,
                glyphOffset,
                sectionIndex: positionedGlyph.sectionIndex,
                isSDF,
                pixelOffsetTL,
                pixelOffsetBR,
                minFontScaleX,
                minFontScaleY
            });
        }
    }
    return quads;
}

class TinyQueue {
    constructor(data = [], compare = defaultCompare) {
        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.length++;
        this._up(this.length - 1);
    }
    pop() {
        if (this.length === 0)
            return undefined;
        const top = this.data[0];
        const bottom = this.data.pop();
        this.length--;
        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 left = (pos << 1) + 1;
            let best = data[left];
            const right = left + 1;
            if (right < this.length && compare(data[right], best) < 0) {
                left = right;
                best = data[right];
            }
            if (compare(best, item) >= 0)
                break;
            data[pos] = best;
            pos = left;
        }
        data[pos] = item;
    }
}
function defaultCompare(a, b) {
    return a < b ? -1 : a > b ? 1 : 0;
}

function findPoleOfInaccessibility (polygonRings, precision = 1, debug = false) {
    let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
    const outerRing = polygonRings[0];
    for (let i = 0; i < outerRing.length; i++) {
        const p = outerRing[i];
        if (!i || p.x < minX)
            minX = p.x;
        if (!i || p.y < minY)
            minY = p.y;
        if (!i || p.x > maxX)
            maxX = p.x;
        if (!i || p.y > maxY)
            maxY = p.y;
    }
    const width = maxX - minX;
    const height = maxY - minY;
    const cellSize = Math.min(width, height);
    let h = cellSize / 2;
    const cellQueue = new TinyQueue([], compareMax);
    if (cellSize === 0)
        return new pointGeometry(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));
        }
    }
    let bestCell = getCentroidCell(polygonRings);
    let numProbes = cellQueue.length;
    while (cellQueue.length) {
        const cell = cellQueue.pop();
        if (cell.d > bestCell.d || !bestCell.d) {
            bestCell = cell;
            if (debug)
                console.log('found best %d after %d probes', Math.round(10000 * cell.d) / 10000, numProbes);
        }
        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));
        numProbes += 4;
    }
    if (debug) {
        console.log(`num probes: ${ numProbes }`);
        console.log(`best distance: ${ bestCell.d }`);
    }
    return bestCell.p;
}
function compareMax(a, b) {
    return b.max - a.max;
}
function Cell(x, y, h, polygon) {
    this.p = new pointGeometry(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 (let k = 0; k < polygon.length; k++) {
        const ring = polygon[k];
        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);
}

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.sqrt(2);
        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;
            break;
        }
        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;
            break;
        }
        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;
            break;
        }
        switch (anchor) {
        case 'top-right':
        case 'bottom-right':
        case 'right':
            x = -offsetX;
            break;
        case 'top-left':
        case 'bottom-left':
        case 'left':
            x = offsetX;
            break;
        }
        return [
            x,
            y
        ];
    }
    return offset[1] !== INVALID_TEXT_OFFSET ? fromTextOffset(anchor, offset[0], offset[1]) : fromRadialOffset(anchor, offset[0]);
}
function performSymbolLayout(bucket, glyphMap, glyphPositions, imageMap, imagePositions, showCollisionBoxes, canonical, tileZoom) {
    bucket.createArrays();
    const tileSize = 512 * bucket.overscaling;
    bucket.tilePixelRatio = EXTENT / tileSize;
    bucket.compareText = {};
    bucket.iconsNeedLinear = false;
    const layout = bucket.layers[0].layout;
    const unevaluatedLayoutValues = bucket.layers[0]._unevaluatedLayout._values;
    const sizes = {};
    if (bucket.textSizeData.kind === 'composite') {
        const {minZoom, maxZoom} = bucket.textSizeData;
        sizes.compositeTextSizes = [
            unevaluatedLayoutValues['text-size'].possiblyEvaluate(new EvaluationParameters(minZoom), canonical),
            unevaluatedLayoutValues['text-size'].possiblyEvaluate(new EvaluationParameters(maxZoom), canonical)
        ];
    }
    if (bucket.iconSizeData.kind === 'composite') {
        const {minZoom, maxZoom} = bucket.iconSizeData;
        sizes.compositeIconSizes = [
            unevaluatedLayoutValues['icon-size'].possiblyEvaluate(new EvaluationParameters(minZoom), canonical),
            unevaluatedLayoutValues['icon-size'].possiblyEvaluate(new EvaluationParameters(maxZoom), canonical)
        ];
    }
    sizes.layoutTextSize = unevaluatedLayoutValues['text-size'].possiblyEvaluate(new EvaluationParameters(tileZoom + 1), canonical);
    sizes.layoutIconSize = unevaluatedLayoutValues['icon-size'].possiblyEvaluate(new EvaluationParameters(tileZoom + 1), canonical);
    sizes.textMaxSize = unevaluatedLayoutValues['text-size'].possiblyEvaluate(new EvaluationParameters(18), canonical);
    const textAlongLine = layout.get('text-rotation-alignment') === 'map' && layout.get('symbol-placement') !== 'point';
    const keepUpright = layout.get('text-keep-upright');
    const textSize = layout.get('text-size');
    for (const feature of bucket.features) {
        const fontstack = layout.get('text-font').evaluate(feature, {}, canonical).join(',');
        const layoutTextSizeThisZoom = textSize.evaluate(feature, {}, canonical);
        const layoutTextSize = sizes.layoutTextSize.evaluate(feature, {}, canonical);
        const layoutIconSize = sizes.layoutIconSize.evaluate(feature, {}, canonical);
        const shapedTextOrientations = {
            horizontal: {},
            vertical: undefined
        };
        const text = feature.text;
        let textOffset = [
            0,
            0
        ];
        if (text) {
            const unformattedText = text.toString();
            const spacing = layout.get('text-letter-spacing').evaluate(feature, {}, canonical) * ONE_EM;
            const lineHeight = layout.get('text-line-height').evaluate(feature, {}, canonical) * ONE_EM;
            const spacingIfAllowed = allowsLetterSpacing(unformattedText) ? spacing : 0;
            const textAnchor = layout.get('text-anchor').evaluate(feature, {}, canonical);
            const variableTextAnchor = layout.get('text-variable-anchor');
            if (!variableTextAnchor) {
                const radialOffset = layout.get('text-radial-offset').evaluate(feature, {}, canonical);
                if (radialOffset) {
                    textOffset = evaluateVariableOffset(textAnchor, [
                        radialOffset * ONE_EM,
                        INVALID_TEXT_OFFSET
                    ]);
                } else {
                    textOffset = layout.get('text-offset').evaluate(feature, {}, canonical).map(t => t * ONE_EM);
                }
            }
            let textJustify = textAlongLine ? 'center' : layout.get('text-justify').evaluate(feature, {}, canonical);
            const symbolPlacement = layout.get('symbol-placement');
            const maxWidth = symbolPlacement === 'point' ? layout.get('text-max-width').evaluate(feature, {}, canonical) * ONE_EM : 0;
            const addVerticalShapingForPointLabelIfNeeded = () => {
                if (bucket.allowVerticalPlacement && allowsVerticalWritingMode(unformattedText)) {
                    shapedTextOrientations.vertical = shapeText(text, glyphMap, glyphPositions, imagePositions, fontstack, maxWidth, lineHeight, textAnchor, 'left', spacingIfAllowed, textOffset, WritingMode.vertical, true, symbolPlacement, layoutTextSize, layoutTextSizeThisZoom);
                }
            };
            if (!textAlongLine && variableTextAnchor) {
                const justifications = textJustify === 'auto' ? variableTextAnchor.map(a => getAnchorJustification(a)) : [textJustify];
                let singleLine = false;
                for (let i = 0; i < justifications.length; i++) {
                    const justification = justifications[i];
                    if (shapedTextOrientations.horizontal[justification])
                        continue;
                    if (singleLine) {
                        shapedTextOrientations.horizontal[justification] = shapedTextOrientations.horizontal[0];
                    } else {
                        const shaping = shapeText(text, glyphMap, glyphPositions, imagePositions, fontstack, maxWidth, lineHeight, 'center', justification, spacingIfAllowed, textOffset, WritingMode.horizontal, false, symbolPlacement, 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, glyphMap, glyphPositions, imagePositions, fontstack, maxWidth, lineHeight, textAnchor, textJustify, spacingIfAllowed, textOffset, WritingMode.horizontal, false, symbolPlacement, layoutTextSize, layoutTextSizeThisZoom);
                if (shaping)
                    shapedTextOrientations.horizontal[textJustify] = shaping;
                addVerticalShapingForPointLabelIfNeeded();
                if (allowsVerticalWritingMode(unformattedText) && textAlongLine && keepUpright) {
                    shapedTextOrientations.vertical = shapeText(text, glyphMap, glyphPositions, imagePositions, fontstack, maxWidth, lineHeight, textAnchor, textJustify, spacingIfAllowed, textOffset, WritingMode.vertical, false, symbolPlacement, layoutTextSize, layoutTextSizeThisZoom);
                }
            }
        }
        let shapedIcon;
        let isSDFIcon = false;
        if (feature.icon && feature.icon.name) {
            const image = imageMap[feature.icon.name];
            if (image) {
                shapedIcon = shapeIcon(imagePositions[feature.icon.name], layout.get('icon-offset').evaluate(feature, {}, canonical), layout.get('icon-anchor').evaluate(feature, {}, canonical));
                isSDFIcon = image.sdf;
                if (bucket.sdfIcons === undefined) {
                    bucket.sdfIcons = image.sdf;
                } else if (bucket.sdfIcons !== image.sdf) {
                    warnOnce('Style sheet warning: Cannot mix SDF and non-SDF icons in one buffer');
                }
                if (image.pixelRatio !== bucket.pixelRatio) {
                    bucket.iconsNeedLinear = true;
                } else if (layout.get('icon-rotate').constantOr(1) !== 0) {
                    bucket.iconsNeedLinear = true;
                }
            }
        }
        const shapedText = getDefaultHorizontalShaping(shapedTextOrientations.horizontal) || shapedTextOrientations.vertical;
        if (!bucket.iconsInText) {
            bucket.iconsInText = shapedText ? shapedText.iconsInText : false;
        }
        if (shapedText || shapedIcon) {
            addFeature(bucket, feature, shapedTextOrientations, shapedIcon, imageMap, sizes, layoutTextSize, layoutIconSize, textOffset, isSDFIcon, canonical);
        }
    }
    if (showCollisionBoxes) {
        bucket.generateCollisionDebugBuffers(tileZoom, bucket.collisionBoxArray);
    }
}
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';
}
function addFeature(bucket, feature, shapedTextOrientations, shapedIcon, imageMap, sizes, layoutTextSize, layoutIconSize, textOffset, isSDFIcon, canonical) {
    let textMaxSize = sizes.textMaxSize.evaluate(feature, {}, canonical);
    if (textMaxSize === undefined) {
        textMaxSize = layoutTextSize;
    }
    const layout = bucket.layers[0].layout;
    const iconOffset = layout.get('icon-offset').evaluate(feature, {}, canonical);
    const defaultHorizontalShaping = getDefaultHorizontalShaping(shapedTextOrientations.horizontal);
    const glyphSize = ONE_EM, 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 = layout.get('icon-padding') * bucket.tilePixelRatio, textMaxAngle = degToRad(layout.get('text-max-angle')), textAlongLine = layout.get('text-rotation-alignment') === 'map' && 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 addSymbolAtAnchor = (line, anchor) => {
        if (anchor.x < 0 || anchor.x >= EXTENT || anchor.y < 0 || anchor.y >= EXTENT) {
            return;
        }
        addSymbol(bucket, anchor, line, shapedTextOrientations, shapedIcon, imageMap, verticallyShapedIcon, bucket.layers[0], bucket.collisionBoxArray, feature.index, feature.sourceLayerIndex, bucket.index, textBoxScale, 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, EXTENT)) {
            const anchors = getAnchors(line, symbolMinDistance, textMaxAngle, shapedTextOrientations.vertical || defaultHorizontalShaping, shapedIcon, glyphSize, textMaxBoxScale, bucket.overscaling, EXTENT);
            for (const anchor of anchors) {
                const shapedText = defaultHorizontalShaping;
                if (!shapedText || !anchorIsTooClose(bucket, shapedText.text, textRepeatDistance, anchor)) {
                    addSymbolAtAnchor(line, anchor);
                }
            }
        }
    } else if (symbolPlacement === 'line-center') {
        for (const line of feature.geometry) {
            if (line.length > 1) {
                const anchor = getCenterAnchor(line, textMaxAngle, shapedTextOrientations.vertical || defaultHorizontalShaping, shapedIcon, glyphSize, textMaxBoxScale);
                if (anchor) {
                    addSymbolAtAnchor(line, anchor);
                }
            }
        }
    } else if (feature.type === 'Polygon') {
        for (const polygon of classifyRings$2(feature.geometry, 0)) {
            const poi = findPoleOfInaccessibility(polygon, 16);
            addSymbolAtAnchor(polygon[0], new Anchor(poi.x, poi.y, 0));
        }
    } else if (feature.type === 'LineString') {
        for (const line of feature.geometry) {
            addSymbolAtAnchor(line, new Anchor(line[0].x, line[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));
            }
        }
    }
}
const MAX_GLYPH_ICON_SIZE = 255;
const MAX_PACKED_SIZE = MAX_GLYPH_ICON_SIZE * SIZE_PACK_FACTOR;
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 = [SIZE_PACK_FACTOR * layer.layout.get('text-size').evaluate(feature, {}, canonical)];
        if (textSizeData[0] > MAX_PACKED_SIZE) {
            warnOnce(`${ bucket.layerIds[0] }: Value for "text-size" is >= ${ MAX_GLYPH_ICON_SIZE }. Reduce your "text-size".`);
        }
    } else if (sizeData.kind === 'composite') {
        textSizeData = [
            SIZE_PACK_FACTOR * sizes.compositeTextSizes[0].evaluate(feature, {}, canonical),
            SIZE_PACK_FACTOR * sizes.compositeTextSizes[1].evaluate(feature, {}, canonical)
        ];
        if (textSizeData[0] > MAX_PACKED_SIZE || textSizeData[1] > MAX_PACKED_SIZE) {
            warnOnce(`${ bucket.layerIds[0] }: Value for "text-size" is >= ${ MAX_GLYPH_ICON_SIZE }. 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;
}
function evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, shaped, boxScale, padding, rotate) {
    let y1 = shaped.top;
    let y2 = shaped.bottom;
    let x1 = shaped.left;
    let x2 = shaped.right;
    const collisionPadding = shaped.collisionPadding;
    if (collisionPadding) {
        x1 -= collisionPadding[0];
        y1 -= collisionPadding[1];
        x2 += collisionPadding[2];
        y2 += collisionPadding[3];
    }
    if (rotate) {
        const tl = new pointGeometry(x1, y1);
        const tr = new pointGeometry(x2, y1);
        const bl = new pointGeometry(x1, y2);
        const br = new pointGeometry(x2, y2);
        const rotateRadians = degToRad(rotate);
        tl._rotate(rotateRadians);
        tr._rotate(rotateRadians);
        bl._rotate(rotateRadians);
        br._rotate(rotateRadians);
        x1 = Math.min(tl.x, tr.x, bl.x, br.x);
        x2 = Math.max(tl.x, tr.x, bl.x, br.x);
        y1 = Math.min(tl.y, tr.y, bl.y, br.y);
        y2 = Math.max(tl.y, tr.y, bl.y, br.y);
    }
    collisionBoxArray.emplaceBack(anchor.x, anchor.y, x1, y1, x2, y2, padding, featureIndex, sourceLayerIndex, bucketIndex);
    return collisionBoxArray.length - 1;
}
function evaluateCircleCollisionFeature(shaped) {
    if (shaped.collisionPadding) {
        shaped.top -= shaped.collisionPadding[1];
        shaped.bottom += shaped.collisionPadding[3];
    }
    const height = shaped.bottom - shaped.top;
    return height > 0 ? Math.max(10, height) : null;
}
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 textBoxIndex, iconBoxIndex, verticalTextBoxIndex, verticalIconBoxIndex;
    let textCircle, verticalTextCircle, verticalIconCircle;
    let numIconVertices = 0;
    let numVerticalIconVertices = 0;
    let numHorizontalGlyphVertices = 0;
    let numVerticalGlyphVertices = 0;
    let placedIconSymbolIndex = -1;
    let verticalPlacedIconSymbolIndex = -1;
    const placedTextSymbolIndices = {};
    let key = murmurhashJs('');
    let textOffset0 = 0;
    let textOffset1 = 0;
    if (layer._unevaluatedLayout.getValue('text-radial-offset') === undefined) {
        [textOffset0, textOffset1] = layer.layout.get('text-offset').evaluate(feature, {}, canonical).map(t => t * ONE_EM);
    } else {
        textOffset0 = layer.layout.get('text-radial-offset').evaluate(feature, {}, canonical) * ONE_EM;
        textOffset1 = INVALID_TEXT_OFFSET;
    }
    if (bucket.allowVerticalPlacement && shapedTextOrientations.vertical) {
        const verticalShaping = shapedTextOrientations.vertical;
        if (textAlongLine) {
            verticalTextCircle = evaluateCircleCollisionFeature(verticalShaping);
            if (verticallyShapedIcon) {
                verticalIconCircle = evaluateCircleCollisionFeature(verticallyShapedIcon);
            }
        } else {
            const textRotation = layer.layout.get('text-rotate').evaluate(feature, {}, canonical);
            const verticalTextRotation = textRotation + 90;
            verticalTextBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, verticalShaping, textBoxScale, textPadding, verticalTextRotation);
            if (verticallyShapedIcon) {
                verticalIconBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, verticallyShapedIcon, iconBoxScale, iconPadding, verticalTextRotation);
            }
        }
    }
    if (shapedIcon) {
        const iconRotate = layer.layout.get('icon-rotate').evaluate(feature, {}, canonical);
        const hasIconTextFit = layer.layout.get('icon-text-fit') !== 'none';
        const iconQuads = getIconQuads(shapedIcon, iconRotate, isSDFIcon, hasIconTextFit);
        const verticalIconQuads = verticallyShapedIcon ? getIconQuads(verticallyShapedIcon, iconRotate, isSDFIcon, hasIconTextFit) : undefined;
        iconBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, shapedIcon, iconBoxScale, iconPadding, iconRotate);
        numIconVertices = iconQuads.length * 4;
        const sizeData = bucket.iconSizeData;
        let iconSizeData = null;
        if (sizeData.kind === 'source') {
            iconSizeData = [SIZE_PACK_FACTOR * layer.layout.get('icon-size').evaluate(feature, {}, canonical)];
            if (iconSizeData[0] > MAX_PACKED_SIZE) {
                warnOnce(`${ bucket.layerIds[0] }: Value for "icon-size" is >= ${ MAX_GLYPH_ICON_SIZE }. Reduce your "icon-size".`);
            }
        } else if (sizeData.kind === 'composite') {
            iconSizeData = [
                SIZE_PACK_FACTOR * sizes.compositeIconSizes[0].evaluate(feature, {}, canonical),
                SIZE_PACK_FACTOR * sizes.compositeIconSizes[1].evaluate(feature, {}, canonical)
            ];
            if (iconSizeData[0] > MAX_PACKED_SIZE || iconSizeData[1] > MAX_PACKED_SIZE) {
                warnOnce(`${ bucket.layerIds[0] }: Value for "icon-size" is >= ${ MAX_GLYPH_ICON_SIZE }. Reduce your "icon-size".`);
            }
        }
        bucket.addSymbols(bucket.icon, iconQuads, iconSizeData, iconOffset, iconAlongLine, feature, false, 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, WritingMode.vertical, anchor, lineArray.lineStartIndex, lineArray.lineLength, -1, canonical);
            verticalPlacedIconSymbolIndex = bucket.icon.placedSymbolArray.length - 1;
        }
    }
    for (const justification in shapedTextOrientations.horizontal) {
        const shaping = shapedTextOrientations.horizontal[justification];
        if (!textBoxIndex) {
            key = murmurhashJs(shaping.text);
            if (textAlongLine) {
                textCircle = evaluateCircleCollisionFeature(shaping);
            } else {
                const textRotate = layer.layout.get('text-rotate').evaluate(feature, {}, canonical);
                textBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, shaping, textBoxScale, textPadding, textRotate);
            }
        }
        const singleLine = shaping.positionedLines.length === 1;
        numHorizontalGlyphVertices += addTextVertices(bucket, anchor, shaping, imageMap, layer, textAlongLine, feature, textOffset, lineArray, shapedTextOrientations.vertical ? WritingMode.horizontal : WritingMode.horizontalOnly, singleLine ? Object.keys(shapedTextOrientations.horizontal) : [justification], placedTextSymbolIndices, placedIconSymbolIndex, sizes, canonical);
        if (singleLine) {
            break;
        }
    }
    if (shapedTextOrientations.vertical) {
        numVerticalGlyphVertices += addTextVertices(bucket, anchor, shapedTextOrientations.vertical, imageMap, layer, textAlongLine, feature, textOffset, lineArray, WritingMode.vertical, ['vertical'], placedTextSymbolIndices, verticalPlacedIconSymbolIndex, sizes, canonical);
    }
    let collisionCircleDiameter = -1;
    const getCollisionCircleHeight = (diameter, prevHeight) => {
        return diameter ? Math.max(diameter, prevHeight) : prevHeight;
    };
    collisionCircleDiameter = getCollisionCircleHeight(textCircle, collisionCircleDiameter);
    collisionCircleDiameter = getCollisionCircleHeight(verticalTextCircle, collisionCircleDiameter);
    collisionCircleDiameter = getCollisionCircleHeight(verticalIconCircle, collisionCircleDiameter);
    const useRuntimeCollisionCircles = collisionCircleDiameter > -1 ? 1 : 0;
    if (useRuntimeCollisionCircles)
        collisionCircleDiameter *= layoutTextSize / ONE_EM;
    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 !== undefined) {
        bucket.addToSortKeyRanges(bucket.symbolInstances.length, feature.sortKey);
    }
    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, textBoxIndex !== undefined ? textBoxIndex : bucket.collisionBoxArray.length, textBoxIndex !== undefined ? textBoxIndex + 1 : bucket.collisionBoxArray.length, verticalTextBoxIndex !== undefined ? verticalTextBoxIndex : bucket.collisionBoxArray.length, verticalTextBoxIndex !== undefined ? verticalTextBoxIndex + 1 : bucket.collisionBoxArray.length, iconBoxIndex !== undefined ? iconBoxIndex : bucket.collisionBoxArray.length, iconBoxIndex !== undefined ? iconBoxIndex + 1 : bucket.collisionBoxArray.length, verticalIconBoxIndex ? verticalIconBoxIndex : bucket.collisionBoxArray.length, verticalIconBoxIndex ? verticalIconBoxIndex + 1 : bucket.collisionBoxArray.length, featureIndex, numHorizontalGlyphVertices, numVerticalGlyphVertices, numIconVertices, numVerticalIconVertices, useRuntimeCollisionCircles, 0, textBoxScale, textOffset0, textOffset1, collisionCircleDiameter);
}
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;
}

const vectorTileFeatureTypes = vectorTile.VectorTileFeature.types;
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;
}
class SymbolBuffers {
    constructor(programConfigurations) {
        this.layoutVertexArray = new StructArrayLayout4i4ui4i24();
        this.indexArray = new StructArrayLayout3ui6();
        this.programConfigurations = programConfigurations;
        this.segments = new SegmentVector();
        this.dynamicLayoutVertexArray = new StructArrayLayout3f12();
        this.opacityVertexArray = new StructArrayLayout1ul4();
        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);
class CollisionBuffers {
    constructor(LayoutArray, layoutAttributes, IndexArray) {
        this.layoutVertexArray = new LayoutArray();
        this.layoutAttributes = layoutAttributes;
        this.indexArray = new IndexArray();
        this.segments = new SegmentVector();
        this.collisionVertexArray = new StructArrayLayout2ub2f12();
        this.collisionVertexArrayExt = new StructArrayLayout3f12();
    }
    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);
        this.collisionVertexBufferExt = context.createVertexBuffer(this.collisionVertexArrayExt, collisionVertexAttributesExt.members, true);
    }
    destroy() {
        if (!this.layoutVertexBuffer)
            return;
        this.layoutVertexBuffer.destroy();
        this.indexBuffer.destroy();
        this.segments.destroy();
        this.collisionVertexBuffer.destroy();
        this.collisionVertexBufferExt.destroy();
    }
}
register('CollisionBuffers', CollisionBuffers);
class SymbolBucket {
    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.hasPattern = false;
        this.hasRTLText = false;
        this.sortKeyRanges = [];
        this.collisionCircleArray = [];
        this.placementInvProjMatrix = identity$1([]);
        this.placementViewportMatrix = identity$1([]);
        const layer = this.layers[0];
        const unevaluatedLayoutValues = layer._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 = layout.get('text-allow-overlap') || layout.get('icon-allow-overlap') || layout.get('text-ignore-placement') || layout.get('icon-ignore-placement');
        this.sortFeaturesByKey = zOrder !== 'viewport-y' && sortKey.constantOr(1) !== undefined;
        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 => /^text/.test(property)));
        this.icon = new SymbolBuffers(new ProgramConfigurationSet(this.layers, this.zoom, property => /^icon/.test(property)));
        this.glyphOffsetArray = new GlyphOffsetArray();
        this.lineVertexArray = new SymbolLineVertexArray();
        this.symbolInstances = new SymbolInstanceArray();
    }
    calculateGlyphDependencies(text, stack, textAlongLine, allowVerticalPlacement, doesAllowVerticalWritingMode) {
        for (let i = 0; i < text.length; i++) {
            stack[text.charCodeAt(i)] = true;
            if ((textAlongLine || allowVerticalPlacement) && doesAllowVerticalWritingMode) {
                const verticalChar = verticalizedCharacterMap[text.charAt(i)];
                if (verticalChar) {
                    stack[verticalChar.charCodeAt(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);
                if (containsRTLText(formattedText)) {
                    this.hasRTLText = true;
                }
                if (!this.hasRTLText || getRTLTextPluginStatus() === 'unavailable' || this.hasRTLText && plugin.isParsed()) {
                    text = transformText$1(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) : undefined;
            const symbolFeature = {
                id,
                text,
                icon,
                index,
                sourceLayerIndex,
                geometry: evaluationFeature.geometry,
                properties: feature.properties,
                type: vectorTileFeatureTypes[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') === 'map' && layout.get('symbol-placement') !== 'point';
                this.allowVerticalPlacement = this.writingModes && this.writingModes.indexOf(WritingMode.vertical) >= 0;
                for (const section of text.sections) {
                    if (!section.image) {
                        const doesAllowVerticalWritingMode = allowsVerticalWritingMode(text.toString());
                        const sectionFont = section.fontStack || fontStack;
                        const sectionStack = stacks[sectionFont] = stacks[sectionFont] || {};
                        this.calculateGlyphDependencies(section.text, sectionStack, 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 !== undefined) {
            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 : undefined);
        const glyphOffsetArrayStart = this.glyphOffsetArray.length;
        const vertexStartIndex = segment.vertexLength;
        const angle = this.allowVerticalPlacement && writingMode === WritingMode.vertical ? 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 + 1, index + 2);
            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, {}, canonical, sections && 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);
    }
    _commitLayoutVertex(array, point, anchorX, anchorY, extrude) {
        array.emplaceBack(point.x, point.y, anchorX, anchorY, Math.round(extrude.x), Math.round(extrude.y));
    }
    _addCollisionDebugVertices(box, scale, arrays, boxAnchorPoint, symbolInstance) {
        const segment = arrays.segments.prepareSegment(4, arrays.layoutVertexArray, arrays.indexArray);
        const index = segment.vertexLength;
        const anchorX = symbolInstance.anchorX;
        const anchorY = symbolInstance.anchorY;
        for (let i = 0; i < 4; i++) {
            arrays.collisionVertexArray.emplaceBack(0, 0, 0, 0);
        }
        arrays.collisionVertexArrayExt.emplaceBack(scale, -box.padding, -box.padding);
        arrays.collisionVertexArrayExt.emplaceBack(scale, box.padding, -box.padding);
        arrays.collisionVertexArrayExt.emplaceBack(scale, box.padding, box.padding);
        arrays.collisionVertexArrayExt.emplaceBack(scale, -box.padding, box.padding);
        this._commitLayoutVertex(arrays.layoutVertexArray, boxAnchorPoint, anchorX, anchorY, new pointGeometry(box.x1, box.y1));
        this._commitLayoutVertex(arrays.layoutVertexArray, boxAnchorPoint, anchorX, anchorY, new pointGeometry(box.x2, box.y1));
        this._commitLayoutVertex(arrays.layoutVertexArray, boxAnchorPoint, anchorX, anchorY, new pointGeometry(box.x2, box.y2));
        this._commitLayoutVertex(arrays.layoutVertexArray, boxAnchorPoint, anchorX, anchorY, new pointGeometry(box.x1, box.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;
    }
    _addTextDebugCollisionBoxes(size, zoom, collisionBoxArray, startIndex, endIndex, instance) {
        for (let b = startIndex; b < endIndex; b++) {
            const box = collisionBoxArray.get(b);
            const scale = this.getSymbolInstanceTextSize(size, instance, zoom, b);
            this._addCollisionDebugVertices(box, scale, this.textCollisionBox, box.anchorPoint, instance);
        }
    }
    _addIconDebugCollisionBoxes(size, zoom, collisionBoxArray, startIndex, endIndex, instance) {
        for (let b = startIndex; b < endIndex; b++) {
            const box = collisionBoxArray.get(b);
            const scale = this.getSymbolInstanceIconSize(size, zoom, b);
            this._addCollisionDebugVertices(box, scale, this.iconCollisionBox, box.anchorPoint, instance);
        }
    }
    generateCollisionDebugBuffers(zoom, collisionBoxArray) {
        if (this.hasDebugData()) {
            this.destroyDebugData();
        }
        this.textCollisionBox = new CollisionBuffers(StructArrayLayout2i2i2i12, collisionBoxLayout.members, StructArrayLayout2ui4);
        this.iconCollisionBox = new CollisionBuffers(StructArrayLayout2i2i2i12, collisionBoxLayout.members, StructArrayLayout2ui4);
        const iconSize = evaluateSizeForZoom(this.iconSizeData, zoom);
        const textSize = evaluateSizeForZoom(this.textSizeData, zoom);
        for (let i = 0; i < this.symbolInstances.length; i++) {
            const symbolInstance = this.symbolInstances.get(i);
            this._addTextDebugCollisionBoxes(textSize, zoom, collisionBoxArray, symbolInstance.textBoxStartIndex, symbolInstance.textBoxEndIndex, symbolInstance);
            this._addTextDebugCollisionBoxes(textSize, zoom, collisionBoxArray, symbolInstance.verticalTextBoxStartIndex, symbolInstance.verticalTextBoxEndIndex, symbolInstance);
            this._addIconDebugCollisionBoxes(iconSize, zoom, collisionBoxArray, symbolInstance.iconBoxStartIndex, symbolInstance.iconBoxEndIndex, symbolInstance);
            this._addIconDebugCollisionBoxes(iconSize, zoom, collisionBoxArray, symbolInstance.verticalIconBoxStartIndex, symbolInstance.verticalIconBoxEndIndex, symbolInstance);
        }
    }
    getSymbolInstanceTextSize(textSize, instance, zoom, boxIndex) {
        const symbolIndex = instance.rightJustifiedTextSymbolIndex >= 0 ? instance.rightJustifiedTextSymbolIndex : instance.centerJustifiedTextSymbolIndex >= 0 ? instance.centerJustifiedTextSymbolIndex : instance.leftJustifiedTextSymbolIndex >= 0 ? instance.leftJustifiedTextSymbolIndex : instance.verticalPlacedTextSymbolIndex >= 0 ? instance.verticalPlacedTextSymbolIndex : boxIndex;
        const symbol = this.text.placedSymbolArray.get(symbolIndex);
        const featureSize = evaluateSizeForFeature(this.textSizeData, textSize, symbol) / ONE_EM;
        return this.tilePixelRatio * featureSize;
    }
    getSymbolInstanceIconSize(iconSize, zoom, index) {
        const symbol = this.icon.placedSymbolArray.get(index);
        const featureSize = evaluateSizeForFeature(this.iconSizeData, iconSize, symbol);
        return this.tilePixelRatio * featureSize;
    }
    _commitDebugCollisionVertexUpdate(array, scale, padding) {
        array.emplaceBack(scale, -padding, -padding);
        array.emplaceBack(scale, padding, -padding);
        array.emplaceBack(scale, padding, padding);
        array.emplaceBack(scale, -padding, padding);
    }
    _updateTextDebugCollisionBoxes(size, zoom, collisionBoxArray, startIndex, endIndex, instance) {
        for (let b = startIndex; b < endIndex; b++) {
            const box = collisionBoxArray.get(b);
            const scale = this.getSymbolInstanceTextSize(size, instance, zoom, b);
            const array = this.textCollisionBox.collisionVertexArrayExt;
            this._commitDebugCollisionVertexUpdate(array, scale, box.padding);
        }
    }
    _updateIconDebugCollisionBoxes(size, zoom, collisionBoxArray, startIndex, endIndex) {
        for (let b = startIndex; b < endIndex; b++) {
            const box = collisionBoxArray.get(b);
            const scale = this.getSymbolInstanceIconSize(size, zoom, b);
            const array = this.iconCollisionBox.collisionVertexArrayExt;
            this._commitDebugCollisionVertexUpdate(array, scale, box.padding);
        }
    }
    updateCollisionDebugBuffers(zoom, collisionBoxArray) {
        if (!this.hasDebugData()) {
            return;
        }
        if (this.hasTextCollisionBoxData())
            this.textCollisionBox.collisionVertexArrayExt.clear();
        if (this.hasIconCollisionBoxData())
            this.iconCollisionBox.collisionVertexArrayExt.clear();
        const iconSize = evaluateSizeForZoom(this.iconSizeData, zoom);
        const textSize = evaluateSizeForZoom(this.textSizeData, zoom);
        for (let i = 0; i < this.symbolInstances.length; i++) {
            const symbolInstance = this.symbolInstances.get(i);
            this._updateTextDebugCollisionBoxes(textSize, zoom, collisionBoxArray, symbolInstance.textBoxStartIndex, symbolInstance.textBoxEndIndex, symbolInstance);
            this._updateTextDebugCollisionBoxes(textSize, zoom, collisionBoxArray, symbolInstance.verticalTextBoxStartIndex, symbolInstance.verticalTextBoxEndIndex, symbolInstance);
            this._updateIconDebugCollisionBoxes(iconSize, zoom, collisionBoxArray, symbolInstance.iconBoxStartIndex, symbolInstance.iconBoxEndIndex);
            this._updateIconDebugCollisionBoxes(iconSize, zoom, collisionBoxArray, symbolInstance.verticalIconBoxStartIndex, symbolInstance.verticalIconBoxEndIndex);
        }
        if (this.hasTextCollisionBoxData() && this.textCollisionBox.collisionVertexBufferExt) {
            this.textCollisionBox.collisionVertexBufferExt.updateData(this.textCollisionBox.collisionVertexArrayExt);
        }
        if (this.hasIconCollisionBoxData() && this.iconCollisionBox.collisionVertexBufferExt) {
            this.iconCollisionBox.collisionVertexBufferExt.updateData(this.iconCollisionBox.collisionVertexArrayExt);
        }
    }
    _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,
                padding: box.padding,
                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,
                padding: box.padding,
                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,
                padding: box.padding,
                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,
                padding: box.padding,
                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 + 1, vertexIndex + 2);
            iconOrText.indexArray.emplaceBack(vertexIndex + 1, vertexIndex + 2, vertexIndex + 3);
        }
    }
    getSortedSymbolIndexes(angle) {
        if (this.sortedAngle === angle && this.symbolInstanceIndexes !== undefined) {
            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 && 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);
            [
                symbolInstance.rightJustifiedTextSymbolIndex,
                symbolInstance.centerJustifiedTextSymbolIndex,
                symbolInstance.leftJustifiedTextSymbolIndex
            ].forEach((index, i, array) => {
                if (index >= 0 && array.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;

function resolveTokens(properties, text) {
    return text.replace(/{([^{}]+)}/g, (match, key) => {
        return key in properties ? String(properties[key]) : '';
    });
}

const layout = new Properties({
    'symbol-placement': new DataConstantProperty(spec['layout_symbol']['symbol-placement']),
    'symbol-spacing': new DataConstantProperty(spec['layout_symbol']['symbol-spacing']),
    'symbol-avoid-edges': new DataConstantProperty(spec['layout_symbol']['symbol-avoid-edges']),
    'symbol-sort-key': new DataDrivenProperty(spec['layout_symbol']['symbol-sort-key']),
    'symbol-z-order': new DataConstantProperty(spec['layout_symbol']['symbol-z-order']),
    'icon-allow-overlap': new DataConstantProperty(spec['layout_symbol']['icon-allow-overlap']),
    'icon-ignore-placement': new DataConstantProperty(spec['layout_symbol']['icon-ignore-placement']),
    'icon-optional': new DataConstantProperty(spec['layout_symbol']['icon-optional']),
    'icon-rotation-alignment': new DataConstantProperty(spec['layout_symbol']['icon-rotation-alignment']),
    'icon-size': new DataDrivenProperty(spec['layout_symbol']['icon-size']),
    'icon-text-fit': new DataConstantProperty(spec['layout_symbol']['icon-text-fit']),
    'icon-text-fit-padding': new DataConstantProperty(spec['layout_symbol']['icon-text-fit-padding']),
    'icon-image': new DataDrivenProperty(spec['layout_symbol']['icon-image']),
    'icon-rotate': new DataDrivenProperty(spec['layout_symbol']['icon-rotate']),
    'icon-padding': new DataConstantProperty(spec['layout_symbol']['icon-padding']),
    'icon-keep-upright': new DataConstantProperty(spec['layout_symbol']['icon-keep-upright']),
    'icon-offset': new DataDrivenProperty(spec['layout_symbol']['icon-offset']),
    'icon-anchor': new DataDrivenProperty(spec['layout_symbol']['icon-anchor']),
    'icon-pitch-alignment': new DataConstantProperty(spec['layout_symbol']['icon-pitch-alignment']),
    'text-pitch-alignment': new DataConstantProperty(spec['layout_symbol']['text-pitch-alignment']),
    'text-rotation-alignment': new DataConstantProperty(spec['layout_symbol']['text-rotation-alignment']),
    'text-field': new DataDrivenProperty(spec['layout_symbol']['text-field']),
    'text-font': new DataDrivenProperty(spec['layout_symbol']['text-font']),
    'text-size': new DataDrivenProperty(spec['layout_symbol']['text-size']),
    'text-max-width': new DataDrivenProperty(spec['layout_symbol']['text-max-width']),
    'text-line-height': new DataDrivenProperty(spec['layout_symbol']['text-line-height']),
    'text-letter-spacing': new DataDrivenProperty(spec['layout_symbol']['text-letter-spacing']),
    'text-justify': new DataDrivenProperty(spec['layout_symbol']['text-justify']),
    'text-radial-offset': new DataDrivenProperty(spec['layout_symbol']['text-radial-offset']),
    'text-variable-anchor': new DataConstantProperty(spec['layout_symbol']['text-variable-anchor']),
    'text-anchor': new DataDrivenProperty(spec['layout_symbol']['text-anchor']),
    'text-max-angle': new DataConstantProperty(spec['layout_symbol']['text-max-angle']),
    'text-writing-mode': new DataConstantProperty(spec['layout_symbol']['text-writing-mode']),
    'text-rotate': new DataDrivenProperty(spec['layout_symbol']['text-rotate']),
    'text-padding': new DataConstantProperty(spec['layout_symbol']['text-padding']),
    'text-keep-upright': new DataConstantProperty(spec['layout_symbol']['text-keep-upright']),
    'text-transform': new DataDrivenProperty(spec['layout_symbol']['text-transform']),
    'text-offset': new DataDrivenProperty(spec['layout_symbol']['text-offset']),
    'text-allow-overlap': new DataConstantProperty(spec['layout_symbol']['text-allow-overlap']),
    'text-ignore-placement': new DataConstantProperty(spec['layout_symbol']['text-ignore-placement']),
    'text-optional': new DataConstantProperty(spec['layout_symbol']['text-optional'])
});
const paint$3 = new Properties({
    'icon-opacity': new DataDrivenProperty(spec['paint_symbol']['icon-opacity']),
    'icon-color': new DataDrivenProperty(spec['paint_symbol']['icon-color']),
    'icon-halo-color': new DataDrivenProperty(spec['paint_symbol']['icon-halo-color']),
    'icon-halo-width': new DataDrivenProperty(spec['paint_symbol']['icon-halo-width']),
    'icon-halo-blur': new DataDrivenProperty(spec['paint_symbol']['icon-halo-blur']),
    'icon-translate': new DataConstantProperty(spec['paint_symbol']['icon-translate']),
    'icon-translate-anchor': new DataConstantProperty(spec['paint_symbol']['icon-translate-anchor']),
    'text-opacity': new DataDrivenProperty(spec['paint_symbol']['text-opacity']),
    'text-color': new DataDrivenProperty(spec['paint_symbol']['text-color'], {
        runtimeType: ColorType,
        getOverride: o => o.textColor,
        hasOverride: o => !!o.textColor
    }),
    'text-halo-color': new DataDrivenProperty(spec['paint_symbol']['text-halo-color']),
    'text-halo-width': new DataDrivenProperty(spec['paint_symbol']['text-halo-width']),
    'text-halo-blur': new DataDrivenProperty(spec['paint_symbol']['text-halo-blur']),
    'text-translate': new DataConstantProperty(spec['paint_symbol']['text-translate']),
    'text-translate-anchor': new DataConstantProperty(spec['paint_symbol']['text-translate-anchor'])
});
var properties$3 = {
    paint: paint$3,
    layout
};

class FormatSectionOverride {
    constructor(defaultValue) {
        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 && 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'] });

class SymbolStyleLayer extends StyleLayer {
    constructor(layer) {
        super(layer, properties$3);
    }
    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');
        }
        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.indexOf(m) < 0)
                        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() {
        return false;
    }
    _setPaintOverrides() {
        for (const overridable of properties$3.paint.overridableProperties) {
            if (!SymbolStyleLayer.hasPaintOverride(this.layout, overridable)) {
                continue;
            }
            const overriden = this.paint.get(overridable);
            const override = new FormatSectionOverride(overriden);
            const styleExpression = new StyleExpression(override, overriden.property.specification);
            let expression = null;
            if (overriden.value.kind === 'constant' || overriden.value.kind === 'source') {
                expression = new ZoomConstantExpression('source', styleExpression);
            } else {
                expression = new ZoomDependentExpression('composite', styleExpression, overriden.value.zoomStops, overriden.value._interpolationType);
            }
            this.paint._values[overridable] = new PossiblyEvaluatedPropertyValue(overriden.property, expression, overriden.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 = properties$3.paint.properties[propertyName];
        let hasOverrides = false;
        const checkSections = sections => {
            for (const section of sections) {
                if (property.overrides && 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') {
            const checkExpression = expression => {
                if (hasOverrides)
                    return;
                if (expression instanceof Literal$1 && 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;
    }
    getProgramConfiguration(zoom) {
        return new ProgramConfiguration(this, zoom);
    }
}

const paint$2 = new Properties({
    'background-color': new DataConstantProperty(spec['paint_background']['background-color']),
    'background-pattern': new CrossFadedProperty(spec['paint_background']['background-pattern']),
    'background-opacity': new DataConstantProperty(spec['paint_background']['background-opacity'])
});
var properties$2 = { paint: paint$2 };

class BackgroundStyleLayer extends StyleLayer {
    constructor(layer) {
        super(layer, properties$2);
    }
    getProgramIds() {
        const image = this.paint.get('background-pattern');
        return [image ? 'backgroundPattern' : 'background'];
    }
}

const paint$1 = new Properties({
    'raster-opacity': new DataConstantProperty(spec['paint_raster']['raster-opacity']),
    'raster-hue-rotate': new DataConstantProperty(spec['paint_raster']['raster-hue-rotate']),
    'raster-brightness-min': new DataConstantProperty(spec['paint_raster']['raster-brightness-min']),
    'raster-brightness-max': new DataConstantProperty(spec['paint_raster']['raster-brightness-max']),
    'raster-saturation': new DataConstantProperty(spec['paint_raster']['raster-saturation']),
    'raster-contrast': new DataConstantProperty(spec['paint_raster']['raster-contrast']),
    'raster-resampling': new DataConstantProperty(spec['paint_raster']['raster-resampling']),
    'raster-fade-duration': new DataConstantProperty(spec['paint_raster']['raster-fade-duration'])
});
var properties$1 = { paint: paint$1 };

class RasterStyleLayer extends StyleLayer {
    constructor(layer) {
        super(layer, properties$1);
    }
    getProgramIds() {
        return ['raster'];
    }
}

function validateCustomStyleLayer(layerObject) {
    const errors = [];
    const id = layerObject.id;
    if (id === undefined) {
        errors.push({ message: `layers.${ id }: missing required property "id"` });
    }
    if (layerObject.render === undefined) {
        errors.push({ message: `layers.${ id }: missing required method "render"` });
    }
    if (layerObject.renderingMode && layerObject.renderingMode !== '2d' && layerObject.renderingMode !== '3d') {
        errors.push({ message: `layers.${ id }: property "renderingMode" must be either "2d" or "3d"` });
    }
    return errors;
}
class CustomStyleLayer extends StyleLayer {
    constructor(implementation) {
        super(implementation, {});
        this.implementation = implementation;
    }
    is3D() {
        return this.implementation.renderingMode === '3d';
    }
    hasOffscreenPass() {
        return this.implementation.prerender !== undefined;
    }
    recalculate() {
    }
    updateTransitions() {
    }
    hasTransition() {
    }
    serialize() {
    }
    onAdd(map) {
        if (this.implementation.onAdd) {
            this.implementation.onAdd(map, map.painter.context.gl);
        }
    }
    onRemove(map) {
        if (this.implementation.onRemove) {
            this.implementation.onRemove(map, map.painter.context.gl);
        }
    }
}

const paint = new Properties({
    'sky-type': new DataConstantProperty(spec['paint_sky']['sky-type']),
    'sky-atmosphere-sun': new DataConstantProperty(spec['paint_sky']['sky-atmosphere-sun']),
    'sky-atmosphere-sun-intensity': new DataConstantProperty(spec['paint_sky']['sky-atmosphere-sun-intensity']),
    'sky-gradient-center': new DataConstantProperty(spec['paint_sky']['sky-gradient-center']),
    'sky-gradient-radius': new DataConstantProperty(spec['paint_sky']['sky-gradient-radius']),
    'sky-gradient': new ColorRampProperty(spec['paint_sky']['sky-gradient']),
    'sky-atmosphere-halo-color': new DataConstantProperty(spec['paint_sky']['sky-atmosphere-halo-color']),
    'sky-atmosphere-color': new DataConstantProperty(spec['paint_sky']['sky-atmosphere-color']),
    'sky-opacity': new DataConstantProperty(spec['paint_sky']['sky-opacity'])
});
var properties = { paint };

function getCelestialDirection(azimuth, altitude, leftHanded) {
    const up = fromValues(0, 0, 1);
    const rotation = identity(create());
    rotateY(rotation, rotation, leftHanded ? -degToRad(azimuth) + Math.PI : degToRad(azimuth));
    rotateX(rotation, rotation, -degToRad(altitude));
    transformQuat(up, up, rotation);
    return normalize$2(up, up);
}
class SkyLayer extends StyleLayer {
    constructor(layer) {
        super(layer, properties);
        this._updateColorRamp();
    }
    _handleSpecialPaintPropertyUpdate(name) {
        if (name === 'sky-gradient') {
            this._updateColorRamp();
        } else if (name === 'sky-atmosphere-sun' || name === 'sky-atmosphere-halo-color' || name === 'sky-atmosphere-color' || name === 'sky-atmosphere-sun-intensity') {
            this._skyboxInvalidated = true;
        }
    }
    _updateColorRamp() {
        const expression = this._transitionablePaint._values['sky-gradient'].value.expression;
        this.colorRamp = renderColorRamp({
            expression,
            evaluationKey: 'skyRadialProgress'
        });
        if (this.colorRampTexture) {
            this.colorRampTexture.destroy();
            this.colorRampTexture = null;
        }
    }
    needsSkyboxCapture(painter) {
        if (!!this._skyboxInvalidated || !this.skyboxTexture || !this.skyboxGeometry) {
            return true;
        }
        if (!this.paint.get('sky-atmosphere-sun')) {
            const lightPosition = painter.style.light.properties.get('position');
            return this._lightPosition.azimuthal !== lightPosition.azimuthal || this._lightPosition.polar !== lightPosition.polar;
        }
    }
    getCenter(painter, leftHanded) {
        const type = this.paint.get('sky-type');
        if (type === 'atmosphere') {
            const sunPosition = this.paint.get('sky-atmosphere-sun');
            const useLightPosition = !sunPosition;
            const light = painter.style.light;
            const lightPosition = light.properties.get('position');
            if (useLightPosition && light.properties.get('anchor') === 'viewport') {
                warnOnce('The sun direction is attached to a light with viewport anchor, lighting may behave unexpectedly.');
            }
            return useLightPosition ? getCelestialDirection(lightPosition.azimuthal, -lightPosition.polar + 90, leftHanded) : getCelestialDirection(sunPosition[0], -sunPosition[1] + 90, leftHanded);
        } else if (type === 'gradient') {
            const direction = this.paint.get('sky-gradient-center');
            return getCelestialDirection(direction[0], -direction[1] + 90, leftHanded);
        }
    }
    is3D() {
        return false;
    }
    isSky() {
        return true;
    }
    markSkyboxValid(painter) {
        this._skyboxInvalidated = false;
        this._lightPosition = painter.style.light.properties.get('position');
    }
    hasOffscreenPass() {
        return true;
    }
    getProgramIds() {
        const type = this.paint.get('sky-type');
        if (type === 'atmosphere') {
            return [
                'skyboxCapture',
                'skybox'
            ];
        } else if (type === 'gradient') {
            return ['skyboxGradient'];
        }
        return null;
    }
}

const subclasses = {
    circle: CircleStyleLayer,
    heatmap: HeatmapStyleLayer,
    hillshade: HillshadeStyleLayer,
    fill: FillStyleLayer,
    'fill-extrusion': FillExtrusionStyleLayer,
    line: LineStyleLayer,
    symbol: SymbolStyleLayer,
    background: BackgroundStyleLayer,
    raster: RasterStyleLayer,
    sky: SkyLayer
};
function createStyleLayer(layer) {
    if (layer.type === 'custom') {
        return new CustomStyleLayer(layer);
    } else {
        return new subclasses[layer.type](layer);
    }
}

const {HTMLImageElement, HTMLCanvasElement, HTMLVideoElement, ImageData, ImageBitmap} = window$1;
class Texture {
    constructor(context, image, format, options) {
        this.context = context;
        this.format = format;
        this.texture = context.gl.createTexture();
        this.update(image, options);
    }
    update(image, options, position) {
        const {width, height} = image;
        const resize = (!this.size || this.size[0] !== width || this.size[1] !== height) && !position;
        const {context} = this;
        const {gl} = context;
        this.useMipmap = Boolean(options && options.useMipmap);
        gl.bindTexture(gl.TEXTURE_2D, this.texture);
        context.pixelStoreUnpackFlipY.set(false);
        context.pixelStoreUnpack.set(1);
        context.pixelStoreUnpackPremultiplyAlpha.set(this.format === gl.RGBA && (!options || options.premultiply !== false));
        if (resize) {
            this.size = [
                width,
                height
            ];
            if (image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof HTMLVideoElement || image instanceof ImageData || ImageBitmap && image instanceof ImageBitmap) {
                gl.texImage2D(gl.TEXTURE_2D, 0, this.format, this.format, gl.UNSIGNED_BYTE, image);
            } else {
                gl.texImage2D(gl.TEXTURE_2D, 0, this.format, width, height, 0, this.format, gl.UNSIGNED_BYTE, image.data);
            }
        } else {
            const {x, y} = position || {
                x: 0,
                y: 0
            };
            if (image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof HTMLVideoElement || image instanceof ImageData || ImageBitmap && image instanceof ImageBitmap) {
                gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, gl.RGBA, gl.UNSIGNED_BYTE, image);
            } else {
                gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, image.data);
            }
        }
        if (this.useMipmap && this.isSizePowerOfTwo()) {
            gl.generateMipmap(gl.TEXTURE_2D);
        }
    }
    bind(filter, wrap, minFilter) {
        const {context} = this;
        const {gl} = context;
        gl.bindTexture(gl.TEXTURE_2D, this.texture);
        if (minFilter === gl.LINEAR_MIPMAP_NEAREST && !this.isSizePowerOfTwo()) {
            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;
        }
    }
    isSizePowerOfTwo() {
        return this.size[0] === this.size[1] && Math.log(this.size[0]) / Math.LN2 % 1 === 0;
    }
    destroy() {
        const {gl} = this.context;
        gl.deleteTexture(this.texture);
        this.texture = null;
    }
}

class LngLatBounds {
    constructor(sw, ne) {
        if (!sw) ; else if (ne) {
            this.setSouthWest(sw).setNorthEast(ne);
        } else if (sw.length === 4) {
            this.setSouthWest([
                sw[0],
                sw[1]
            ]).setNorthEast([
                sw[2],
                sw[3]
            ]);
        } else {
            this.setSouthWest(sw[0]).setNorthEast(sw[1]);
        }
    }
    setNorthEast(ne) {
        this._ne = ne instanceof LngLat ? new LngLat(ne.lng, ne.lat) : LngLat.convert(ne);
        return this;
    }
    setSouthWest(sw) {
        this._sw = sw instanceof LngLat ? new LngLat(sw.lng, sw.lat) : LngLat.convert(sw);
        return this;
    }
    extend(obj) {
        const sw = this._sw, ne = this._ne;
        let sw2, ne2;
        if (obj instanceof LngLat) {
            sw2 = obj;
            ne2 = obj;
        } else if (obj instanceof LngLatBounds) {
            sw2 = obj._sw;
            ne2 = obj._ne;
            if (!sw2 || !ne2)
                return this;
        } else {
            if (Array.isArray(obj)) {
                if (obj.length === 4 || obj.every(Array.isArray)) {
                    const lngLatBoundsObj = obj;
                    return this.extend(LngLatBounds.convert(lngLatBoundsObj));
                } else {
                    const lngLatObj = obj;
                    return this.extend(LngLat.convert(lngLatObj));
                }
            }
            return this;
        }
        if (!sw && !ne) {
            this._sw = new LngLat(sw2.lng, sw2.lat);
            this._ne = new LngLat(ne2.lng, ne2.lat);
        } else {
            sw.lng = Math.min(sw2.lng, sw.lng);
            sw.lat = Math.min(sw2.lat, sw.lat);
            ne.lng = Math.max(ne2.lng, ne.lng);
            ne.lat = Math.max(ne2.lat, ne.lat);
        }
        return this;
    }
    getCenter() {
        return new LngLat((this._sw.lng + this._ne.lng) / 2, (this._sw.lat + this._ne.lat) / 2);
    }
    getSouthWest() {
        return this._sw;
    }
    getNorthEast() {
        return this._ne;
    }
    getNorthWest() {
        return new LngLat(this.getWest(), this.getNorth());
    }
    getSouthEast() {
        return new LngLat(this.getEast(), this.getSouth());
    }
    getWest() {
        return this._sw.lng;
    }
    getSouth() {
        return this._sw.lat;
    }
    getEast() {
        return this._ne.lng;
    }
    getNorth() {
        return this._ne.lat;
    }
    toArray() {
        return [
            this._sw.toArray(),
            this._ne.toArray()
        ];
    }
    toString() {
        return `LngLatBounds(${ this._sw.toString() }, ${ this._ne.toString() })`;
    }
    isEmpty() {
        return !(this._sw && this._ne);
    }
    contains(lnglat) {
        const {lng, lat} = LngLat.convert(lnglat);
        const containsLatitude = this._sw.lat <= lat && lat <= this._ne.lat;
        let containsLongitude = this._sw.lng <= lng && lng <= this._ne.lng;
        if (this._sw.lng > this._ne.lng) {
            containsLongitude = this._sw.lng >= lng && lng >= this._ne.lng;
        }
        return containsLatitude && containsLongitude;
    }
    static convert(input) {
        if (!input || input instanceof LngLatBounds)
            return input;
        return new LngLatBounds(input);
    }
}

const earthRadius = 6371008.8;
class LngLat {
    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');
        }
    }
    wrap() {
        return new LngLat(wrap(this.lng, -180, 180), this.lat);
    }
    toArray() {
        return [
            this.lng,
            this.lat
        ];
    }
    toString() {
        return `LngLat(${ this.lng }, ${ this.lat })`;
    }
    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);
        const maxMeters = earthRadius * Math.acos(Math.min(a, 1));
        return maxMeters;
    }
    toBounds(radius = 0) {
        const earthCircumferenceInMetersAtEquator = 40075017;
        const latAccuracy = 360 * radius / earthCircumferenceInMetersAtEquator, lngAccuracy = latAccuracy / Math.cos(Math.PI / 180 * this.lat);
        return new LngLatBounds(new LngLat(this.lng - lngAccuracy, this.lat - latAccuracy), new LngLat(this.lng + lngAccuracy, this.lat + latAccuracy));
    }
    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>]');
    }
}

const earthCircumfrence = 2 * Math.PI * earthRadius;
function circumferenceAtLatitude(latitude) {
    return earthCircumfrence * Math.cos(latitude * Math.PI / 180);
}
function mercatorXfromLng(lng) {
    return (180 + lng) / 360;
}
function mercatorYfromLat(lat) {
    return (90 - lat) / 360;
}
function mercatorZfromAltitude(altitude, lat) {
    return altitude / circumferenceAtLatitude(lat);
}
function lngFromMercatorX(x) {
    return x * 360 - 180;
}
function latFromMercatorY(y) {
    return clamp(90 - y * 360, -90, 90);
}
function altitudeFromMercatorZ(z, y) {
    return z * circumferenceAtLatitude(latFromMercatorY(y));
}
function mercatorScale(lat) {
    return 1 / Math.cos(lat * Math.PI / 180);
}
class MercatorCoordinate {
    constructor(x, y, z = 0) {
        this.x = +x;
        this.y = +y;
        this.z = +z;
    }
    static fromLngLat(lngLatLike, altitude = 0) {
        const lngLat = LngLat.convert(lngLatLike);
        return new MercatorCoordinate(mercatorXfromLng(lngLat.lng), mercatorYfromLat(lngLat.lat), mercatorZfromAltitude(altitude, lngLat.lat));
    }
    toLngLat() {
        return new LngLat(lngFromMercatorX(this.x), latFromMercatorY(this.y));
    }
    toAltitude() {
        return altitudeFromMercatorZ(this.z, this.y);
    }
    meterInMercatorCoordinateUnits() {
        return 1 / earthCircumfrence * mercatorScale(latFromMercatorY(this.y));
    }
}

class LineAtlas {
    constructor(width, height) {
        this.width = width;
        this.height = height;
        this.nextRow = 0;
        this.image = new AlphaImage({
            width,
            height
        });
        this.positions = {};
        this.uploaded = false;
    }
    getDash(dasharray, lineCap) {
        const key = this.getKey(dasharray, lineCap);
        return this.positions[key];
    }
    trim() {
        const width = this.width;
        const height = this.height = nextPowerOfTwo(this.nextRow);
        this.image.resize({
            width,
            height
        });
    }
    getKey(dasharray, lineCap) {
        return dasharray.join(',') + lineCap;
    }
    getDashRanges(dasharray, lineAtlasWidth, stretch) {
        const oddDashArray = dasharray.length % 2 === 1;
        const ranges = [];
        let left = oddDashArray ? -dasharray[dasharray.length - 1] * stretch : 0;
        let right = dasharray[0] * stretch;
        let isDash = true;
        ranges.push({
            left,
            right,
            isDash,
            zeroLength: dasharray[0] === 0
        });
        let currentDashLength = dasharray[0];
        for (let i = 1; i < dasharray.length; i++) {
            isDash = !isDash;
            const dashLength = dasharray[i];
            left = currentDashLength * stretch;
            currentDashLength += dashLength;
            right = currentDashLength * stretch;
            ranges.push({
                left,
                right,
                isDash,
                zeroLength: dashLength === 0
            });
        }
        return ranges;
    }
    addRoundDash(ranges, stretch, n) {
        const halfStretch = stretch / 2;
        for (let y = -n; y <= n; y++) {
            const row = this.nextRow + n + y;
            const index = this.width * row;
            let currIndex = 0;
            let range = ranges[currIndex];
            for (let x = 0; x < this.width; x++) {
                if (x / range.right > 1) {
                    range = ranges[++currIndex];
                }
                const distLeft = Math.abs(x - range.left);
                const distRight = Math.abs(x - range.right);
                const minDist = Math.min(distLeft, distRight);
                let signedDistance;
                const distMiddle = y / n * (halfStretch + 1);
                if (range.isDash) {
                    const distEdge = halfStretch - Math.abs(distMiddle);
                    signedDistance = Math.sqrt(minDist * minDist + distEdge * distEdge);
                } else {
                    signedDistance = halfStretch - Math.sqrt(minDist * minDist + distMiddle * distMiddle);
                }
                this.image.data[index + x] = Math.max(0, Math.min(255, signedDistance + 128));
            }
        }
    }
    addRegularDash(ranges, capLength) {
        for (let i = ranges.length - 1; i >= 0; --i) {
            const part = ranges[i];
            const next = ranges[i + 1];
            if (part.zeroLength) {
                ranges.splice(i, 1);
            } else if (next && next.isDash === part.isDash) {
                next.left = part.left;
                ranges.splice(i, 1);
            }
        }
        const first = ranges[0];
        const last = ranges[ranges.length - 1];
        if (first.isDash === last.isDash) {
            first.left = last.left - this.width;
            last.right = first.right + this.width;
        }
        const index = this.width * this.nextRow;
        let currIndex = 0;
        let range = ranges[currIndex];
        for (let x = 0; x < this.width; x++) {
            if (x / range.right > 1) {
                range = ranges[++currIndex];
            }
            const distLeft = Math.abs(x - range.left);
            const distRight = Math.abs(x - range.right);
            const minDist = Math.min(distLeft, distRight);
            const signedDistance = (range.isDash ? minDist : -minDist) + capLength;
            this.image.data[index + x] = Math.max(0, Math.min(255, signedDistance + 128));
        }
    }
    addDash(dasharray, lineCap) {
        const key = this.getKey(dasharray, lineCap);
        if (this.positions[key])
            return this.positions[key];
        const round = lineCap === 'round';
        const n = round ? 7 : 0;
        const height = 2 * n + 1;
        if (this.nextRow + height > this.height) {
            warnOnce('LineAtlas out of space');
            return null;
        }
        if (dasharray.length === 0) {
            dasharray.push(1);
        }
        let length = 0;
        for (let i = 0; i < dasharray.length; i++) {
            if (dasharray[i] < 0) {
                warnOnce('Negative value is found in line dasharray, replacing values with 0');
                dasharray[i] = 0;
            }
            length += dasharray[i];
        }
        if (length !== 0) {
            const stretch = this.width / length;
            const ranges = this.getDashRanges(dasharray, this.width, stretch);
            if (round) {
                this.addRoundDash(ranges, stretch, n);
            } else {
                const capLength = lineCap === 'square' ? 0.5 * stretch : 0;
                this.addRegularDash(ranges, capLength);
            }
        }
        const y = this.nextRow + n;
        this.nextRow += height;
        const pos = {
            tl: [
                y,
                n
            ],
            br: [
                length,
                0
            ]
        };
        this.positions[key] = pos;
        return pos;
    }
}
register('LineAtlas', LineAtlas);

class ThrottledInvoker {
    constructor(callback) {
        this._callback = callback;
        this._triggered = false;
        if (typeof MessageChannel !== 'undefined') {
            this._channel = new MessageChannel();
            this._channel.port2.onmessage = () => {
                this._triggered = false;
                this._callback();
            };
        }
    }
    trigger() {
        if (!this._triggered) {
            this._triggered = true;
            if (this._channel) {
                this._channel.port1.postMessage(true);
            } else {
                setTimeout(() => {
                    this._triggered = false;
                    this._callback();
                }, 0);
            }
        }
    }
    remove() {
        delete this._channel;
        this._callback = () => {
        };
    }
}

const performance = window$1.performance;
function getPerformanceMeasurement(request) {
    const url = request ? request.url.toString() : undefined;
    return performance.getEntriesByName(url);
}

class Scheduler {
    constructor() {
        this.tasks = {};
        this.taskQueue = [];
        bindAll(['process'], this);
        this.invoker = new ThrottledInvoker(this.process);
        this.nextId = 0;
    }
    add(fn, metadata) {
        const id = this.nextId++;
        const priority = getPriority(metadata);
        if (priority === 0) {
            isWorker() ? void 0 : undefined;
            try {
                fn();
            } finally {
            }
            return {
                cancel: () => {
                }
            };
        }
        this.tasks[id] = {
            fn,
            metadata,
            priority,
            id
        };
        this.taskQueue.push(id);
        this.invoker.trigger();
        return {
            cancel: () => {
                delete this.tasks[id];
            }
        };
    }
    process() {
        isWorker() ? void 0 : undefined;
        try {
            this.taskQueue = this.taskQueue.filter(id => !!this.tasks[id]);
            if (!this.taskQueue.length) {
                return;
            }
            const id = this.pick();
            if (id === null)
                return;
            const task = this.tasks[id];
            delete this.tasks[id];
            if (this.taskQueue.length) {
                this.invoker.trigger();
            }
            if (!task) {
                return;
            }
            task.fn();
        } finally {
        }
    }
    pick() {
        let minIndex = null;
        let minPriority = Infinity;
        for (let i = 0; i < this.taskQueue.length; i++) {
            const id = this.taskQueue[i];
            const task = this.tasks[id];
            if (task.priority < minPriority) {
                minPriority = task.priority;
                minIndex = i;
            }
        }
        if (minIndex === null)
            return null;
        const id = this.taskQueue[minIndex];
        this.taskQueue.splice(minIndex, 1);
        return id;
    }
    remove() {
        this.invoker.remove();
    }
}
function getPriority({type, isSymbolTile, zoom}) {
    zoom = zoom || 0;
    if (type === 'message')
        return 0;
    if (type === 'maybePrepare' && !isSymbolTile)
        return 100 - zoom;
    if (type === 'parseTile' && !isSymbolTile)
        return 200 - zoom;
    if (type === 'parseTile' && isSymbolTile)
        return 300 - zoom;
    if (type === 'maybePrepare' && isSymbolTile)
        return 400 - zoom;
    return 500;
}

class Actor {
    constructor(target, parent, mapId) {
        this.target = target;
        this.parent = parent;
        this.mapId = mapId;
        this.callbacks = {};
        this.cancelCallbacks = {};
        bindAll(['receive'], this);
        this.target.addEventListener('message', this.receive, false);
        this.globalScope = isWorker() ? target : window$1;
        this.scheduler = new Scheduler();
    }
    send(type, data, callback, targetMapId, mustQueue = false, callbackMetadata) {
        const id = Math.round(Math.random() * 1000000000000000000).toString(36).substring(0, 10);
        if (callback) {
            callback.metadata = callbackMetadata;
            this.callbacks[id] = callback;
        }
        const buffers = isSafari(this.globalScope) ? undefined : [];
        this.target.postMessage({
            id,
            type,
            hasCallback: !!callback,
            targetMapId,
            mustQueue,
            sourceMapId: this.mapId,
            data: serialize(data, buffers)
        }, buffers);
        return {
            cancel: () => {
                if (callback) {
                    delete this.callbacks[id];
                }
                this.target.postMessage({
                    id,
                    type: '<cancel>',
                    targetMapId,
                    sourceMapId: this.mapId
                });
            }
        };
    }
    receive(message) {
        const data = message.data, id = data.id;
        if (!id) {
            return;
        }
        if (data.targetMapId && this.mapId !== data.targetMapId) {
            return;
        }
        if (data.type === '<cancel>') {
            const cancel = this.cancelCallbacks[id];
            delete this.cancelCallbacks[id];
            if (cancel) {
                cancel.cancel();
            }
        } else {
            if (data.mustQueue || isWorker()) {
                const callback = this.callbacks[id];
                const metadata = callback && callback.metadata || { type: 'message' };
                this.cancelCallbacks[id] = this.scheduler.add(() => this.processTask(id, data), metadata);
            } else {
                this.processTask(id, data);
            }
        }
    }
    processTask(id, task) {
        if (task.type === '<response>') {
            const callback = this.callbacks[id];
            delete this.callbacks[id];
            if (callback) {
                if (task.error) {
                    callback(deserialize$1(task.error));
                } else {
                    callback(null, deserialize$1(task.data));
                }
            }
        } else {
            const buffers = isSafari(this.globalScope) ? undefined : [];
            const done = task.hasCallback ? (err, data) => {
                delete this.cancelCallbacks[id];
                this.target.postMessage({
                    id,
                    type: '<response>',
                    sourceMapId: this.mapId,
                    error: err ? serialize(err) : null,
                    data: serialize(data, buffers)
                }, buffers);
            } : _ => {
            };
            const params = deserialize$1(task.data);
            if (this.parent[task.type]) {
                this.parent[task.type](task.sourceMapId, params, done);
            } else if (this.parent.getWorkerSource) {
                const keys = task.type.split('.');
                const scope = this.parent.getWorkerSource(task.sourceMapId, keys[0], params.source);
                scope[keys[1]](params, done);
            } else {
                done(new Error(`Could not find function ${ task.type }`));
            }
        }
    }
    remove() {
        this.scheduler.remove();
        this.target.removeEventListener('message', this.receive, false);
    }
}

/**
 * getTileBBox
 *
 * @param    {Number}  x  Tile coordinate x
 * @param    {Number}  y  Tile coordinate y
 * @param    {Number}  z  Tile zoom
 * @returns  {String}  String of the bounding box
 */
function getTileBBox(x, y, z) {
    const scale = 360 / Math.pow(2, z);

    const minX = x * scale - 180;
    const minY = 90 - (y + 1) * scale;
    const maxX = (x + 1) * scale - 180;
    const maxY = 90 - y * scale;

    return [minX, minY, maxX, maxY].join(',');
}

class CanonicalTileID {
    constructor(z, x, y) {
        this.z = z;
        this.x = x;
        this.y = y;
        this.key = calculateKey(0, z, z, x, y);
    }
    equals(id) {
        return this.z === id.z && this.x === id.x && this.y === id.y;
    }
    url(urls, scheme, zoomOffset) {
        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}', (this.x % 16).toString(16) + (this.y % 16).toString(16)).replace('{z}', String(this.z + (zoomOffset || 0))).replace('{x}', String(this.x)).replace('{y}', String(scheme === 'tms' ? this.z === 0 ? 0 : Math.pow(2, this.z - 1) - 1 - this.y : this.y)).replace('{quadkey}', quadkey).replace('{bbox-epsg-4490}', bbox);
    }
    getTilePoint(coord) {
        const tilesAtZoom = Math.pow(2, this.z);
        return new pointGeometry((coord.x * tilesAtZoom - this.x) * EXTENT, (coord.y * tilesAtZoom - this.y) * EXTENT);
    }
    getTileVec3(coord) {
        const tilesAtZoom = Math.pow(2, this.z);
        const x = (coord.x * tilesAtZoom - this.x) * EXTENT;
        const y = (coord.y * tilesAtZoom - this.y) * EXTENT;
        return fromValues(x, y, altitudeFromMercatorZ(coord.z, coord.y));
    }
    toString() {
        return `${ this.z }/${ this.x }/${ this.y }`;
    }
}
class UnwrappedTileID {
    constructor(wrap, canonical) {
        this.wrap = wrap;
        this.canonical = canonical;
        this.key = calculateKey(wrap, canonical.z, canonical.z, canonical.x, canonical.y);
    }
}
class OverscaledTileID {
    constructor(overscaledZ, wrap, z, x, y) {
        this.overscaledZ = overscaledZ;
        this.wrap = wrap;
        this.canonical = new CanonicalTileID(z, +x, +y);
        this.key = wrap === 0 && overscaledZ === z ? this.canonical.key : calculateKey(wrap, overscaledZ, z, x, y);
    }
    equals(id) {
        return this.overscaledZ === id.overscaledZ && this.wrap === id.wrap && this.canonical.equals(id.canonical);
    }
    scaledTo(targetZ) {
        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);
        }
    }
    calculateScaledKey(targetZ, withWrap = true) {
        if (this.overscaledZ === targetZ && withWrap)
            return this.key;
        if (targetZ > this.canonical.z) {
            return calculateKey(this.wrap * +withWrap, targetZ, this.canonical.z, this.canonical.x, this.canonical.y);
        } else {
            const zDifference = this.canonical.z - targetZ;
            return calculateKey(this.wrap * +withWrap, targetZ, targetZ, this.canonical.x >> zDifference, this.canonical.y >> zDifference);
        }
    }
    isChildOf(parent) {
        if (parent.wrap !== this.wrap) {
            return false;
        }
        const zDifference = this.canonical.z - parent.canonical.z;
        return parent.overscaledZ === 0 || parent.overscaledZ < this.overscaledZ && parent.canonical.x === this.canonical.x >> zDifference && parent.canonical.y === this.canonical.y >> zDifference;
    }
    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;
        if (this.canonical.y < rhs.canonical.y)
            return true;
        return false;
    }
    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));
    }
    getTileVec3(coord) {
        return this.canonical.getTileVec3(new MercatorCoordinate(coord.x - this.wrap, coord.y, coord.z));
    }
}
function calculateKey(wrap, overscaledZ, z, x, y) {
    const dim = 1 << Math.min(z, 22);
    let xy = dim * (y % dim) + x % dim;
    if (wrap && z < 22) {
        const bitsAvailable = 2 * (22 - z);
        xy += dim * dim * ((wrap < 0 ? -2 * wrap - 1 : 2 * wrap) % (1 << bitsAvailable));
    }
    const key = (xy * 32 + z) * 16 + (overscaledZ - z);
    return key;
}
function getQuadkey(z, x, y) {
    let quadkey = '', mask;
    for (let i = z; i > 0; i--) {
        mask = 1 << i - 1;
        quadkey += (x & mask ? 1 : 0) + (y & mask ? 2 : 0);
    }
    return quadkey;
}
register('CanonicalTileID', CanonicalTileID);
register('OverscaledTileID', OverscaledTileID, { omit: ['projMatrix'] });

class DictionaryCoder {
    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) {
        return this._numberToString[n];
    }
}

class Feature {
    constructor(vectorTileFeature, z, x, y, id) {
        this.type = 'Feature';
        this._vectorTileFeature = vectorTileFeature;
        vectorTileFeature._z = z;
        vectorTileFeature._x = x;
        vectorTileFeature._y = y;
        this.properties = vectorTileFeature.properties;
        this.id = id;
    }
    get geometry() {
        if (this._geometry === undefined) {
            this._geometry = this._vectorTileFeature.toGeoJSON(this._vectorTileFeature._x, this._vectorTileFeature._y, this._vectorTileFeature._z).geometry;
        }
        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')
                continue;
            json[i] = this[i];
        }
        return json;
    }
}

function deserialize(input, style) {
    const output = {};
    if (!style)
        return output;
    for (const bucket of input) {
        const layers = bucket.layerIds.map(id => style.getLayer(id)).filter(Boolean);
        if (layers.length === 0) {
            continue;
        }
        bucket.layers = layers;
        if (bucket.stateDependentLayerIds) {
            bucket.stateDependentLayers = bucket.stateDependentLayerIds.map(lId => layers.filter(l => l.id === lId)[0]);
        }
        for (const layer of layers) {
            output[layer.id] = bucket;
        }
    }
    return output;
}

var posAttributes = createLayout([{
        name: 'a_pos',
        type: 'Int16',
        components: 2
    }]);

const CLOCK_SKEW_RETRY_TIMEOUT = 30000;
class Tile {
    constructor(tileID, size, tileZoom) {
        this.tileID = tileID;
        this.uid = uniqueId();
        this.uses = 0;
        this.tileSize = size;
        this.tileZoom = tileZoom;
        this.buckets = {};
        this.expirationTime = null;
        this.queryPadding = 0;
        this.hasSymbolBuckets = false;
        this.hasRTLText = false;
        this.dependencies = {};
        this.expiredRequestCount = 0;
        this.state = 'loading';
    }
    registerFadeDuration(duration) {
        const fadeEndTime = duration + this.timeAdded;
        if (fadeEndTime < exported$1.now())
            return;
        if (this.fadeEndTime && fadeEndTime < this.fadeEndTime)
            return;
        this.fadeEndTime = fadeEndTime;
    }
    wasRequested() {
        return this.state === 'errored' || this.state === 'loaded' || this.state === 'reloading';
    }
    loadVectorData(data, painter, justReloaded) {
        if (this.hasData()) {
            this.unloadVectorData();
        }
        this.state = 'loaded';
        if (!data) {
            this.collisionBoxArray = new CollisionBoxArray();
            return;
        }
        if (data.featureIndex) {
            this.latestFeatureIndex = data.featureIndex;
            if (data.rawTileData) {
                this.latestRawTileData = data.rawTileData;
                this.latestFeatureIndex.rawTileData = data.rawTileData;
            } else if (this.latestRawTileData) {
                this.latestFeatureIndex.rawTileData = this.latestRawTileData;
            }
        }
        this.collisionBoxArray = data.collisionBoxArray;
        this.buckets = deserialize(data.buckets, painter.style);
        this.hasSymbolBuckets = false;
        for (const id in this.buckets) {
            const bucket = this.buckets[id];
            if (bucket instanceof SymbolBucket) {
                this.hasSymbolBuckets = true;
                if (justReloaded) {
                    bucket.justReloaded = true;
                } else {
                    break;
                }
            }
        }
        this.hasRTLText = false;
        if (this.hasSymbolBuckets) {
            for (const id in this.buckets) {
                const bucket = this.buckets[id];
                if (bucket instanceof SymbolBucket) {
                    if (bucket.hasRTLText) {
                        this.hasRTLText = true;
                        lazyLoadRTLTextPlugin();
                        break;
                    }
                }
            }
        }
        this.queryPadding = 0;
        for (const id in this.buckets) {
            const bucket = this.buckets[id];
            this.queryPadding = Math.max(this.queryPadding, painter.style.getLayer(id).queryRadius(bucket));
        }
        if (data.imageAtlas) {
            this.imageAtlas = data.imageAtlas;
        }
        if (data.glyphAtlasImage) {
            this.glyphAtlasImage = data.glyphAtlasImage;
        }
        if (data.lineAtlas) {
            this.lineAtlas = data.lineAtlas;
        }
    }
    unloadVectorData() {
        for (const id in this.buckets) {
            this.buckets[id].destroy();
        }
        this.buckets = {};
        if (this.imageAtlas) {
            this.imageAtlas = null;
        }
        if (this.lineAtlas) {
            this.lineAtlas = null;
        }
        if (this.imageAtlasTexture) {
            this.imageAtlasTexture.destroy();
        }
        if (this.glyphAtlasTexture) {
            this.glyphAtlasTexture.destroy();
        }
        if (this.lineAtlasTexture) {
            this.lineAtlasTexture.destroy();
        }
        this.latestFeatureIndex = null;
        this.state = 'unloaded';
    }
    getBucket(layer) {
        return this.buckets[layer.id];
    }
    upload(context) {
        for (const id in this.buckets) {
            const bucket = this.buckets[id];
            if (bucket.uploadPending()) {
                bucket.upload(context);
            }
        }
        const gl = context.gl;
        if (this.imageAtlas && !this.imageAtlas.uploaded) {
            this.imageAtlasTexture = new Texture(context, this.imageAtlas.image, gl.RGBA);
            this.imageAtlas.uploaded = true;
        }
        if (this.glyphAtlasImage) {
            this.glyphAtlasTexture = new Texture(context, this.glyphAtlasImage, gl.ALPHA);
            this.glyphAtlasImage = null;
        }
        if (this.lineAtlas && !this.lineAtlas.uploaded) {
            this.lineAtlasTexture = new Texture(context, this.lineAtlas.image, gl.ALPHA);
            this.lineAtlas.uploaded = true;
        }
    }
    prepare(imageManager) {
        if (this.imageAtlas) {
            this.imageAtlas.patchUpdatedImages(imageManager, this.imageAtlasTexture);
        }
    }
    queryRenderedFeatures(layers, serializedLayers, sourceFeatureState, tileResult, params, transform, pixelPosMatrix, visualizeQueryGeometry) {
        if (!this.latestFeatureIndex || !this.latestFeatureIndex.rawTileData)
            return {};
        return this.latestFeatureIndex.query({
            tileResult,
            pixelPosMatrix,
            transform,
            params
        }, layers, serializedLayers, sourceFeatureState);
    }
    querySourceFeatures(result, params) {
        const featureIndex = this.latestFeatureIndex;
        if (!featureIndex || !featureIndex.rawTileData)
            return;
        const vtLayers = featureIndex.loadVTLayers();
        const sourceLayer = params ? params.sourceLayer : '';
        const layer = vtLayers._geojsonTileLayer || vtLayers[sourceLayer];
        if (!layer)
            return;
        const filter = createFilter(params && params.filter);
        const {z, x, y} = this.tileID.canonical;
        const coord = {
            z,
            x,
            y
        };
        for (let i = 0; i < layer.length; i++) {
            const feature = layer.feature(i);
            if (filter.needGeometry) {
                const evaluationFeature = toEvaluationFeature(feature, true);
                if (!filter.filter(new EvaluationParameters(this.tileID.overscaledZ), evaluationFeature, this.tileID.canonical))
                    continue;
            } else if (!filter.filter(new EvaluationParameters(this.tileID.overscaledZ), feature)) {
                continue;
            }
            const id = featureIndex.getId(feature, sourceLayer);
            const geojsonFeature = new Feature(feature, z, x, y, id);
            geojsonFeature.tile = coord;
            result.push(geojsonFeature);
        }
    }
    hasData() {
        return this.state === 'loaded' || this.state === 'reloading' || this.state === 'expired';
    }
    patternsLoaded() {
        return this.imageAtlas && !!Object.keys(this.imageAtlas.patternPositions).length;
    }
    setExpiryData(data) {
        const prior = this.expirationTime;
        if (data.cacheControl) {
            const parsedCC = parseCacheControl(data.cacheControl);
            if (parsedCC['max-age'])
                this.expirationTime = Date.now() + parsedCC['max-age'] * 1000;
        } else if (data.expires) {
            this.expirationTime = new Date(data.expires).getTime();
        }
        if (this.expirationTime) {
            const now = Date.now();
            let isExpired = false;
            if (this.expirationTime > now) {
                isExpired = false;
            } else if (!prior) {
                isExpired = true;
            } else if (this.expirationTime < prior) {
                isExpired = true;
            } else {
                const delta = this.expirationTime - prior;
                if (!delta) {
                    isExpired = true;
                } else {
                    this.expirationTime = now + Math.max(delta, CLOCK_SKEW_RETRY_TIMEOUT);
                }
            }
            if (isExpired) {
                this.expiredRequestCount++;
                this.state = 'expired';
            } else {
                this.expiredRequestCount = 0;
            }
        }
    }
    getExpiryTimeout() {
        if (this.expirationTime) {
            if (this.expiredRequestCount) {
                return 1000 * (1 << Math.min(this.expiredRequestCount - 1, 31));
            } else {
                return Math.min(this.expirationTime - new Date().getTime(), Math.pow(2, 31) - 1);
            }
        }
    }
    setFeatureState(states, painter) {
        if (!this.latestFeatureIndex || !this.latestFeatureIndex.rawTileData || Object.keys(states).length === 0) {
            return;
        }
        const vtLayers = this.latestFeatureIndex.loadVTLayers();
        for (const id in this.buckets) {
            if (!painter.style.hasLayer(id))
                continue;
            const bucket = this.buckets[id];
            const sourceLayerId = bucket.layers[0]['sourceLayer'] || '_geojsonTileLayer';
            const sourceLayer = vtLayers[sourceLayerId];
            const sourceLayerStates = states[sourceLayerId];
            if (!sourceLayer || !sourceLayerStates || Object.keys(sourceLayerStates).length === 0)
                continue;
            bucket.update(sourceLayerStates, sourceLayer, this.imageAtlas && this.imageAtlas.patternPositions || {});
            const layer = painter && painter.style && painter.style.getLayer(id);
            if (layer) {
                this.queryPadding = Math.max(this.queryPadding, layer.queryRadius(bucket));
            }
        }
    }
    holdingForFade() {
        return this.symbolFadeHoldUntil !== undefined;
    }
    symbolFadeFinished() {
        return !this.symbolFadeHoldUntil || this.symbolFadeHoldUntil < exported$1.now();
    }
    clearFadeHold() {
        this.symbolFadeHoldUntil = undefined;
    }
    setHoldDuration(duration) {
        this.symbolFadeHoldUntil = exported$1.now() + duration;
    }
    setDependencies(namespace, dependencies) {
        const index = {};
        for (const dep of dependencies) {
            index[dep] = true;
        }
        this.dependencies[namespace] = index;
    }
    hasDependency(namespaces, keys) {
        for (const namespace of namespaces) {
            const dependencies = this.dependencies[namespace];
            if (dependencies) {
                for (const key of keys) {
                    if (dependencies[key]) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    clearQueryDebugViz() {
    }
}

class SourceFeatureState {
    constructor() {
        this.state = {};
        this.stateChanges = {};
        this.deletedStates = {};
    }
    updateState(sourceLayer, featureId, newState) {
        const feature = String(featureId);
        this.stateChanges[sourceLayer] = this.stateChanges[sourceLayer] || {};
        this.stateChanges[sourceLayer][feature] = this.stateChanges[sourceLayer][feature] || {};
        extend$1(this.stateChanges[sourceLayer][feature], newState);
        if (this.deletedStates[sourceLayer] === null) {
            this.deletedStates[sourceLayer] = {};
            for (const ft in this.state[sourceLayer]) {
                if (ft !== feature)
                    this.deletedStates[sourceLayer][ft] = null;
            }
        } else {
            const featureDeletionQueued = this.deletedStates[sourceLayer] && this.deletedStates[sourceLayer][feature] === null;
            if (featureDeletionQueued) {
                this.deletedStates[sourceLayer][feature] = {};
                for (const prop in this.state[sourceLayer][feature]) {
                    if (!newState[prop])
                        this.deletedStates[sourceLayer][feature][prop] = null;
                }
            } else {
                for (const key in newState) {
                    const deletionInQueue = this.deletedStates[sourceLayer] && this.deletedStates[sourceLayer][feature] && this.deletedStates[sourceLayer][feature][key] === null;
                    if (deletionInQueue)
                        delete this.deletedStates[sourceLayer][feature][key];
                }
            }
        }
    }
    removeFeatureState(sourceLayer, featureId, key) {
        const sourceLayerDeleted = this.deletedStates[sourceLayer] === null;
        if (sourceLayerDeleted)
            return;
        const feature = String(featureId);
        this.deletedStates[sourceLayer] = this.deletedStates[sourceLayer] || {};
        if (key && featureId !== undefined) {
            if (this.deletedStates[sourceLayer][feature] !== null) {
                this.deletedStates[sourceLayer][feature] = this.deletedStates[sourceLayer][feature] || {};
                this.deletedStates[sourceLayer][feature][key] = null;
            }
        } else if (featureId !== undefined) {
            const updateInQueue = this.stateChanges[sourceLayer] && this.stateChanges[sourceLayer][feature];
            if (updateInQueue) {
                this.deletedStates[sourceLayer][feature] = {};
                for (key in this.stateChanges[sourceLayer][feature])
                    this.deletedStates[sourceLayer][feature][key] = null;
            } else {
                this.deletedStates[sourceLayer][feature] = null;
            }
        } else {
            this.deletedStates[sourceLayer] = null;
        }
    }
    getState(sourceLayer, featureId) {
        const feature = String(featureId);
        const base = this.state[sourceLayer] || {};
        const changes = this.stateChanges[sourceLayer] || {};
        const reconciledState = extend$1({}, base[feature], changes[feature]);
        if (this.deletedStates[sourceLayer] === null)
            return {};
        else if (this.deletedStates[sourceLayer]) {
            const featureDeletions = this.deletedStates[sourceLayer][featureId];
            if (featureDeletions === null)
                return {};
            for (const prop in featureDeletions)
                delete reconciledState[prop];
        }
        return reconciledState;
    }
    initializeTileState(tile, painter) {
        tile.setFeatureState(this.state, painter);
    }
    coalesceChanges(tiles, painter) {
        const featuresChanged = {};
        for (const sourceLayer in this.stateChanges) {
            this.state[sourceLayer] = this.state[sourceLayer] || {};
            const layerStates = {};
            for (const feature in this.stateChanges[sourceLayer]) {
                if (!this.state[sourceLayer][feature])
                    this.state[sourceLayer][feature] = {};
                extend$1(this.state[sourceLayer][feature], this.stateChanges[sourceLayer][feature]);
                layerStates[feature] = this.state[sourceLayer][feature];
            }
            featuresChanged[sourceLayer] = layerStates;
        }
        for (const sourceLayer in this.deletedStates) {
            this.state[sourceLayer] = this.state[sourceLayer] || {};
            const layerStates = {};
            if (this.deletedStates[sourceLayer] === null) {
                for (const ft in this.state[sourceLayer]) {
                    layerStates[ft] = {};
                    this.state[sourceLayer][ft] = {};
                }
            } else {
                for (const feature in this.deletedStates[sourceLayer]) {
                    const deleteWholeFeatureState = this.deletedStates[sourceLayer][feature] === null;
                    if (deleteWholeFeatureState)
                        this.state[sourceLayer][feature] = {};
                    else {
                        for (const key of Object.keys(this.deletedStates[sourceLayer][feature])) {
                            delete this.state[sourceLayer][feature][key];
                        }
                    }
                    layerStates[feature] = this.state[sourceLayer][feature];
                }
            }
            featuresChanged[sourceLayer] = featuresChanged[sourceLayer] || {};
            extend$1(featuresChanged[sourceLayer], layerStates);
        }
        this.stateChanges = {};
        this.deletedStates = {};
        if (Object.keys(featuresChanged).length === 0)
            return;
        for (const id in tiles) {
            const tile = tiles[id];
            tile.setFeatureState(featuresChanged, painter);
        }
    }
}

class MipLevel {
    constructor(size_) {
        this.size = size_;
        this.minimums = [];
        this.maximums = [];
        this.leaves = [];
    }
    getElevation(x, y) {
        const idx = this.toIdx(x, y);
        return {
            min: this.minimums[idx],
            max: this.maximums[idx]
        };
    }
    isLeaf(x, y) {
        return this.leaves[this.toIdx(x, y)];
    }
    toIdx(x, y) {
        return y * this.size + x;
    }
}
function aabbRayIntersect(min, max, pos, dir) {
    let tMin = 0;
    let tMax = Number.MAX_VALUE;
    const epsilon = 1e-15;
    for (let i = 0; i < 3; i++) {
        if (Math.abs(dir[i]) < epsilon) {
            if (pos[i] < min[i] || pos[i] > max[i])
                return null;
        } else {
            const ood = 1 / dir[i];
            let t1 = (min[i] - pos[i]) * ood;
            let t2 = (max[i] - pos[i]) * ood;
            if (t1 > t2) {
                const temp = t1;
                t1 = t2;
                t2 = temp;
            }
            if (t1 > tMin)
                tMin = t1;
            if (t2 < tMax)
                tMax = t2;
            if (tMin > tMax)
                return null;
        }
    }
    return tMin;
}
function triangleRayIntersect(ax, ay, az, bx, by, bz, cx, cy, cz, pos, dir) {
    const abX = bx - ax;
    const abY = by - ay;
    const abZ = bz - az;
    const acX = cx - ax;
    const acY = cy - ay;
    const acZ = cz - az;
    const pvecX = dir[1] * acZ - dir[2] * acY;
    const pvecY = dir[2] * acX - dir[0] * acZ;
    const pvecZ = dir[0] * acY - dir[1] * acX;
    const det = abX * pvecX + abY * pvecY + abZ * pvecZ;
    if (Math.abs(det) < 1e-15)
        return null;
    const invDet = 1 / det;
    const tvecX = pos[0] - ax;
    const tvecY = pos[1] - ay;
    const tvecZ = pos[2] - az;
    const u = (tvecX * pvecX + tvecY * pvecY + tvecZ * pvecZ) * invDet;
    if (u < 0 || u > 1)
        return null;
    const qvecX = tvecY * abZ - tvecZ * abY;
    const qvecY = tvecZ * abX - tvecX * abZ;
    const qvecZ = tvecX * abY - tvecY * abX;
    const v = (dir[0] * qvecX + dir[1] * qvecY + dir[2] * qvecZ) * invDet;
    if (v < 0 || u + v > 1)
        return null;
    return (acX * qvecX + acY * qvecY + acZ * qvecZ) * invDet;
}
function frac(v, lo, hi) {
    return (v - lo) / (hi - lo);
}
function decodeBounds(x, y, depth, boundsMinx, boundsMiny, boundsMaxx, boundsMaxy, outMin, outMax) {
    const scale = 1 << depth;
    const rangex = boundsMaxx - boundsMinx;
    const rangey = boundsMaxy - boundsMiny;
    const minX = (x + 0) / scale * rangex + boundsMinx;
    const maxX = (x + 1) / scale * rangex + boundsMinx;
    const minY = (y + 0) / scale * rangey + boundsMiny;
    const maxY = (y + 1) / scale * rangey + boundsMiny;
    outMin[0] = minX;
    outMin[1] = minY;
    outMax[0] = maxX;
    outMax[1] = maxY;
}
const aabbSkirtPadding = 100;
class DemMinMaxQuadTree {
    constructor(dem_) {
        this.maximums = [];
        this.minimums = [];
        this.leaves = [];
        this.childOffsets = [];
        this.nodeCount = 0;
        this.dem = dem_;
        this._siblingOffset = [
            [
                0,
                0
            ],
            [
                1,
                0
            ],
            [
                0,
                1
            ],
            [
                1,
                1
            ]
        ];
        if (!this.dem)
            return;
        const mips = buildDemMipmap(this.dem);
        const maxLvl = mips.length - 1;
        const rootMip = mips[maxLvl];
        const min = rootMip.minimums;
        const max = rootMip.maximums;
        const leaves = rootMip.leaves;
        this._addNode(min[0], max[0], leaves[0]);
        this._construct(mips, 0, 0, maxLvl, 0);
    }
    raycastRoot(minx, miny, maxx, maxy, p, d, exaggeration = 1) {
        const min = [
            minx,
            miny,
            -aabbSkirtPadding
        ];
        const max = [
            maxx,
            maxy,
            this.maximums[0] * exaggeration
        ];
        return aabbRayIntersect(min, max, p, d);
    }
    raycast(rootMinx, rootMiny, rootMaxx, rootMaxy, p, d, exaggeration = 1) {
        if (!this.nodeCount)
            return null;
        const t = this.raycastRoot(rootMinx, rootMiny, rootMaxx, rootMaxy, p, d, exaggeration);
        if (t == null)
            return null;
        const tHits = [];
        const sortedHits = [];
        const boundsMin = [];
        const boundsMax = [];
        const stack = [{
                idx: 0,
                t,
                nodex: 0,
                nodey: 0,
                depth: 0
            }];
        while (stack.length > 0) {
            const {idx, t, nodex, nodey, depth} = stack.pop();
            if (this.leaves[idx]) {
                decodeBounds(nodex, nodey, depth, rootMinx, rootMiny, rootMaxx, rootMaxy, boundsMin, boundsMax);
                const scale = 1 << depth;
                const minxUv = (nodex + 0) / scale;
                const maxxUv = (nodex + 1) / scale;
                const minyUv = (nodey + 0) / scale;
                const maxyUv = (nodey + 1) / scale;
                const az = sampleElevation(minxUv, minyUv, this.dem) * exaggeration;
                const bz = sampleElevation(maxxUv, minyUv, this.dem) * exaggeration;
                const cz = sampleElevation(maxxUv, maxyUv, this.dem) * exaggeration;
                const dz = sampleElevation(minxUv, maxyUv, this.dem) * exaggeration;
                const t0 = triangleRayIntersect(boundsMin[0], boundsMin[1], az, boundsMax[0], boundsMin[1], bz, boundsMax[0], boundsMax[1], cz, p, d);
                const t1 = triangleRayIntersect(boundsMax[0], boundsMax[1], cz, boundsMin[0], boundsMax[1], dz, boundsMin[0], boundsMin[1], az, p, d);
                const tMin = Math.min(t0 !== null ? t0 : Number.MAX_VALUE, t1 !== null ? t1 : Number.MAX_VALUE);
                if (tMin === Number.MAX_VALUE) {
                    const hitPos = scaleAndAdd([], p, d, t);
                    const fracx = frac(hitPos[0], boundsMin[0], boundsMax[0]);
                    const fracy = frac(hitPos[1], boundsMin[1], boundsMax[1]);
                    if (bilinearLerp(az, bz, dz, cz, fracx, fracy) >= hitPos[2])
                        return t;
                } else {
                    return tMin;
                }
                continue;
            }
            let hitCount = 0;
            for (let i = 0; i < this._siblingOffset.length; i++) {
                const childNodeX = (nodex << 1) + this._siblingOffset[i][0];
                const childNodeY = (nodey << 1) + this._siblingOffset[i][1];
                decodeBounds(childNodeX, childNodeY, depth + 1, rootMinx, rootMiny, rootMaxx, rootMaxy, boundsMin, boundsMax);
                boundsMin[2] = -aabbSkirtPadding;
                boundsMax[2] = this.maximums[this.childOffsets[idx] + i] * exaggeration;
                const result = aabbRayIntersect(boundsMin, boundsMax, p, d);
                if (result != null) {
                    const tHit = result;
                    tHits[i] = tHit;
                    let added = false;
                    for (let j = 0; j < hitCount && !added; j++) {
                        if (tHit >= tHits[sortedHits[j]]) {
                            sortedHits.splice(j, 0, i);
                            added = true;
                        }
                    }
                    if (!added)
                        sortedHits[hitCount] = i;
                    hitCount++;
                }
            }
            for (let i = 0; i < hitCount; i++) {
                const hitIdx = sortedHits[i];
                stack.push({
                    idx: this.childOffsets[idx] + hitIdx,
                    t: tHits[hitIdx],
                    nodex: (nodex << 1) + this._siblingOffset[hitIdx][0],
                    nodey: (nodey << 1) + this._siblingOffset[hitIdx][1],
                    depth: depth + 1
                });
            }
        }
        return null;
    }
    _addNode(min, max, leaf) {
        this.minimums.push(min);
        this.maximums.push(max);
        this.leaves.push(leaf);
        this.childOffsets.push(0);
        return this.nodeCount++;
    }
    _construct(mips, x, y, lvl, parentIdx) {
        if (mips[lvl].isLeaf(x, y) === 1) {
            return;
        }
        if (!this.childOffsets[parentIdx])
            this.childOffsets[parentIdx] = this.nodeCount;
        const childLvl = lvl - 1;
        const childMip = mips[childLvl];
        let leafMask = 0;
        let firstNodeIdx;
        for (let i = 0; i < this._siblingOffset.length; i++) {
            const childX = x * 2 + this._siblingOffset[i][0];
            const childY = y * 2 + this._siblingOffset[i][1];
            const elevation = childMip.getElevation(childX, childY);
            const leaf = childMip.isLeaf(childX, childY);
            const nodeIdx = this._addNode(elevation.min, elevation.max, leaf);
            if (leaf)
                leafMask |= 1 << i;
            if (!firstNodeIdx)
                firstNodeIdx = nodeIdx;
        }
        for (let i = 0; i < this._siblingOffset.length; i++) {
            if (!(leafMask & 1 << i)) {
                this._construct(mips, x * 2 + this._siblingOffset[i][0], y * 2 + this._siblingOffset[i][1], childLvl, firstNodeIdx + i);
            }
        }
    }
}
function bilinearLerp(p00, p10, p01, p11, x, y) {
    return number(number(p00, p01, y), number(p10, p11, y), x);
}
function sampleElevation(fx, fy, dem) {
    const demSize = dem.dim;
    const x = clamp(fx * demSize - 0.5, 0, demSize - 1);
    const y = clamp(fy * demSize - 0.5, 0, demSize - 1);
    const ixMin = Math.floor(x);
    const iyMin = Math.floor(y);
    const ixMax = Math.min(ixMin + 1, demSize - 1);
    const iyMax = Math.min(iyMin + 1, demSize - 1);
    const e00 = dem.get(ixMin, iyMin);
    const e10 = dem.get(ixMax, iyMin);
    const e01 = dem.get(ixMin, iyMax);
    const e11 = dem.get(ixMax, iyMax);
    return bilinearLerp(e00, e10, e01, e11, x - ixMin, y - iyMin);
}
function buildDemMipmap(dem) {
    const demSize = dem.dim;
    const elevationDiffThreshold = 5;
    const texelSizeOfMip0 = 8;
    const levelCount = Math.ceil(Math.log2(demSize / texelSizeOfMip0));
    const mips = [];
    let blockCount = Math.ceil(Math.pow(2, levelCount));
    const blockSize = 1 / blockCount;
    const blockSamples = (x, y, size, exclusive, outBounds) => {
        const padding = exclusive ? 1 : 0;
        const minx = x * size;
        const maxx = (x + 1) * size - padding;
        const miny = y * size;
        const maxy = (y + 1) * size - padding;
        outBounds[0] = minx;
        outBounds[1] = miny;
        outBounds[2] = maxx;
        outBounds[3] = maxy;
    };
    let mip = new MipLevel(blockCount);
    const blockBounds = [];
    for (let idx = 0; idx < blockCount * blockCount; idx++) {
        const y = Math.floor(idx / blockCount);
        const x = idx % blockCount;
        blockSamples(x, y, blockSize, false, blockBounds);
        const e0 = sampleElevation(blockBounds[0], blockBounds[1], dem);
        const e1 = sampleElevation(blockBounds[2], blockBounds[1], dem);
        const e2 = sampleElevation(blockBounds[2], blockBounds[3], dem);
        const e3 = sampleElevation(blockBounds[0], blockBounds[3], dem);
        mip.minimums.push(Math.min(e0, e1, e2, e3));
        mip.maximums.push(Math.max(e0, e1, e2, e3));
        mip.leaves.push(1);
    }
    mips.push(mip);
    for (blockCount /= 2; blockCount >= 1; blockCount /= 2) {
        const prevMip = mips[mips.length - 1];
        mip = new MipLevel(blockCount);
        for (let idx = 0; idx < blockCount * blockCount; idx++) {
            const y = Math.floor(idx / blockCount);
            const x = idx % blockCount;
            blockSamples(x, y, 2, true, blockBounds);
            const e0 = prevMip.getElevation(blockBounds[0], blockBounds[1]);
            const e1 = prevMip.getElevation(blockBounds[2], blockBounds[1]);
            const e2 = prevMip.getElevation(blockBounds[2], blockBounds[3]);
            const e3 = prevMip.getElevation(blockBounds[0], blockBounds[3]);
            const l0 = prevMip.isLeaf(blockBounds[0], blockBounds[1]);
            const l1 = prevMip.isLeaf(blockBounds[2], blockBounds[1]);
            const l2 = prevMip.isLeaf(blockBounds[2], blockBounds[3]);
            const l3 = prevMip.isLeaf(blockBounds[0], blockBounds[3]);
            const minElevation = Math.min(e0.min, e1.min, e2.min, e3.min);
            const maxElevation = Math.max(e0.max, e1.max, e2.max, e3.max);
            const canConcatenate = l0 && l1 && l2 && l3;
            mip.maximums.push(maxElevation);
            mip.minimums.push(minElevation);
            if (maxElevation - minElevation <= elevationDiffThreshold && canConcatenate) {
                mip.leaves.push(1);
            } else {
                mip.leaves.push(0);
            }
        }
        mips.push(mip);
    }
    return mips;
}

const unpackVectors = {
    mapbox: [
        6553.6,
        25.6,
        0.1,
        10000
    ],
    terrarium: [
        256,
        1,
        1 / 256,
        32768
    ]
};
class DEMData {
    get tree() {
        if (!this._tree)
            this._buildQuadTree();
        return this._tree;
    }
    constructor(uid, data, encoding, borderReady = false, buildQuadTree = false) {
        this.uid = uid;
        if (data.height !== data.width)
            throw new RangeError('DEM tiles must be square');
        if (encoding && encoding !== 'mapbox' && encoding !== 'terrarium')
            return warnOnce(`"${ encoding }" is not a valid encoding type. Valid types include "mapbox" and "terrarium".`);
        this.stride = data.height;
        const dim = this.dim = data.height - 2;
        this.data = new Uint32Array(data.data.buffer);
        this.encoding = encoding || 'mapbox';
        this.borderReady = borderReady;
        if (borderReady)
            return;
        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)];
        if (buildQuadTree)
            this._buildQuadTree();
    }
    _buildQuadTree() {
        this._tree = new DemMinMaxQuadTree(this);
    }
    get(x, y, clampToEdge = false) {
        const pixels = new Uint8Array(this.data.buffer);
        if (clampToEdge) {
            x = clamp(x, -1, this.dim);
            y = clamp(y, -1, this.dim);
        }
        const index = this._idx(x, y) * 4;
        const unpack = this.encoding === 'terrarium' ? this._unpackTerrarium : this._unpackMapbox;
        return unpack(pixels[index], pixels[index + 1], pixels[index + 2]);
    }
    static getUnpackVector(encoding) {
        return unpackVectors[encoding];
    }
    get unpackVector() {
        return unpackVectors[this.encoding];
    }
    _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');
        return (y + 1) * this.stride + (x + 1);
    }
    _unpackMapbox(r, g, b) {
        return (r * 256 * 256 + g * 256 + b) / 10 - 10000;
    }
    _unpackTerrarium(r, g, b) {
        return r * 256 + g + b / 256 - 32768;
    }
    static pack(altitude, encoding) {
        const color = [
            0,
            0,
            0,
            0
        ];
        const vector = DEMData.getUnpackVector(encoding);
        let v = Math.floor((altitude + vector[3]) / vector[2]);
        color[2] = v % 256;
        v = Math.floor(v / 256);
        color[1] = v % 256;
        v = Math.floor(v / 256);
        color[0] = v;
        return color;
    }
    getPixels() {
        return new RGBAImage({
            width: this.stride,
            height: this.stride
        }, new Uint8Array(this.data.buffer));
    }
    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;
            break;
        }
        switch (dy) {
        case -1:
            yMin = yMax - 1;
            break;
        case 1:
            yMax = yMin + 1;
            break;
        }
        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)];
            }
        }
    }
    onDeserialize() {
        if (this._tree)
            this._tree.dem = this;
    }
}
register('DEMData', DEMData);
register('DemMinMaxQuadTree', DemMinMaxQuadTree, { omit: ['dem'] });

class TileCache {
    constructor(max, onRemove) {
        this.max = max;
        this.onRemove = onRemove;
        this.reset();
    }
    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(tileID, data, expiryTimeout) {
        const key = tileID.wrapped().key;
        if (this.data[key] === undefined) {
            this.data[key] = [];
        }
        const dataWrapper = {
            value: data,
            timeout: undefined
        };
        if (expiryTimeout !== undefined) {
            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;
    }
    has(tileID) {
        return tileID.wrapped().key in this.data;
    }
    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(tileID) {
        if (!this.has(tileID)) {
            return null;
        }
        const data = this.data[tileID.wrapped().key][0];
        return data.value;
    }
    remove(tileID, value) {
        if (!this.has(tileID)) {
            return this;
        }
        const key = tileID.wrapped().key;
        const dataIndex = value === undefined ? 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;
    }
    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;
    }
    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);
        }
    }
}

const ALWAYS$1 = 519;
class DepthMode {
    constructor(depthFunc, depthMask, depthRange) {
        this.func = depthFunc;
        this.mask = depthMask;
        this.range = depthRange;
    }
}
DepthMode.ReadOnly = false;
DepthMode.ReadWrite = true;
DepthMode.disabled = new DepthMode(ALWAYS$1, DepthMode.ReadOnly, [
    0,
    1
]);

const ALWAYS = 519;
const KEEP = 7680;
class StencilMode {
    constructor(test, ref, mask, fail, depthFail, pass) {
        this.test = test;
        this.ref = ref;
        this.mask = mask;
        this.fail = fail;
        this.depthFail = depthFail;
        this.pass = pass;
    }
}
StencilMode.disabled = new StencilMode({
    func: ALWAYS,
    mask: 0
}, 0, 0, KEEP, KEEP, KEEP);

const ZERO = 0;
const ONE = 1;
const ONE_MINUS_SRC_ALPHA = 771;
class ColorMode {
    constructor(blendFunction, blendColor, mask) {
        this.blendFunction = blendFunction;
        this.blendColor = blendColor;
        this.mask = mask;
    }
}
ColorMode.Replace = [
    ONE,
    ZERO
];
ColorMode.disabled = new ColorMode(ColorMode.Replace, Color$1.transparent, [
    false,
    false,
    false,
    false
]);
ColorMode.unblended = new ColorMode(ColorMode.Replace, Color$1.transparent, [
    true,
    true,
    true,
    true
]);
ColorMode.alphaBlended = new ColorMode([
    ONE,
    ONE_MINUS_SRC_ALPHA
], Color$1.transparent, [
    true,
    true,
    true,
    true
]);

const BACK = 1029;
const FRONT = 1028;
const CCW = 2305;
const CW = 2304;
class CullFaceMode {
    constructor(enable, mode, frontFace) {
        this.enable = enable;
        this.mode = mode;
        this.frontFace = frontFace;
    }
}
CullFaceMode.disabled = new CullFaceMode(false, BACK, CCW);
CullFaceMode.backCCW = new CullFaceMode(true, BACK, CCW);
CullFaceMode.backCW = new CullFaceMode(true, BACK, CW);
CullFaceMode.frontCW = new CullFaceMode(true, FRONT, CW);
CullFaceMode.frontCCW = new CullFaceMode(true, FRONT, CCW);

class SourceCache extends Evented {
    constructor(id, source, onlySymbols) {
        super();
        this.id = id;
        this._onlySymbols = onlySymbols;
        source.on('data', e => {
            if (e.dataType === 'source' && e.sourceDataType === 'metadata')
                this._sourceLoaded = true;
            if (this._sourceLoaded && !this._paused && e.dataType === 'source' && e.sourceDataType === 'content') {
                this.reload();
                if (this.transform) {
                    this.update(this.transform);
                }
            }
        });
        source.on('error', () => {
            this._sourceErrored = true;
        });
        this._source = source;
        this._tiles = {};
        this._cache = new TileCache(0, this._unloadTile.bind(this));
        this._timers = {};
        this._cacheTimers = {};
        this._maxTileCacheSize = null;
        this._loadedParentTiles = {};
        this._coveredTiles = {};
        this._state = new SourceFeatureState();
    }
    onAdd(map) {
        this.map = map;
        this._maxTileCacheSize = map ? map._maxTileCacheSize : null;
    }
    loaded() {
        if (this._sourceErrored) {
            return true;
        }
        if (!this._sourceLoaded) {
            return false;
        }
        if (!this._source.loaded()) {
            return false;
        }
        for (const t in this._tiles) {
            const tile = this._tiles[t];
            if (tile.state !== 'loaded' && tile.state !== 'errored')
                return false;
        }
        return true;
    }
    getSource() {
        return this._source;
    }
    pause() {
        this._paused = true;
    }
    resume() {
        if (!this._paused)
            return;
        const shouldReload = this._shouldReloadOnResume;
        this._paused = false;
        this._shouldReloadOnResume = false;
        if (shouldReload)
            this.reload();
        if (this.transform)
            this.update(this.transform);
    }
    _loadTile(tile, callback) {
        tile.isSymbolTile = this._onlySymbols;
        return this._source.loadTile(tile, callback);
    }
    _unloadTile(tile) {
        if (this._source.unloadTile)
            return this._source.unloadTile(tile, () => {
            });
    }
    _abortTile(tile) {
        if (this._source.abortTile)
            return this._source.abortTile(tile, () => {
            });
    }
    serialize() {
        return this._source.serialize();
    }
    prepare(context) {
        if (this._source.prepare) {
            this._source.prepare();
        }
        this._state.coalesceChanges(this._tiles, this.map ? this.map.painter : null);
        for (const i in this._tiles) {
            const tile = this._tiles[i];
            tile.upload(context);
            tile.prepare(this.map.style.imageManager);
        }
    }
    getIds() {
        return values(this._tiles).map(tile => tile.tileID).sort(compareTileId).map(id => id.key);
    }
    getRenderableIds(symbolLayer) {
        const renderables = [];
        for (const id in this._tiles) {
            if (this._isIdRenderable(+id, symbolLayer))
                renderables.push(this._tiles[id]);
        }
        if (symbolLayer) {
            return renderables.sort((a_, b_) => {
                const a = a_.tileID;
                const b = b_.tileID;
                const rotatedA = new pointGeometry(a.canonical.x, a.canonical.y)._rotate(this.transform.angle);
                const rotatedB = new pointGeometry(b.canonical.x, b.canonical.y)._rotate(this.transform.angle);
                return a.overscaledZ - b.overscaledZ || rotatedB.y - rotatedA.y || rotatedB.x - rotatedA.x;
            }).map(tile => tile.tileID.key);
        }
        return renderables.map(tile => tile.tileID).sort(compareTileId).map(id => id.key);
    }
    hasRenderableParent(tileID) {
        const parentTile = this.findLoadedParent(tileID, 0);
        if (parentTile) {
            return this._isIdRenderable(parentTile.tileID.key);
        }
        return false;
    }
    _isIdRenderable(id, symbolLayer) {
        return this._tiles[id] && this._tiles[id].hasData() && !this._coveredTiles[id] && (symbolLayer || !this._tiles[id].holdingForFade());
    }
    reload() {
        if (this._paused) {
            this._shouldReloadOnResume = true;
            return;
        }
        this._cache.reset();
        for (const i in this._tiles) {
            if (this._tiles[i].state !== 'errored')
                this._reloadTile(+i, 'reloading');
        }
    }
    _reloadTile(id, state) {
        const tile = this._tiles[id];
        if (!tile)
            return;
        if (tile.state !== 'loading') {
            tile.state = state;
        }
        this._loadTile(tile, this._tileLoaded.bind(this, tile, id, state));
    }
    _tileLoaded(tile, id, previousState, err) {
        if (err) {
            tile.state = 'errored';
            if (err.status !== 404)
                this._source.fire(new ErrorEvent(err, { tile }));
            else
                this.update(this.transform);
            return;
        }
        tile.timeAdded = exported$1.now();
        if (previousState === 'expired')
            tile.refreshedUponExpiration = true;
        this._setTileReloadTimer(id, tile);
        if (this.getSource().type === 'raster-dem' && tile.dem)
            this._backfillDEM(tile);
        this._state.initializeTileState(tile, this.map ? this.map.painter : null);
        this._source.fire(new Event('data', {
            dataType: 'source',
            tile,
            coord: tile.tileID,
            'sourceCacheId': this.id
        }));
    }
    _backfillDEM(tile) {
        const renderables = this.getRenderableIds();
        for (let i = 0; i < renderables.length; i++) {
            const borderId = renderables[i];
            if (tile.neighboringTiles && tile.neighboringTiles[borderId]) {
                const borderTile = this.getTileByID(borderId);
                fillBorder(tile, borderTile);
                fillBorder(borderTile, tile);
            }
        }
        function fillBorder(tile, borderTile) {
            if (!tile.dem || tile.dem.borderReady)
                return;
            tile.needsHillshadePrepare = true;
            tile.needsDEMTextureUpload = true;
            let dx = borderTile.tileID.canonical.x - tile.tileID.canonical.x;
            const dy = borderTile.tileID.canonical.y - tile.tileID.canonical.y;
            const dim = Math.pow(2, tile.tileID.canonical.z);
            const borderId = borderTile.tileID.key;
            if (dx === 0 && dy === 0)
                return;
            if (Math.abs(dy) > 1) {
                return;
            }
            if (Math.abs(dx) > 1) {
                if (Math.abs(dx + dim) === 1) {
                    dx += dim;
                } else if (Math.abs(dx - dim) === 1) {
                    dx -= dim;
                }
            }
            if (!borderTile.dem || !tile.dem)
                return;
            tile.dem.backfillBorder(borderTile.dem, dx, dy);
            if (tile.neighboringTiles && tile.neighboringTiles[borderId])
                tile.neighboringTiles[borderId].backfilled = true;
        }
    }
    getTile(tileID) {
        return this.getTileByID(tileID.key);
    }
    getTileByID(id) {
        return this._tiles[id];
    }
    _retainLoadedChildren(idealTiles, zoom, maxCoveringZoom, retain) {
        for (const id in this._tiles) {
            let tile = this._tiles[id];
            if (retain[id] || !tile.hasData() || tile.tileID.overscaledZ <= zoom || tile.tileID.overscaledZ > maxCoveringZoom)
                continue;
            let topmostLoadedID = tile.tileID;
            while (tile && tile.tileID.overscaledZ > zoom + 1) {
                const parentID = tile.tileID.scaledTo(tile.tileID.overscaledZ - 1);
                tile = this._tiles[parentID.key];
                if (tile && tile.hasData()) {
                    topmostLoadedID = parentID;
                }
            }
            let tileID = topmostLoadedID;
            while (tileID.overscaledZ > zoom) {
                tileID = tileID.scaledTo(tileID.overscaledZ - 1);
                if (idealTiles[tileID.key]) {
                    retain[topmostLoadedID.key] = topmostLoadedID;
                    break;
                }
            }
        }
    }
    findLoadedParent(tileID, minCoveringZoom) {
        if (tileID.key in this._loadedParentTiles) {
            const parent = this._loadedParentTiles[tileID.key];
            if (parent && parent.tileID.overscaledZ >= minCoveringZoom) {
                return parent;
            } else {
                return null;
            }
        }
        for (let z = tileID.overscaledZ - 1; z >= minCoveringZoom; z--) {
            const parentTileID = tileID.scaledTo(z);
            const tile = this._getLoadedTile(parentTileID);
            if (tile) {
                return tile;
            }
        }
    }
    _getLoadedTile(tileID) {
        const tile = this._tiles[tileID.key];
        if (tile && tile.hasData()) {
            return tile;
        }
        const cachedTile = this._cache.getByKey(this._source.reparseOverscaled ? tileID.wrapped().key : tileID.canonical.key);
        return cachedTile;
    }
    updateCacheSize(transform, tileSize) {
        tileSize = tileSize || this._source.tileSize;
        const widthInTiles = Math.ceil(transform.width / tileSize) + 1;
        const heightInTiles = Math.ceil(transform.height / tileSize) + 1;
        const approxTilesInView = widthInTiles * heightInTiles;
        const commonZoomRange = 5;
        const viewDependentMaxSize = Math.floor(approxTilesInView * commonZoomRange);
        const maxSize = typeof this._maxTileCacheSize === 'number' ? Math.min(this._maxTileCacheSize, viewDependentMaxSize) : viewDependentMaxSize;
        this._cache.setMaxSize(maxSize);
    }
    handleWrapJump(lng) {
        const prevLng = this._prevLng === undefined ? lng : this._prevLng;
        const lngDifference = lng - prevLng;
        const worldDifference = lngDifference / 360;
        const wrapDelta = Math.round(worldDifference);
        this._prevLng = lng;
        if (wrapDelta) {
            const tiles = {};
            for (const key in this._tiles) {
                const tile = this._tiles[key];
                tile.tileID = tile.tileID.unwrapTo(tile.tileID.wrap + wrapDelta);
                tiles[tile.tileID.key] = tile;
            }
            this._tiles = tiles;
            for (const id in this._timers) {
                clearTimeout(this._timers[id]);
                delete this._timers[id];
            }
            for (const id in this._tiles) {
                const tile = this._tiles[id];
                this._setTileReloadTimer(+id, tile);
            }
        }
    }
    update(transform, tileSize, updateForTerrain) {
        this.transform = transform;
        if (!this._sourceLoaded || this._paused || this.transform.freezeTileCoverage) {
            return;
        }
        if (this.usedForTerrain && !updateForTerrain) {
            return;
        }
        this.updateCacheSize(transform, tileSize);
        this.handleWrapJump(this.transform.center.lng);
        this._coveredTiles = {};
        let idealTileIDs;
        if (!this.used && !this.usedForTerrain) {
            idealTileIDs = [];
        } else if (this._source.tileID) {
            idealTileIDs = transform.getVisibleUnwrappedCoordinates(this._source.tileID).map(unwrapped => new OverscaledTileID(unwrapped.canonical.z, unwrapped.wrap, unwrapped.canonical.z, unwrapped.canonical.x, unwrapped.canonical.y));
        } else {
            idealTileIDs = transform.coveringTiles({
                tileSize: tileSize || this._source.tileSize,
                minzoom: this._source.minzoom,
                maxzoom: this._source.maxzoom,
                roundZoom: this._source.roundZoom && !updateForTerrain,
                reparseOverscaled: this._source.reparseOverscaled,
                useElevationData: !!this.transform.elevation && !this.usedForTerrain
            });
            if (this._source.hasTile) {
                idealTileIDs = idealTileIDs.filter(coord => this._source.hasTile(coord));
            }
        }
        const retain = this._updateRetainedTiles(idealTileIDs);
        if (isRasterType(this._source.type) && idealTileIDs.length !== 0) {
            const parentsForFading = {};
            const fadingTiles = {};
            const ids = Object.keys(retain);
            for (const id of ids) {
                const tileID = retain[id];
                const tile = this._tiles[id];
                if (!tile || tile.fadeEndTime && tile.fadeEndTime <= exported$1.now())
                    continue;
                const parentTile = this.findLoadedParent(tileID, Math.max(tileID.overscaledZ - SourceCache.maxOverzooming, this._source.minzoom));
                if (parentTile) {
                    this._addTile(parentTile.tileID);
                    parentsForFading[parentTile.tileID.key] = parentTile.tileID;
                }
                fadingTiles[id] = tileID;
            }
            const minZoom = idealTileIDs[idealTileIDs.length - 1].overscaledZ;
            for (const id in this._tiles) {
                const childTile = this._tiles[id];
                if (retain[id] || !childTile.hasData()) {
                    continue;
                }
                let parentID = childTile.tileID;
                while (parentID.overscaledZ > minZoom) {
                    parentID = parentID.scaledTo(parentID.overscaledZ - 1);
                    const tile = this._tiles[parentID.key];
                    if (tile && tile.hasData() && fadingTiles[parentID.key]) {
                        retain[id] = childTile.tileID;
                        break;
                    }
                }
            }
            for (const id in parentsForFading) {
                if (!retain[id]) {
                    this._coveredTiles[id] = true;
                    retain[id] = parentsForFading[id];
                }
            }
        }
        for (const retainedId in retain) {
            this._tiles[retainedId].clearFadeHold();
        }
        const remove = keysDifference(this._tiles, retain);
        for (const tileID of remove) {
            const tile = this._tiles[tileID];
            if (tile.hasSymbolBuckets && !tile.holdingForFade()) {
                tile.setHoldDuration(this.map._fadeDuration);
            } else if (!tile.hasSymbolBuckets || tile.symbolFadeFinished()) {
                this._removeTile(+tileID);
            }
        }
        this._updateLoadedParentTileCache();
        if (this._onlySymbols && this._source.afterUpdate) {
            this._source.afterUpdate();
        }
    }
    releaseSymbolFadeTiles() {
        for (const id in this._tiles) {
            if (this._tiles[id].holdingForFade()) {
                this._removeTile(+id);
            }
        }
    }
    _updateRetainedTiles(idealTileIDs) {
        const retain = {};
        if (idealTileIDs.length === 0) {
            return retain;
        }
        const checked = {};
        const minZoom = idealTileIDs[idealTileIDs.length - 1].overscaledZ;
        const maxZoom = idealTileIDs[0].overscaledZ;
        const minCoveringZoom = Math.max(maxZoom - SourceCache.maxOverzooming, this._source.minzoom);
        const maxCoveringZoom = Math.max(maxZoom + SourceCache.maxUnderzooming, this._source.minzoom);
        const missingTiles = {};
        for (const tileID of idealTileIDs) {
            const tile = this._addTile(tileID);
            retain[tileID.key] = tileID;
            if (tile.hasData())
                continue;
            if (minZoom < this._source.maxzoom) {
                missingTiles[tileID.key] = tileID;
            }
        }
        this._retainLoadedChildren(missingTiles, minZoom, maxCoveringZoom, retain);
        for (const tileID of idealTileIDs) {
            let tile = this._tiles[tileID.key];
            if (tile.hasData())
                continue;
            if (tileID.canonical.z >= this._source.maxzoom) {
                const childCoord = tileID.children(this._source.maxzoom)[0];
                const childTile = this.getTile(childCoord);
                if (!!childTile && childTile.hasData()) {
                    retain[childCoord.key] = childCoord;
                    continue;
                }
            } else {
                const children = tileID.children(this._source.maxzoom);
                if (retain[children[0].key] && retain[children[1].key] && retain[children[2].key] && retain[children[3].key])
                    continue;
            }
            let parentWasRequested = tile.wasRequested();
            for (let overscaledZ = tileID.overscaledZ - 1; overscaledZ >= minCoveringZoom; --overscaledZ) {
                const parentId = tileID.scaledTo(overscaledZ);
                if (checked[parentId.key])
                    break;
                checked[parentId.key] = true;
                tile = this.getTile(parentId);
                if (!tile && parentWasRequested) {
                    tile = this._addTile(parentId);
                }
                if (tile) {
                    retain[parentId.key] = parentId;
                    parentWasRequested = tile.wasRequested();
                    if (tile.hasData())
                        break;
                }
            }
        }
        return retain;
    }
    _updateLoadedParentTileCache() {
        this._loadedParentTiles = {};
        for (const tileKey in this._tiles) {
            const path = [];
            let parentTile;
            let currentId = this._tiles[tileKey].tileID;
            while (currentId.overscaledZ > 0) {
                if (currentId.key in this._loadedParentTiles) {
                    parentTile = this._loadedParentTiles[currentId.key];
                    break;
                }
                path.push(currentId.key);
                const parentId = currentId.scaledTo(currentId.overscaledZ - 1);
                parentTile = this._getLoadedTile(parentId);
                if (parentTile) {
                    break;
                }
                currentId = parentId;
            }
            for (const key of path) {
                this._loadedParentTiles[key] = parentTile;
            }
        }
    }
    _addTile(tileID) {
        let tile = this._tiles[tileID.key];
        if (tile)
            return tile;
        tile = this._cache.getAndRemove(tileID);
        if (tile) {
            this._setTileReloadTimer(tileID.key, tile);
            tile.tileID = tileID;
            this._state.initializeTileState(tile, this.map ? this.map.painter : null);
            if (this._cacheTimers[tileID.key]) {
                clearTimeout(this._cacheTimers[tileID.key]);
                delete this._cacheTimers[tileID.key];
                this._setTileReloadTimer(tileID.key, tile);
            }
        }
        const cached = Boolean(tile);
        if (!cached) {
            tile = new Tile(tileID, this._source.tileSize * tileID.overscaleFactor(), this.transform.tileZoom);
            this._loadTile(tile, this._tileLoaded.bind(this, tile, tileID.key, tile.state));
        }
        if (!tile)
            return null;
        tile.uses++;
        this._tiles[tileID.key] = tile;
        if (!cached)
            this._source.fire(new Event('dataloading', {
                tile,
                coord: tile.tileID,
                dataType: 'source'
            }));
        return tile;
    }
    _setTileReloadTimer(id, tile) {
        if (id in this._timers) {
            clearTimeout(this._timers[id]);
            delete this._timers[id];
        }
        const expiryTimeout = tile.getExpiryTimeout();
        if (expiryTimeout) {
            this._timers[id] = setTimeout(() => {
                this._reloadTile(id, 'expired');
                delete this._timers[id];
            }, expiryTimeout);
        }
    }
    _removeTile(id) {
        const tile = this._tiles[id];
        if (!tile)
            return;
        tile.uses--;
        delete this._tiles[id];
        if (this._timers[id]) {
            clearTimeout(this._timers[id]);
            delete this._timers[id];
        }
        if (tile.uses > 0)
            return;
        if (tile.hasData() && tile.state !== 'reloading') {
            this._cache.add(tile.tileID, tile, tile.getExpiryTimeout());
        } else {
            tile.aborted = true;
            this._abortTile(tile);
            this._unloadTile(tile);
        }
    }
    clearTiles() {
        this._shouldReloadOnResume = false;
        this._paused = false;
        for (const id in this._tiles)
            this._removeTile(+id);
        this._cache.reset();
    }
    tilesIn(queryGeometry, use3DQuery, visualizeQueryGeometry) {
        const tileResults = [];
        const transform = this.transform;
        if (!transform)
            return tileResults;
        for (const tileID in this._tiles) {
            const tile = this._tiles[tileID];
            if (visualizeQueryGeometry) {
                tile.clearQueryDebugViz();
            }
            if (tile.holdingForFade()) {
                continue;
            }
            const tileResult = queryGeometry.containsTile(tile, transform, use3DQuery);
            if (tileResult) {
                tileResults.push(tileResult);
            }
        }
        return tileResults;
    }
    getVisibleCoordinates(symbolLayer) {
        const coords = this.getRenderableIds(symbolLayer).map(id => this._tiles[id].tileID);
        for (const coord of coords) {
            coord.projMatrix = this.transform.calculateProjMatrix(coord.toUnwrapped());
        }
        return coords;
    }
    hasTransition() {
        if (this._source.hasTransition()) {
            return true;
        }
        if (isRasterType(this._source.type)) {
            for (const id in this._tiles) {
                const tile = this._tiles[id];
                if (tile.fadeEndTime !== undefined && tile.fadeEndTime >= exported$1.now()) {
                    return true;
                }
            }
        }
        return false;
    }
    setFeatureState(sourceLayer, featureId, state) {
        sourceLayer = sourceLayer || '_geojsonTileLayer';
        this._state.updateState(sourceLayer, featureId, state);
    }
    removeFeatureState(sourceLayer, featureId, key) {
        sourceLayer = sourceLayer || '_geojsonTileLayer';
        this._state.removeFeatureState(sourceLayer, featureId, key);
    }
    getFeatureState(sourceLayer, featureId) {
        sourceLayer = sourceLayer || '_geojsonTileLayer';
        return this._state.getState(sourceLayer, featureId);
    }
    setDependencies(tileKey, namespace, dependencies) {
        const tile = this._tiles[tileKey];
        if (tile) {
            tile.setDependencies(namespace, dependencies);
        }
    }
    reloadTilesForDependencies(namespaces, keys) {
        for (const id in this._tiles) {
            const tile = this._tiles[id];
            if (tile.hasDependency(namespaces, keys)) {
                this._reloadTile(+id, 'reloading');
            }
        }
        this._cache.filter(tile => !tile.hasDependency(namespaces, keys));
    }
}
SourceCache.maxOverzooming = 10;
SourceCache.maxUnderzooming = 3;
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;
}
function isRasterType(type) {
    return type === 'raster' || type === 'image' || type === 'video';
}

class Elevation {
    getAtPointOrZero(point, defaultIfNotLoaded = 0) {
        return this.getAtPoint(point, defaultIfNotLoaded) || 0;
    }
    getAtPoint(point, defaultIfNotLoaded, exaggerated = true) {
        if (defaultIfNotLoaded == null)
            defaultIfNotLoaded = null;
        const src = this._source();
        if (!src)
            return defaultIfNotLoaded;
        if (point.y < 0 || point.y > 1) {
            return defaultIfNotLoaded;
        }
        const cache = src;
        const z = cache.getSource().maxzoom;
        const tiles = 1 << z;
        const wrap = Math.floor(point.x);
        const px = point.x - wrap;
        const tileID = new OverscaledTileID(z, wrap, z, Math.floor(px * tiles), Math.floor(point.y * tiles));
        const demTile = this.findDEMTileFor(tileID);
        if (!(demTile && demTile.dem)) {
            return defaultIfNotLoaded;
        }
        const dem = demTile.dem;
        const tilesAtTileZoom = 1 << demTile.tileID.canonical.z;
        const x = (px * tilesAtTileZoom - demTile.tileID.canonical.x) * dem.dim;
        const y = (point.y * tilesAtTileZoom - demTile.tileID.canonical.y) * dem.dim;
        const i = Math.floor(x);
        const j = Math.floor(y);
        const exaggeration = exaggerated ? this.exaggeration() : 1;
        return exaggeration * number(number(dem.get(i, j), dem.get(i, j + 1), y - j), number(dem.get(i + 1, j), dem.get(i + 1, j + 1), y - j), x - i);
    }
    getAtTileOffset(tileID, x, y) {
        const tilesAtTileZoom = 1 << tileID.canonical.z;
        return this.getAtPointOrZero(new MercatorCoordinate(tileID.wrap + (tileID.canonical.x + x / EXTENT) / tilesAtTileZoom, (tileID.canonical.y + y / EXTENT) / tilesAtTileZoom));
    }
    getForTilePoints(tileID, points, interpolated, useDemTile) {
        const helper = DEMSampler.create(this, tileID, useDemTile);
        if (!helper) {
            return false;
        }
        points.forEach(p => {
            p[2] = this.exaggeration() * helper.getElevationAt(p[0], p[1], interpolated);
        });
        return true;
    }
    getMinMaxForTile(tileID) {
        const demTile = this.findDEMTileFor(tileID);
        if (!(demTile && demTile.dem)) {
            return null;
        }
        const dem = demTile.dem;
        const tree = dem.tree;
        const demTileID = demTile.tileID;
        const scale = 1 << tileID.canonical.z - demTileID.canonical.z;
        let xOffset = tileID.canonical.x / scale - demTileID.canonical.x;
        let yOffset = tileID.canonical.y / scale - demTileID.canonical.y;
        let index = 0;
        for (let i = 0; i < tileID.canonical.z - demTileID.canonical.z; i++) {
            if (tree.leaves[index])
                break;
            xOffset *= 2;
            yOffset *= 2;
            const childOffset = 2 * Math.floor(yOffset) + Math.floor(xOffset);
            index = tree.childOffsets[index] + childOffset;
            xOffset = xOffset % 1;
            yOffset = yOffset % 1;
        }
        return {
            min: this.exaggeration() * tree.minimums[index],
            max: this.exaggeration() * tree.maximums[index]
        };
    }
    getMinElevationBelowMSL() {
        throw new Error('Pure virtual method called.');
    }
    raycast(position, dir, exaggeration) {
        throw new Error('Pure virtual method called.');
    }
    pointCoordinate(screenPoint) {
        throw new Error('Pure virtual method called.');
    }
    _source() {
        throw new Error('Pure virtual method called.');
    }
    exaggeration() {
        throw new Error('Pure virtual method called.');
    }
    findDEMTileFor(_) {
        throw new Error('Pure virtual method called.');
    }
    get visibleDemTiles() {
        throw new Error('Getter must be implemented in subclass.');
    }
}
class DEMSampler {
    constructor(demTile, scale, offset) {
        this._demTile = demTile;
        this._dem = this._demTile.dem;
        this._scale = scale;
        this._offset = offset;
    }
    static create(elevation, tileID, useDemTile) {
        const demTile = useDemTile || elevation.findDEMTileFor(tileID);
        if (!(demTile && demTile.dem)) {
            return;
        }
        const dem = demTile.dem;
        const demTileID = demTile.tileID;
        const scale = 1 << tileID.canonical.z - demTileID.canonical.z;
        const xOffset = (tileID.canonical.x / scale - demTileID.canonical.x) * dem.dim;
        const yOffset = (tileID.canonical.y / scale - demTileID.canonical.y) * dem.dim;
        const k = demTile.tileSize / EXTENT / scale;
        return new DEMSampler(demTile, k, [
            xOffset,
            yOffset
        ]);
    }
    tileCoordToPixel(x, y) {
        const px = x * this._scale + this._offset[0];
        const py = y * this._scale + this._offset[1];
        const i = Math.floor(px);
        const j = Math.floor(py);
        return new pointGeometry(i, j);
    }
    getElevationAt(x, y, interpolated, clampToEdge) {
        const px = x * this._scale + this._offset[0];
        const py = y * this._scale + this._offset[1];
        const i = Math.floor(px);
        const j = Math.floor(py);
        const dem = this._dem;
        clampToEdge = !!clampToEdge;
        return interpolated ? number(number(dem.get(i, j, clampToEdge), dem.get(i, j + 1, clampToEdge), py - j), number(dem.get(i + 1, j, clampToEdge), dem.get(i + 1, j + 1, clampToEdge), py - j), px - i) : dem.get(i, j, clampToEdge);
    }
    getElevationAtPixel(x, y, clampToEdge) {
        return this._dem.get(x, y, !!clampToEdge);
    }
    getMeterToDEM(lat) {
        return (1 << this._demTile.tileID.canonical.z) * mercatorZfromAltitude(1, lat) * this._dem.stride;
    }
}

class FeatureIndex {
    constructor(tileID, promoteId) {
        this.tileID = tileID;
        this.x = tileID.canonical.x;
        this.y = tileID.canonical.y;
        this.z = tileID.canonical.z;
        this.grid = new gridIndex(EXTENT, 16, 0);
        this.featureIndexArray = new FeatureIndexArray();
        this.promoteId = promoteId;
    }
    insert(feature, geometry, featureIndex, sourceLayerIndex, bucketIndex, layoutVertexArrayOffset = 0) {
        const key = this.featureIndexArray.length;
        this.featureIndexArray.emplaceBack(featureIndex, sourceLayerIndex, bucketIndex, layoutVertexArrayOffset);
        const grid = this.grid;
        for (let r = 0; r < geometry.length; r++) {
            const ring = geometry[r];
            const bbox = [
                Infinity,
                Infinity,
                -Infinity,
                -Infinity
            ];
            for (let i = 0; i < ring.length; i++) {
                const p = ring[i];
                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] < EXTENT && bbox[1] < EXTENT && bbox[2] >= 0 && bbox[3] >= 0) {
                grid.insert(key, bbox[0], bbox[1], bbox[2], bbox[3]);
            }
        }
    }
    loadVTLayers() {
        if (!this.vtLayers) {
            this.vtLayers = new vectorTile.VectorTile(new pbf(this.rawTileData)).layers;
            this.sourceLayerCoder = new DictionaryCoder(this.vtLayers ? Object.keys(this.vtLayers).sort() : ['_geojsonTileLayer']);
        }
        return this.vtLayers;
    }
    query(args, styleLayers, serializedLayers, sourceFeatureState) {
        this.loadVTLayers();
        const params = args.params || {}, filter = createFilter(params.filter);
        const tilespaceGeometry = args.tileResult;
        const transform = args.transform;
        const bounds = tilespaceGeometry.bufferedTilespaceBounds;
        const queryPredicate = (bx1, by1, bx2, by2) => {
            return polygonIntersectsBox(tilespaceGeometry.bufferedTilespaceGeometry, bx1, by1, bx2, by2);
        };
        const matching = this.grid.query(bounds.min.x, bounds.min.y, bounds.max.x, bounds.max.y, queryPredicate);
        matching.sort(topDownFeatureComparator);
        let elevationHelper = null;
        if (transform.elevation && matching.length > 0) {
            elevationHelper = DEMSampler.create(transform.elevation, this.tileID);
        }
        const result = {};
        let previousIndex;
        for (let k = 0; k < matching.length; k++) {
            const index = matching[k];
            if (index === previousIndex)
                continue;
            previousIndex = index;
            const match = this.featureIndexArray.get(index);
            let featureGeometry = null;
            this.loadMatchingFeature(result, match, filter, params.layers, params.availableImages, styleLayers, serializedLayers, sourceFeatureState, (feature, styleLayer, featureState, layoutVertexArrayOffset = 0) => {
                if (!featureGeometry) {
                    featureGeometry = loadGeometry(feature);
                }
                return styleLayer.queryIntersectsFeature(tilespaceGeometry, feature, featureState, featureGeometry, this.z, args.transform, args.pixelPosMatrix, elevationHelper, layoutVertexArrayOffset);
            });
        }
        return result;
    }
    loadMatchingFeature(result, featureIndexData, filter, filterLayerIDs, availableImages, styleLayers, serializedLayers, sourceFeatureState, intersectionTest) {
        const {featureIndex, bucketIndex, sourceLayerIndex, layoutVertexArrayOffset} = featureIndexData;
        const layerIDs = this.bucketLayerIDs[bucketIndex];
        if (filterLayerIDs && !arraysIntersect(filterLayerIDs, layerIDs))
            return;
        const sourceLayerName = this.sourceLayerCoder.decode(sourceLayerIndex);
        const sourceLayer = this.vtLayers[sourceLayerName];
        const feature = sourceLayer.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 (let l = 0; l < layerIDs.length; l++) {
            const layerID = layerIDs[l];
            if (filterLayerIDs && filterLayerIDs.indexOf(layerID) < 0) {
                continue;
            }
            const styleLayer = styleLayers[layerID];
            if (!styleLayer)
                continue;
            let featureState = {};
            if (id !== undefined && sourceFeatureState) {
                featureState = sourceFeatureState.getState(styleLayer.sourceLayer || '_geojsonTileLayer', id);
            }
            const serializedLayer = extend$1({}, 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, layoutVertexArrayOffset);
            if (!intersectionZ) {
                continue;
            }
            const geojsonFeature = new Feature(feature, this.z, this.x, this.y, id);
            geojsonFeature.layer = serializedLayer;
            let layerResult = result[layerID];
            if (layerResult === undefined) {
                layerResult = result[layerID] = [];
            }
            layerResult.push({
                featureIndex,
                feature: geojsonFeature,
                intersectionZ
            });
        }
    }
    lookupSymbolFeatures(symbolFeatureIndexes, serializedLayers, bucketIndex, sourceLayerIndex, filterSpec, filterLayerIDs, availableImages, styleLayers) {
        const result = {};
        this.loadVTLayers();
        const filter = createFilter(filterSpec);
        for (const symbolFeatureIndex of symbolFeatureIndexes) {
            this.loadMatchingFeature(result, {
                bucketIndex,
                sourceLayerIndex,
                featureIndex: symbolFeatureIndex,
                layoutVertexArrayOffset: 0
            }, 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);
        }
        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 && prop.evaluate ? prop.evaluate(feature, featureState, availableImages) : prop;
    });
}
function topDownFeatureComparator(a, b) {
    return b - a;
}

const glyphPadding = 1;
const localGlyphPadding = glyphPadding * SDF_SCALE;
class GlyphAtlas {
    constructor(stacks) {
        const positions = {};
        const bins = [];
        for (const stack in stacks) {
            const glyphs = stacks[stack];
            const stackPositions = positions[stack] = {};
            for (const id in glyphs) {
                const src = glyphs[+id];
                if (!src || src.bitmap.width === 0 || src.bitmap.height === 0)
                    continue;
                const padding = src.metrics.localGlyph ? localGlyphPadding : glyphPadding;
                const bin = {
                    x: 0,
                    y: 0,
                    w: src.bitmap.width + 2 * padding,
                    h: src.bitmap.height + 2 * padding
                };
                bins.push(bin);
                stackPositions[id] = {
                    rect: bin,
                    metrics: src.metrics
                };
            }
        }
        const {w, h} = potpack(bins);
        const image = new AlphaImage({
            width: w || 1,
            height: h || 1
        });
        for (const stack in stacks) {
            const glyphs = stacks[stack];
            for (const id in glyphs) {
                const src = glyphs[+id];
                if (!src || src.bitmap.width === 0 || src.bitmap.height === 0)
                    continue;
                const bin = positions[stack][id].rect;
                const padding = src.metrics.localGlyph ? localGlyphPadding : glyphPadding;
                AlphaImage.copy(src.bitmap, image, {
                    x: 0,
                    y: 0
                }, {
                    x: bin.x + padding,
                    y: bin.y + padding
                }, src.bitmap);
            }
        }
        this.image = image;
        this.positions = positions;
    }
}
register('GlyphAtlas', GlyphAtlas);

class WorkerTile {
    constructor(params) {
        this.tileID = new OverscaledTileID(params.tileID.overscaledZ, params.tileID.wrap, params.tileID.canonical.z, params.tileID.canonical.x, params.tileID.canonical.y);
        this.tileZoom = params.tileZoom;
        this.uid = params.uid;
        this.zoom = params.zoom;
        this.pixelRatio = params.pixelRatio;
        this.tileSize = params.tileSize;
        this.source = params.source;
        this.overscaling = this.tileID.overscaleFactor();
        this.showCollisionBoxes = params.showCollisionBoxes;
        this.collectResourceTiming = !!params.collectResourceTiming;
        this.returnDependencies = !!params.returnDependencies;
        this.promoteId = params.promoteId;
        this.enableTerrain = !!params.enableTerrain;
        this.isSymbolTile = params.isSymbolTile;
    }
    parse(data, layerIndex, availableImages, actor, callback) {
        this.status = 'parsing';
        this.data = data;
        this.collisionBoxArray = new CollisionBoxArray();
        const sourceLayerCoder = new DictionaryCoder(Object.keys(data.layers).sort());
        const featureIndex = new FeatureIndex(this.tileID, this.promoteId);
        featureIndex.bucketLayerIDs = [];
        const buckets = {};
        const lineAtlas = new LineAtlas(256, 256);
        const options = {
            featureIndex,
            iconDependencies: {},
            patternDependencies: {},
            glyphDependencies: {},
            lineAtlas,
            availableImages
        };
        const layerFamilies = layerIndex.familiesBySource[this.source];
        for (const sourceLayerId in layerFamilies) {
            const sourceLayer = data.layers[sourceLayerId];
            if (!sourceLayer) {
                continue;
            }
            let anySymbolLayers = false;
            let anyOtherLayers = false;
            for (const family of layerFamilies[sourceLayerId]) {
                if (family[0].type === 'symbol') {
                    anySymbolLayers = true;
                } else {
                    anyOtherLayers = true;
                }
            }
            if (this.isSymbolTile === true && !anySymbolLayers) {
                continue;
            } else if (this.isSymbolTile === false && !anyOtherLayers) {
                continue;
            }
            if (sourceLayer.version === 1) {
                warnOnce(`Vector tile source "${ this.source }" layer "${ sourceLayerId }" ` + `does not use vector tile spec v2 and therefore may have some rendering errors.`);
            }
            const sourceLayerIndex = sourceLayerCoder.encode(sourceLayerId);
            const features = [];
            for (let index = 0; index < sourceLayer.length; index++) {
                const feature = sourceLayer.feature(index);
                const id = featureIndex.getId(feature, sourceLayerId);
                features.push({
                    feature,
                    id,
                    index,
                    sourceLayerIndex
                });
            }
            for (const family of layerFamilies[sourceLayerId]) {
                const layer = family[0];
                if (this.isSymbolTile !== undefined && layer.type === 'symbol' !== this.isSymbolTile)
                    continue;
                if (layer.minzoom && this.zoom < Math.floor(layer.minzoom))
                    continue;
                if (layer.maxzoom && this.zoom >= layer.maxzoom)
                    continue;
                if (layer.visibility === 'none')
                    continue;
                recalculateLayers(family, this.zoom, availableImages);
                const bucket = buckets[layer.id] = layer.createBucket({
                    index: featureIndex.bucketLayerIDs.length,
                    layers: family,
                    zoom: this.zoom,
                    pixelRatio: this.pixelRatio,
                    overscaling: this.overscaling,
                    collisionBoxArray: this.collisionBoxArray,
                    sourceLayerIndex,
                    sourceID: this.source,
                    enableTerrain: this.enableTerrain
                });
                bucket.populate(features, options, this.tileID.canonical);
                featureIndex.bucketLayerIDs.push(family.map(l => l.id));
            }
        }
        lineAtlas.trim();
        let error;
        let glyphMap;
        let iconMap;
        let patternMap;
        const taskMetadata = {
            type: 'maybePrepare',
            isSymbolTile: this.isSymbolTile,
            zoom: this.zoom
        };
        const stacks = mapObject(options.glyphDependencies, glyphs => Object.keys(glyphs).map(Number));
        if (Object.keys(stacks).length) {
            actor.send('getGlyphs', {
                uid: this.uid,
                stacks
            }, (err, result) => {
                if (!error) {
                    error = err;
                    glyphMap = result;
                    maybePrepare.call(this);
                }
            }, undefined, false, taskMetadata);
        } else {
            glyphMap = {};
        }
        const icons = Object.keys(options.iconDependencies);
        if (icons.length) {
            actor.send('getImages', {
                icons,
                source: this.source,
                tileID: this.tileID,
                type: 'icons'
            }, (err, result) => {
                if (!error) {
                    error = err;
                    iconMap = result;
                    maybePrepare.call(this);
                }
            }, undefined, false, taskMetadata);
        } else {
            iconMap = {};
        }
        const patterns = Object.keys(options.patternDependencies);
        if (patterns.length) {
            actor.send('getImages', {
                icons: patterns,
                source: this.source,
                tileID: this.tileID,
                type: 'patterns'
            }, (err, result) => {
                if (!error) {
                    error = err;
                    patternMap = result;
                    maybePrepare.call(this);
                }
            }, undefined, false, taskMetadata);
        } else {
            patternMap = {};
        }
        maybePrepare.call(this);
        function maybePrepare() {
            if (error) {
                return callback(error);
            } else if (glyphMap && iconMap && patternMap) {
                const glyphAtlas = new GlyphAtlas(glyphMap);
                const imageAtlas = new ImageAtlas(iconMap, patternMap);
                for (const key in buckets) {
                    const bucket = buckets[key];
                    if (bucket instanceof SymbolBucket) {
                        recalculateLayers(bucket.layers, this.zoom, availableImages);
                        performSymbolLayout(bucket, glyphMap, glyphAtlas.positions, iconMap, imageAtlas.iconPositions, this.showCollisionBoxes, this.tileID.canonical, this.tileZoom);
                    } else if (bucket.hasPattern && (bucket instanceof LineBucket || bucket instanceof FillBucket || bucket instanceof FillExtrusionBucket)) {
                        recalculateLayers(bucket.layers, this.zoom, availableImages);
                        bucket.addFeatures(options, this.tileID.canonical, imageAtlas.patternPositions);
                    }
                }
                this.status = 'done';
                callback(null, {
                    buckets: values(buckets).filter(b => !b.isEmpty()),
                    featureIndex,
                    collisionBoxArray: this.collisionBoxArray,
                    glyphAtlasImage: glyphAtlas.image,
                    lineAtlas,
                    imageAtlas,
                    glyphMap: this.returnDependencies ? glyphMap : null,
                    iconMap: this.returnDependencies ? iconMap : null,
                    glyphPositions: this.returnDependencies ? glyphAtlas.positions : null
                });
            }
        }
    }
}
function recalculateLayers(layers, zoom, availableImages) {
    const parameters = new EvaluationParameters(zoom);
    for (const layer of layers) {
        layer.recalculate(parameters, availableImages);
    }
}

class DedupedRequest {
    constructor(scheduler) {
        this.entries = {};
        this.scheduler = scheduler;
    }
    request(key, metadata, request, callback) {
        const entry = this.entries[key] = this.entries[key] || { callbacks: [] };
        if (entry.result) {
            const [err, result] = entry.result;
            if (this.scheduler) {
                this.scheduler.add(() => {
                    callback(err, result);
                }, metadata);
            } else {
                callback(err, result);
            }
            return () => {
            };
        }
        entry.callbacks.push(callback);
        if (!entry.cancel) {
            entry.cancel = request((err, result) => {
                entry.result = [
                    err,
                    result
                ];
                for (const cb of entry.callbacks) {
                    if (this.scheduler) {
                        this.scheduler.add(() => {
                            cb(err, result);
                        }, metadata);
                    } else {
                        cb(err, result);
                    }
                }
                setTimeout(() => delete this.entries[key], 1000 * 3);
            });
        }
        return () => {
            if (entry.result)
                return;
            entry.callbacks = entry.callbacks.filter(cb => cb !== callback);
            if (!entry.callbacks.length) {
                entry.cancel();
                delete this.entries[key];
            }
        };
    }
}
function loadVectorTile(params, callback, skipParse) {
    const key = JSON.stringify(params.request);
    const makeRequest = callback => {
        const request = getArrayBuffer(params.request, (err, data, cacheControl, expires) => {
            if (err) {
                callback(err);
            } else if (data) {
                callback(null, {
                    vectorTile: skipParse ? undefined : new vectorTile.VectorTile(new pbf(data)),
                    rawData: data,
                    cacheControl,
                    expires
                });
            }
        });
        return () => {
            request.cancel();
            callback();
        };
    };
    if (params.data) {
        this.deduped.entries[key] = {
            result: [
                null,
                params.data
            ]
        };
    }
    const callbackMetadata = {
        type: 'parseTile',
        isSymbolTile: params.isSymbolTile,
        zoom: params.tileZoom
    };
    return this.deduped.request(key, callbackMetadata, makeRequest, callback);
}
class VectorTileWorkerSource extends Evented {
    constructor(actor, layerIndex, availableImages, isSpriteLoaded, loadVectorData) {
        super();
        this.actor = actor;
        this.layerIndex = layerIndex;
        this.availableImages = availableImages;
        this.loadVectorData = loadVectorData || loadVectorTile;
        this.loading = {};
        this.loaded = {};
        this.deduped = new DedupedRequest(actor.scheduler);
        this.isSpriteLoaded = isSpriteLoaded;
        this.scheduler = actor.scheduler;
    }
    loadTile(params, callback) {
        const uid = params.uid;
        const requestParam = params && params.request;
        const perf = requestParam && requestParam.collectResourceTiming;
        const workerTile = this.loading[uid] = new WorkerTile(params);
        workerTile.abort = this.loadVectorData(params, (err, response) => {
            const aborted = !this.loading[uid];
            delete this.loading[uid];
            if (aborted || err || !response) {
                workerTile.status = 'done';
                if (!aborted)
                    this.loaded[uid] = workerTile;
                return callback(err);
            }
            const rawTileData = response.rawData;
            const cacheControl = {};
            if (response.expires)
                cacheControl.expires = response.expires;
            if (response.cacheControl)
                cacheControl.cacheControl = response.cacheControl;
            workerTile.vectorTile = response.vectorTile || new vectorTile.VectorTile(new pbf(rawTileData));
            const parseTile = () => {
                workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor, (err, result) => {
                    if (err || !result)
                        return callback(err);
                    const resourceTiming = {};
                    if (perf) {
                        const resourceTimingData = getPerformanceMeasurement(requestParam);
                        if (resourceTimingData.length > 0) {
                            resourceTiming.resourceTiming = JSON.parse(JSON.stringify(resourceTimingData));
                        }
                    }
                    callback(null, extend$1({ rawTileData: rawTileData.slice(0) }, result, cacheControl, resourceTiming));
                });
            };
            if (this.isSpriteLoaded) {
                parseTile();
            } else {
                this.once('isSpriteLoaded', () => {
                    if (this.scheduler) {
                        const metadata = {
                            type: 'parseTile',
                            isSymbolTile: params.isSymbolTile,
                            zoom: params.tileZoom
                        };
                        this.scheduler.add(parseTile, metadata);
                    } else {
                        parseTile();
                    }
                });
            }
            this.loaded = this.loaded || {};
            this.loaded[uid] = workerTile;
        });
    }
    reloadTile(params, callback) {
        const loaded = this.loaded, uid = params.uid, vtSource = this;
        if (loaded && loaded[uid]) {
            const workerTile = loaded[uid];
            workerTile.showCollisionBoxes = params.showCollisionBoxes;
            workerTile.enableTerrain = !!params.enableTerrain;
            const done = (err, data) => {
                const reloadCallback = workerTile.reloadCallback;
                if (reloadCallback) {
                    delete workerTile.reloadCallback;
                    workerTile.parse(workerTile.vectorTile, vtSource.layerIndex, this.availableImages, vtSource.actor, reloadCallback);
                }
                callback(err, data);
            };
            if (workerTile.status === 'parsing') {
                workerTile.reloadCallback = done;
            } else if (workerTile.status === 'done') {
                if (workerTile.vectorTile) {
                    workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor, done);
                } else {
                    done();
                }
            }
        }
    }
    abortTile(params, callback) {
        const uid = params.uid;
        const tile = this.loading[uid];
        if (tile) {
            if (tile.abort)
                tile.abort();
            delete this.loading[uid];
        }
        callback();
    }
    removeTile(params, callback) {
        const loaded = this.loaded, uid = params.uid;
        if (loaded && loaded[uid]) {
            delete loaded[uid];
        }
        callback();
    }
}

var refProperties = [
    'type',
    'source',
    'source-layer',
    'minzoom',
    'maxzoom',
    'filter',
    'layout'
];

exports.ARRAY_TYPE = ARRAY_TYPE;
exports.Actor = Actor;
exports.CanonicalTileID = CanonicalTileID;
exports.Color = Color$1;
exports.ColorMode = ColorMode;
exports.CullFaceMode = CullFaceMode;
exports.DEMData = DEMData;
exports.DataConstantProperty = DataConstantProperty;
exports.DedupedRequest = DedupedRequest;
exports.DepthMode = DepthMode;
exports.EXTENT = EXTENT;
exports.Elevation = Elevation;
exports.ErrorEvent = ErrorEvent;
exports.EvaluationParameters = EvaluationParameters;
exports.Event = Event;
exports.Evented = Evented;
exports.GlyphManager = GlyphManager;
exports.ImagePosition = ImagePosition;
exports.LineAtlas = LineAtlas;
exports.LngLat = LngLat;
exports.LngLatBounds = LngLatBounds;
exports.LocalGlyphMode = LocalGlyphMode;
exports.MAX_SAFE_INTEGER = MAX_SAFE_INTEGER;
exports.MercatorCoordinate = MercatorCoordinate;
exports.ONE_EM = ONE_EM;
exports.OverscaledTileID = OverscaledTileID;
exports.Properties = Properties;
exports.RGBAImage = RGBAImage;
exports.RequestManager = RequestManager;
exports.ResourceType = ResourceType;
exports.SegmentVector = SegmentVector;
exports.SourceCache = SourceCache;
exports.StencilMode = StencilMode;
exports.StructArrayLayout1ui2 = StructArrayLayout1ui2;
exports.StructArrayLayout2f1f2i16 = StructArrayLayout2f1f2i16;
exports.StructArrayLayout2i4 = StructArrayLayout2i4;
exports.StructArrayLayout2ui4 = StructArrayLayout2ui4;
exports.StructArrayLayout3f12 = StructArrayLayout3f12;
exports.StructArrayLayout3ui6 = StructArrayLayout3ui6;
exports.StructArrayLayout4i8 = StructArrayLayout4i8;
exports.Texture = Texture;
exports.Tile = Tile;
exports.Transitionable = Transitionable;
exports.Uniform1f = Uniform1f;
exports.Uniform1i = Uniform1i;
exports.Uniform2f = Uniform2f;
exports.Uniform3f = Uniform3f;
exports.Uniform4f = Uniform4f;
exports.UniformColor = UniformColor;
exports.UniformMatrix3f = UniformMatrix3f;
exports.UniformMatrix4f = UniformMatrix4f;
exports.UnwrappedTileID = UnwrappedTileID;
exports.ValidationError = ValidationError;
exports.VectorTileWorkerSource = VectorTileWorkerSource;
exports.WritingMode = WritingMode;
exports.ZoomHistory = ZoomHistory;
exports.add = add;
exports.addDynamicAttributes = addDynamicAttributes;
exports.altitudeFromMercatorZ = altitudeFromMercatorZ;
exports.asyncAll = asyncAll;
exports.bezier = bezier;
exports.bindAll = bindAll;
exports.bufferConvexPolygon = bufferConvexPolygon;
exports.cacheEntryPossiblyAdded = cacheEntryPossiblyAdded;
exports.clamp = clamp;
exports.clearTileCache = clearTileCache;
exports.clipLine = clipLine;
exports.clone = clone;
exports.clone$1 = clone$1;
exports.clone$2 = clone$2;
exports.collisionCircleLayout = collisionCircleLayout;
exports.config = config;
exports.conjugate = conjugate;
exports.copy = copy;
exports.create = create$2;
exports.create$1 = create$3;
exports.create$2 = create$4;
exports.createExpression = createExpression;
exports.createFilter = createFilter;
exports.createLayout = createLayout;
exports.createStyleLayer = createStyleLayer;
exports.cross = cross;
exports.degToRad = degToRad;
exports.div = div;
exports.dot = dot$2;
exports.dot$1 = dot$1;
exports.ease = ease;
exports.easeCubicInOut = easeCubicInOut;
exports.emitValidationErrors = emitValidationErrors;
exports.endsWith = endsWith;
exports.enforceCacheSizeLimit = enforceCacheSizeLimit;
exports.evaluateSizeForFeature = evaluateSizeForFeature;
exports.evaluateSizeForZoom = evaluateSizeForZoom;
exports.evaluateVariableOffset = evaluateVariableOffset;
exports.evented = evented;
exports.exactEquals = exactEquals;
exports.exactEquals$1 = exactEquals$2;
exports.exported = exported$1;
exports.exported$1 = exported;
exports.extend = extend$1;
exports.filterObject = filterObject;
exports.fromMat4 = fromMat4;
exports.fromQuat = fromQuat;
exports.fromRotation = fromRotation;
exports.fromScaling = fromScaling;
exports.furthestTileCorner = furthestTileCorner;
exports.getAABBPointSquareDist = getAABBPointSquareDist;
exports.getAnchorAlignment = getAnchorAlignment;
exports.getAnchorJustification = getAnchorJustification;
exports.getBounds = getBounds;
exports.getImage = getImage;
exports.getJSON = getJSON;
exports.getPerformanceMeasurement = getPerformanceMeasurement;
exports.getRTLTextPluginStatus = getRTLTextPluginStatus;
exports.getReferrer = getReferrer;
exports.getVideo = getVideo;
exports.identity = identity$1;
exports.identity$1 = identity;
exports.invert = invert;
exports.isMapAuthenticated = isMapAuthenticated;
exports.isMapboxURL = isMapboxURL;
exports.latFromMercatorY = latFromMercatorY;
exports.len = len;
exports.length = length$2;
exports.length$1 = length;
exports.loadVectorTile = loadVectorTile;
exports.makeRequest = makeRequest;
exports.mercatorXfromLng = mercatorXfromLng;
exports.mercatorYfromLat = mercatorYfromLat;
exports.mercatorZfromAltitude = mercatorZfromAltitude;
exports.mul = mul;
exports.mul$1 = mul$2;
exports.mul$2 = mul$1;
exports.multiply = multiply$2;
exports.multiply$1 = multiply$1;
exports.nextPowerOfTwo = nextPowerOfTwo;
exports.normalize = normalize$2;
exports.normalize$1 = normalize;
exports.number = number;
exports.ortho = ortho;
exports.pbf = pbf;
exports.perspective = perspective;
exports.pick = pick;
exports.plugin = plugin;
exports.pointGeometry = pointGeometry;
exports.polygonIntersectsBox = polygonIntersectsBox;
exports.polygonIntersectsPolygon = polygonIntersectsPolygon;
exports.polygonizeBounds = polygonizeBounds;
exports.posAttributes = posAttributes;
exports.postTurnstileEvent = postTurnstileEvent;
exports.potpack = potpack;
exports.prevPowerOfTwo = prevPowerOfTwo;
exports.radToDeg = radToDeg;
exports.refProperties = refProperties;
exports.registerForPluginStateChange = registerForPluginStateChange;
exports.removeAuthState = removeAuthState;
exports.renderColorRamp = renderColorRamp;
exports.rotateX = rotateX$1;
exports.rotateX$1 = rotateX;
exports.rotateY = rotateY$1;
exports.rotateZ = rotateZ$1;
exports.rotateZ$1 = rotateZ;
exports.scale = scale$1;
exports.scale$1 = scale$2;
exports.scale$2 = scale;
exports.scaleAndAdd = scaleAndAdd;
exports.setCacheLimits = setCacheLimits;
exports.setRTLTextPlugin = setRTLTextPlugin;
exports.smoothstep = smoothstep;
exports.spec = spec;
exports.storeAuthState = storeAuthState;
exports.sub = sub;
exports.subtract = subtract;
exports.symbolSize = symbolSize;
exports.transformMat3 = transformMat3;
exports.transformMat4 = transformMat4;
exports.transformMat4$1 = transformMat4$1;
exports.transformQuat = transformQuat;
exports.translate = translate;
exports.triggerPluginCompletionEvent = triggerPluginCompletionEvent;
exports.uniqueId = uniqueId;
exports.validateCustomStyleLayer = validateCustomStyleLayer;
exports.validateFog = validateFog;
exports.validateLight = validateLight;
exports.validateStyle = validateStyle;
exports.values = values;
exports.vectorTile = vectorTile;
exports.vectorTile$1 = vectorTile$1;
exports.version = version;
exports.warnOnce = warnOnce;
exports.window = window$1;
exports.wrap = wrap;

}));

define(['./shared'], (function (ref_properties) { 'use strict';

function stringify(obj) {
    const type = typeof obj;
    if (type === 'number' || type === 'boolean' || type === 'string' || obj === undefined || obj === null)
        return JSON.stringify(obj);
    if (Array.isArray(obj)) {
        let str = '[';
        for (const val of obj) {
            str += `${ stringify(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(obj[keys[i]]) },`;
    }
    return `${ str }}`;
}
function getKey(layer) {
    let key = '';
    for (const k of ref_properties.refProperties) {
        key += `/${ stringify(layer[k]) }`;
    }
    return key;
}
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;
}

class StyleLayerIndex {
    constructor(layerConfigs) {
        this.keyCache = {};
        if (layerConfigs) {
            this.replace(layerConfigs);
        }
    }
    replace(layerConfigs) {
        this._layerConfigs = {};
        this._layers = {};
        this.update(layerConfigs, []);
    }
    update(layerConfigs, removedIds) {
        for (const layerConfig of layerConfigs) {
            this._layerConfigs[layerConfig.id] = layerConfig;
            const layer = this._layers[layerConfig.id] = ref_properties.createStyleLayer(layerConfig);
            layer._featureFilter = ref_properties.createFilter(layer.filter);
            if (this.keyCache[layerConfig.id])
                delete this.keyCache[layerConfig.id];
        }
        for (const id of removedIds) {
            delete this.keyCache[id];
            delete this._layerConfigs[id];
            delete this._layers[id];
        }
        this.familiesBySource = {};
        const groups = groupByLayout(ref_properties.values(this._layerConfigs), this.keyCache);
        for (const layerConfigs of groups) {
            const layers = layerConfigs.map(layerConfig => this._layers[layerConfig.id]);
            const layer = layers[0];
            if (layer.visibility === 'none') {
                continue;
            }
            const sourceId = layer.source || '';
            let sourceGroup = this.familiesBySource[sourceId];
            if (!sourceGroup) {
                sourceGroup = this.familiesBySource[sourceId] = {};
            }
            const sourceLayerId = layer.sourceLayer || '_geojsonTileLayer';
            let sourceLayerFamilies = sourceGroup[sourceLayerId];
            if (!sourceLayerFamilies) {
                sourceLayerFamilies = sourceGroup[sourceLayerId] = [];
            }
            sourceLayerFamilies.push(layers);
        }
    }
}

const {ImageBitmap} = ref_properties.window;
class RasterDEMTileWorkerSource {
    loadTile(params, callback) {
        const {uid, encoding, rawImageData, padding, buildQuadTree} = params;
        const imagePixels = ImageBitmap && rawImageData instanceof ImageBitmap ? this.getImageData(rawImageData, padding) : rawImageData;
        const dem = new ref_properties.DEMData(uid, imagePixels, encoding, padding < 1, buildQuadTree);
        callback(null, dem);
    }
    getImageData(imgBitmap, padding) {
        if (!this.offscreenCanvas || !this.offscreenCanvasContext) {
            this.offscreenCanvas = new OffscreenCanvas(imgBitmap.width, imgBitmap.height);
            this.offscreenCanvasContext = this.offscreenCanvas.getContext('2d');
        }
        this.offscreenCanvas.width = imgBitmap.width;
        this.offscreenCanvas.height = imgBitmap.height;
        this.offscreenCanvasContext.drawImage(imgBitmap, 0, 0, imgBitmap.width, imgBitmap.height);
        const imgData = this.offscreenCanvasContext.getImageData(-padding, -padding, imgBitmap.width + 2 * padding, imgBitmap.height + 2 * padding);
        this.offscreenCanvasContext.clearRect(0, 0, this.offscreenCanvas.width, this.offscreenCanvas.height);
        return new ref_properties.RGBAImage({
            width: imgData.width,
            height: imgData.height
        }, imgData.data);
    }
}

var geojsonRewind = rewind$1;
function rewind$1(gj, outer) {
    var type = gj && gj.type, i;
    if (type === 'FeatureCollection') {
        for (i = 0; i < gj.features.length; i++)
            rewind$1(gj.features[i], outer);
    } else if (type === 'GeometryCollection') {
        for (i = 0; i < gj.geometries.length; i++)
            rewind$1(gj.geometries[i], outer);
    } else if (type === 'Feature') {
        rewind$1(gj.geometry, outer);
    } else if (type === 'Polygon') {
        rewindRings(gj.coordinates, outer);
    } else if (type === 'MultiPolygon') {
        for (i = 0; i < gj.coordinates.length; i++)
            rewindRings(gj.coordinates[i], outer);
    }
    return gj;
}
function rewindRings(rings, outer) {
    if (rings.length === 0)
        return;
    rewindRing(rings[0], outer);
    for (var i = 1; i < rings.length; i++) {
        rewindRing(rings[i], !outer);
    }
}
function rewindRing(ring, dir) {
    var area = 0, err = 0;
    for (var i = 0, len = ring.length, j = len - 1; i < len; j = i++) {
        var k = (ring[i][0] - ring[j][0]) * (ring[j][1] + ring[i][1]);
        var m = area + k;
        err += Math.abs(area) >= Math.abs(k) ? area - m + k : k - m + area;
        area = m;
    }
    if (area + err >= 0 !== !!dir)
        ring.reverse();
}

const toGeoJSON = ref_properties.vectorTile.VectorTileFeature.prototype.toGeoJSON;
class FeatureWrapper$1 {
    constructor(feature) {
        this._feature = feature;
        this.extent = ref_properties.EXTENT;
        this.type = feature.type;
        this.properties = feature.tags;
        if ('id' in feature && !isNaN(feature.id)) {
            this.id = parseInt(feature.id, 10);
        }
    }
    loadGeometry() {
        if (this._feature.type === 1) {
            const geometry = [];
            for (const point of this._feature.geometry) {
                geometry.push([new ref_properties.pointGeometry(point[0], point[1])]);
            }
            return geometry;
        } else {
            const geometry = [];
            for (const ring of this._feature.geometry) {
                const newRing = [];
                for (const point of ring) {
                    newRing.push(new ref_properties.pointGeometry(point[0], point[1]));
                }
                geometry.push(newRing);
            }
            return geometry;
        }
    }
    toGeoJSON(x, y, z) {
        return toGeoJSON.call(this, x, y, z);
    }
}
class GeoJSONWrapper$1 {
    constructor(features) {
        this.layers = { '_geojsonTileLayer': this };
        this.name = '_geojsonTileLayer';
        this.extent = ref_properties.EXTENT;
        this.length = features.length;
        this._features = features;
    }
    feature(i) {
        return new FeatureWrapper$1(this._features[i]);
    }
}

var VectorTileFeature = ref_properties.vectorTile$1.VectorTileFeature;
var geojson_wrapper = GeoJSONWrapper;
function GeoJSONWrapper(features, options) {
    this.options = options || {};
    this.features = features;
    this.length = features.length;
}
GeoJSONWrapper.prototype.feature = function (i) {
    return new FeatureWrapper(this.features[i], this.options.extent);
};
function FeatureWrapper(feature, extent) {
    this.id = typeof feature.id === 'number' ? feature.id : undefined;
    this.type = feature.type;
    this.rawGeometry = feature.type === 1 ? [feature.geometry] : feature.geometry;
    this.properties = feature.tags;
    this.extent = extent || 4096;
}
FeatureWrapper.prototype.loadGeometry = function () {
    var rings = this.rawGeometry;
    this.geometry = [];
    for (var i = 0; i < rings.length; i++) {
        var ring = rings[i];
        var newRing = [];
        for (var j = 0; j < ring.length; j++) {
            newRing.push(new ref_properties.pointGeometry(ring[j][0], ring[j][1]));
        }
        this.geometry.push(newRing);
    }
    return this.geometry;
};
FeatureWrapper.prototype.bbox = function () {
    if (!this.geometry)
        this.loadGeometry();
    var rings = this.geometry;
    var x1 = Infinity;
    var x2 = -Infinity;
    var y1 = Infinity;
    var y2 = -Infinity;
    for (var i = 0; i < rings.length; i++) {
        var ring = rings[i];
        for (var j = 0; j < ring.length; j++) {
            var coord = ring[j];
            x1 = Math.min(x1, coord.x);
            x2 = Math.max(x2, coord.x);
            y1 = Math.min(y1, coord.y);
            y2 = Math.max(y2, coord.y);
        }
    }
    return [
        x1,
        y1,
        x2,
        y2
    ];
};
FeatureWrapper.prototype.toGeoJSON = VectorTileFeature.prototype.toGeoJSON;

var vtPbf = fromVectorTileJs;
var fromVectorTileJs_1 = fromVectorTileJs;
var fromGeojsonVt_1 = fromGeojsonVt;
var GeoJSONWrapper_1 = geojson_wrapper;
function fromVectorTileJs(tile) {
    var out = new ref_properties.pbf();
    writeTile(tile, out);
    return out.finish();
}
function fromGeojsonVt(layers, options) {
    options = options || {};
    var l = {};
    for (var k in layers) {
        l[k] = new geojson_wrapper(layers[k].features, options);
        l[k].name = k;
        l[k].version = options.version;
        l[k].extent = options.extent;
    }
    return fromVectorTileJs({ layers: l });
}
function writeTile(tile, pbf) {
    for (var key in tile.layers) {
        pbf.writeMessage(3, writeLayer, tile.layers[key]);
    }
}
function writeLayer(layer, pbf) {
    pbf.writeVarintField(15, layer.version || 1);
    pbf.writeStringField(1, layer.name || '');
    pbf.writeVarintField(5, layer.extent || 4096);
    var i;
    var context = {
        keys: [],
        values: [],
        keycache: {},
        valuecache: {}
    };
    for (i = 0; i < layer.length; i++) {
        context.feature = layer.feature(i);
        pbf.writeMessage(2, writeFeature, context);
    }
    var keys = context.keys;
    for (i = 0; i < keys.length; i++) {
        pbf.writeStringField(3, keys[i]);
    }
    var values = context.values;
    for (i = 0; i < values.length; i++) {
        pbf.writeMessage(4, writeValue, values[i]);
    }
}
function writeFeature(context, pbf) {
    var feature = context.feature;
    if (feature.id !== undefined) {
        pbf.writeVarintField(1, feature.id);
    }
    pbf.writeMessage(2, writeProperties, context);
    pbf.writeVarintField(3, feature.type);
    pbf.writeMessage(4, writeGeometry, feature);
}
function writeProperties(context, pbf) {
    var feature = context.feature;
    var keys = context.keys;
    var values = context.values;
    var keycache = context.keycache;
    var valuecache = context.valuecache;
    for (var key in feature.properties) {
        var value = feature.properties[key];
        var keyIndex = keycache[key];
        if (value === null)
            continue;
        if (typeof keyIndex === 'undefined') {
            keys.push(key);
            keyIndex = keys.length - 1;
            keycache[key] = keyIndex;
        }
        pbf.writeVarint(keyIndex);
        var type = typeof value;
        if (type !== 'string' && type !== 'boolean' && type !== 'number') {
            value = JSON.stringify(value);
        }
        var valueKey = type + ':' + value;
        var valueIndex = valuecache[valueKey];
        if (typeof valueIndex === 'undefined') {
            values.push(value);
            valueIndex = values.length - 1;
            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) {
    var geometry = feature.loadGeometry();
    var type = feature.type;
    var x = 0;
    var y = 0;
    var rings = geometry.length;
    for (var r = 0; r < rings; r++) {
        var ring = geometry[r];
        var count = 1;
        if (type === 1) {
            count = ring.length;
        }
        pbf.writeVarint(command(1, count));
        var lineCount = type === 3 ? ring.length - 1 : ring.length;
        for (var i = 0; i < lineCount; i++) {
            if (i === 1 && type !== 1) {
                pbf.writeVarint(command(2, lineCount - 1));
            }
            var dx = ring[i].x - x;
            var dy = ring[i].y - y;
            pbf.writeVarint(zigzag(dx));
            pbf.writeVarint(zigzag(dy));
            x += dx;
            y += dy;
        }
        if (type === 3) {
            pbf.writeVarint(command(7, 1));
        }
    }
}
function writeValue(value, pbf) {
    var 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);
        }
    }
}
vtPbf.fromVectorTileJs = fromVectorTileJs_1;
vtPbf.fromGeojsonVt = fromGeojsonVt_1;
vtPbf.GeoJSONWrapper = GeoJSONWrapper_1;

function sortKD(ids, coords, nodeSize, left, right, depth) {
    if (right - left <= nodeSize)
        return;
    const m = left + right >> 1;
    select(ids, coords, m, left, right, depth % 2);
    sortKD(ids, coords, nodeSize, left, m - 1, depth + 1);
    sortKD(ids, coords, nodeSize, m + 1, right, depth + 1);
}
function select(ids, coords, k, left, right, inc) {
    while (right > left) {
        if (right - left > 600) {
            const n = right - left + 1;
            const m = k - left + 1;
            const z = Math.log(n);
            const s = 0.5 * Math.exp(2 * z / 3);
            const sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
            const newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
            const newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
            select(ids, coords, k, newLeft, newRight, inc);
        }
        const t = coords[2 * k + inc];
        let i = left;
        let j = right;
        swapItem(ids, coords, left, k);
        if (coords[2 * right + inc] > t)
            swapItem(ids, coords, left, right);
        while (i < j) {
            swapItem(ids, coords, i, j);
            i++;
            j--;
            while (coords[2 * i + inc] < t)
                i++;
            while (coords[2 * j + inc] > t)
                j--;
        }
        if (coords[2 * left + inc] === 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;
    }
}
function swapItem(ids, coords, i, j) {
    swap(ids, i, j);
    swap(coords, 2 * i, 2 * j);
    swap(coords, 2 * i + 1, 2 * j + 1);
}
function swap(arr, i, j) {
    const tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}

function range(ids, coords, minX, minY, maxX, maxY, nodeSize) {
    const stack = [
        0,
        ids.length - 1,
        0
    ];
    const result = [];
    let x, y;
    while (stack.length) {
        const axis = stack.pop();
        const right = stack.pop();
        const left = stack.pop();
        if (right - left <= nodeSize) {
            for (let i = left; i <= right; i++) {
                x = coords[2 * i];
                y = coords[2 * i + 1];
                if (x >= minX && x <= maxX && y >= minY && y <= maxY)
                    result.push(ids[i]);
            }
            continue;
        }
        const m = Math.floor((left + right) / 2);
        x = coords[2 * m];
        y = coords[2 * m + 1];
        if (x >= minX && x <= maxX && y >= minY && y <= maxY)
            result.push(ids[m]);
        const nextAxis = (axis + 1) % 2;
        if (axis === 0 ? minX <= x : minY <= y) {
            stack.push(left);
            stack.push(m - 1);
            stack.push(nextAxis);
        }
        if (axis === 0 ? maxX >= x : maxY >= y) {
            stack.push(m + 1);
            stack.push(right);
            stack.push(nextAxis);
        }
    }
    return result;
}

function within(ids, coords, qx, qy, r, nodeSize) {
    const stack = [
        0,
        ids.length - 1,
        0
    ];
    const result = [];
    const r2 = r * r;
    while (stack.length) {
        const axis = stack.pop();
        const right = stack.pop();
        const left = stack.pop();
        if (right - left <= nodeSize) {
            for (let i = left; i <= right; i++) {
                if (sqDist(coords[2 * i], coords[2 * i + 1], qx, qy) <= r2)
                    result.push(ids[i]);
            }
            continue;
        }
        const m = Math.floor((left + right) / 2);
        const x = coords[2 * m];
        const y = coords[2 * m + 1];
        if (sqDist(x, y, qx, qy) <= r2)
            result.push(ids[m]);
        const nextAxis = (axis + 1) % 2;
        if (axis === 0 ? qx - r <= x : qy - r <= y) {
            stack.push(left);
            stack.push(m - 1);
            stack.push(nextAxis);
        }
        if (axis === 0 ? qx + r >= x : qy + r >= y) {
            stack.push(m + 1);
            stack.push(right);
            stack.push(nextAxis);
        }
    }
    return result;
}
function sqDist(ax, ay, bx, by) {
    const dx = ax - bx;
    const dy = ay - by;
    return dx * dx + dy * dy;
}

const defaultGetX = p => p[0];
const defaultGetY = p => p[1];
class KDBush {
    constructor(points, getX = defaultGetX, getY = defaultGetY, nodeSize = 64, ArrayType = Float64Array) {
        this.nodeSize = nodeSize;
        this.points = points;
        const IndexArrayType = points.length < 65536 ? Uint16Array : Uint32Array;
        const ids = this.ids = new IndexArrayType(points.length);
        const coords = this.coords = new ArrayType(points.length * 2);
        for (let i = 0; i < points.length; i++) {
            ids[i] = i;
            coords[2 * i] = getX(points[i]);
            coords[2 * i + 1] = getY(points[i]);
        }
        sortKD(ids, coords, nodeSize, 0, ids.length - 1, 0);
    }
    range(minX, minY, maxX, maxY) {
        return range(this.ids, this.coords, minX, minY, maxX, maxY, this.nodeSize);
    }
    within(x, y, r) {
        return within(this.ids, this.coords, x, y, r, this.nodeSize);
    }
}

const defaultOptions = {
    minZoom: 0,
    maxZoom: 16,
    minPoints: 2,
    radius: 40,
    extent: 512,
    nodeSize: 64,
    log: false,
    generateId: false,
    reduce: null,
    map: props => props
};
const fround = Math.fround || (tmp => x => {
    tmp[0] = +x;
    return tmp[0];
})(new Float32Array(1));
class Supercluster {
    constructor(options) {
        this.options = extend$1(Object.create(defaultOptions), options);
        this.trees = new Array(this.options.maxZoom + 1);
    }
    load(points) {
        const {log, minZoom, maxZoom, nodeSize} = this.options;
        if (log)
            console.time('total time');
        const timerId = `prepare ${ points.length } points`;
        if (log)
            console.time(timerId);
        this.points = points;
        let clusters = [];
        for (let i = 0; i < points.length; i++) {
            if (!points[i].geometry)
                continue;
            clusters.push(createPointCluster(points[i], i));
        }
        this.trees[maxZoom + 1] = new KDBush(clusters, getX, getY, nodeSize, Float32Array);
        if (log)
            console.timeEnd(timerId);
        for (let z = maxZoom; z >= minZoom; z--) {
            const now = +Date.now();
            clusters = this._cluster(clusters, z);
            this.trees[z] = new KDBush(clusters, getX, getY, nodeSize, Float32Array);
            if (log)
                console.log('z%d: %d clusters in %dms', z, clusters.length, +Date.now() - now);
        }
        if (log)
            console.timeEnd('total time');
        return this;
    }
    getClusters(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.getClusters([
                minLng,
                minLat,
                180,
                maxLat
            ], zoom);
            const westernHem = this.getClusters([
                -180,
                minLat,
                maxLng,
                maxLat
            ], zoom);
            return easternHem.concat(westernHem);
        }
        const tree = this.trees[this._limitZoom(zoom)];
        const ids = tree.range(lngX(minLng), latY(maxLat), lngX(maxLng), latY(minLat));
        const clusters = [];
        for (const id of ids) {
            const c = tree.points[id];
            clusters.push(c.numPoints ? getClusterJSON(c) : this.points[c.index]);
        }
        return clusters;
    }
    getChildren(clusterId) {
        const originId = this._getOriginId(clusterId);
        const originZoom = this._getOriginZoom(clusterId);
        const errorMsg = 'No cluster with the specified id.';
        const index = this.trees[originZoom];
        if (!index)
            throw new Error(errorMsg);
        const origin = index.points[originId];
        if (!origin)
            throw new Error(errorMsg);
        const r = this.options.radius / (this.options.extent * Math.pow(2, originZoom - 1));
        const ids = index.within(origin.x, origin.y, r);
        const children = [];
        for (const id of ids) {
            const c = index.points[id];
            if (c.parentId === clusterId) {
                children.push(c.numPoints ? getClusterJSON(c) : this.points[c.index]);
            }
        }
        if (children.length === 0)
            throw new Error(errorMsg);
        return children;
    }
    getLeaves(clusterId, limit, offset) {
        limit = limit || 10;
        offset = offset || 0;
        const leaves = [];
        this._appendLeaves(leaves, clusterId, limit, offset, 0);
        return leaves;
    }
    getTile(z, x, y) {
        const tree = this.trees[this._limitZoom(z)];
        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 = { features: [] };
        this._addTileFeatures(tree.range((x - p) / z2, top, (x + 1 + p) / z2, bottom), tree.points, x, y, z2, tile);
        if (x === 0) {
            this._addTileFeatures(tree.range(1 - p / z2, top, 1, bottom), tree.points, z2, y, z2, tile);
        }
        if (x === z2 - 1) {
            this._addTileFeatures(tree.range(0, top, p / z2, bottom), tree.points, -1, y, z2, tile);
        }
        return tile.features.length ? tile : null;
    }
    getClusterExpansionZoom(clusterId) {
        let expansionZoom = this._getOriginZoom(clusterId) - 1;
        while (expansionZoom <= this.options.maxZoom) {
            const children = this.getChildren(clusterId);
            expansionZoom++;
            if (children.length !== 1)
                break;
            clusterId = children[0].properties.cluster_id;
        }
        return expansionZoom;
    }
    _appendLeaves(result, clusterId, limit, offset, skipped) {
        const children = this.getChildren(clusterId);
        for (const child of children) {
            const props = child.properties;
            if (props && 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;
    }
    _addTileFeatures(ids, points, x, y, z2, tile) {
        for (const i of ids) {
            const c = points[i];
            const isCluster = c.numPoints;
            let tags, px, py;
            if (isCluster) {
                tags = getClusterProperties(c);
                px = c.x;
                py = c.y;
            } else {
                const p = this.points[c.index];
                tags = p.properties;
                px = lngX(p.geometry.coordinates[0]);
                py = latY(p.geometry.coordinates[1]);
            }
            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) {
                id = c.id;
            } else if (this.options.generateId) {
                id = c.index;
            } else if (this.points[c.index].id) {
                id = this.points[c.index].id;
            }
            if (id !== undefined)
                f.id = id;
            tile.features.push(f);
        }
    }
    _limitZoom(z) {
        return Math.max(this.options.minZoom, Math.min(+z, this.options.maxZoom + 1));
    }
    _cluster(points, zoom) {
        const clusters = [];
        const {radius, extent, reduce, minPoints} = this.options;
        const r = radius / (extent * Math.pow(2, zoom));
        for (let i = 0; i < points.length; i++) {
            const p = points[i];
            if (p.zoom <= zoom)
                continue;
            p.zoom = zoom;
            const tree = this.trees[zoom + 1];
            const neighborIds = tree.within(p.x, p.y, r);
            const numPointsOrigin = p.numPoints || 1;
            let numPoints = numPointsOrigin;
            for (const neighborId of neighborIds) {
                const b = tree.points[neighborId];
                if (b.zoom > zoom)
                    numPoints += b.numPoints || 1;
            }
            if (numPoints >= minPoints) {
                let wx = p.x * numPointsOrigin;
                let wy = p.y * numPointsOrigin;
                let clusterProperties = reduce && numPointsOrigin > 1 ? this._map(p, true) : null;
                const id = (i << 5) + (zoom + 1) + this.points.length;
                for (const neighborId of neighborIds) {
                    const b = tree.points[neighborId];
                    if (b.zoom <= zoom)
                        continue;
                    b.zoom = zoom;
                    const numPoints2 = b.numPoints || 1;
                    wx += b.x * numPoints2;
                    wy += b.y * numPoints2;
                    b.parentId = id;
                    if (reduce) {
                        if (!clusterProperties)
                            clusterProperties = this._map(p, true);
                        reduce(clusterProperties, this._map(b));
                    }
                }
                p.parentId = id;
                clusters.push(createCluster(wx / numPoints, wy / numPoints, id, numPoints, clusterProperties));
            } else {
                clusters.push(p);
                if (numPoints > 1) {
                    for (const neighborId of neighborIds) {
                        const b = tree.points[neighborId];
                        if (b.zoom <= zoom)
                            continue;
                        b.zoom = zoom;
                        clusters.push(b);
                    }
                }
            }
        }
        return clusters;
    }
    _getOriginId(clusterId) {
        return clusterId - this.points.length >> 5;
    }
    _getOriginZoom(clusterId) {
        return (clusterId - this.points.length) % 32;
    }
    _map(point, clone) {
        if (point.numPoints) {
            return clone ? extend$1({}, point.properties) : point.properties;
        }
        const original = this.points[point.index].properties;
        const result = this.options.map(original);
        return clone && result === original ? extend$1({}, result) : result;
    }
}
function createCluster(x, y, id, numPoints, properties) {
    return {
        x: fround(x),
        y: fround(y),
        zoom: Infinity,
        id,
        parentId: -1,
        numPoints,
        properties
    };
}
function createPointCluster(p, id) {
    const [x, y] = p.geometry.coordinates;
    return {
        x: fround(lngX(x)),
        y: fround(latY(y)),
        zoom: Infinity,
        index: id,
        parentId: -1
    };
}
function getClusterJSON(cluster) {
    return {
        type: 'Feature',
        id: cluster.id,
        properties: getClusterProperties(cluster),
        geometry: {
            type: 'Point',
            coordinates: [
                xLng(cluster.x),
                yLat(cluster.y)
            ]
        }
    };
}
function getClusterProperties(cluster) {
    const count = cluster.numPoints;
    const abbrev = count >= 10000 ? `${ Math.round(count / 1000) }k` : count >= 1000 ? `${ Math.round(count / 100) / 10 }k` : count;
    return extend$1(extend$1({}, cluster.properties), {
        cluster: true,
        cluster_id: cluster.id,
        point_count: count,
        point_count_abbreviated: abbrev
    });
}
function lngX(lng) {
    return lng / 360 + 0.5;
}
function latY(lat) {
    const y = 0.25 - lat / 360;
    return y < 0 ? 0 : y > 0.5 ? 0.5 : y;
}
function xLng(x) {
    return (x - 0.5) * 360;
}
function yLat(y) {
    return 90 - y * 360;
}
function extend$1(dest, src) {
    for (const id in src)
        dest[id] = src[id];
    return dest;
}
function getX(p) {
    return p.x;
}
function getY(p) {
    return p.y;
}

function simplify(coords, first, last, sqTolerance) {
    var maxSqDist = sqTolerance;
    var mid = last - first >> 1;
    var minPosToMid = last - first;
    var index;
    var ax = coords[first];
    var ay = coords[first + 1];
    var bx = coords[last];
    var by = coords[last + 1];
    for (var i = first + 3; i < last; i += 3) {
        var d = getSqSegDist(coords[i], coords[i + 1], ax, ay, bx, by);
        if (d > maxSqDist) {
            index = i;
            maxSqDist = d;
        } else if (d === maxSqDist) {
            var 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);
    }
}
function getSqSegDist(px, py, x, y, bx, by) {
    var dx = bx - x;
    var dy = by - y;
    if (dx !== 0 || dy !== 0) {
        var 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;
}

function createFeature(id, type, geom, tags) {
    var feature = {
        id: typeof id === 'undefined' ? null : id,
        type: type,
        geometry: geom,
        tags: tags,
        minX: Infinity,
        minY: Infinity,
        maxX: -Infinity,
        maxY: -Infinity
    };
    calcBBox(feature);
    return feature;
}
function calcBBox(feature) {
    var geom = feature.geometry;
    var type = feature.type;
    if (type === 'Point' || type === 'MultiPoint' || type === 'LineString') {
        calcLineBBox(feature, geom);
    } else if (type === 'Polygon' || type === 'MultiLineString') {
        for (var i = 0; i < geom.length; i++) {
            calcLineBBox(feature, geom[i]);
        }
    } else if (type === 'MultiPolygon') {
        for (i = 0; i < geom.length; i++) {
            for (var j = 0; j < geom[i].length; j++) {
                calcLineBBox(feature, geom[i][j]);
            }
        }
    }
}
function calcLineBBox(feature, geom) {
    for (var 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]);
    }
}

function convert(data, options) {
    var features = [];
    if (data.type === 'FeatureCollection') {
        for (var i = 0; i < data.features.length; i++) {
            convertFeature(features, data.features[i], options, i);
        }
    } else if (data.type === 'Feature') {
        convertFeature(features, data, options);
    } else {
        convertFeature(features, { geometry: data }, options);
    }
    return features;
}
function convertFeature(features, geojson, options, index) {
    if (!geojson.geometry)
        return;
    var coords = geojson.geometry.coordinates;
    var type = geojson.geometry.type;
    var tolerance = Math.pow(options.tolerance / ((1 << options.maxZoom) * options.extent), 2);
    var geometry = [];
    var id = geojson.id;
    if (options.promoteId) {
        id = geojson.properties[options.promoteId];
    } else if (options.generateId) {
        id = index || 0;
    }
    if (type === 'Point') {
        convertPoint(coords, geometry);
    } else if (type === 'MultiPoint') {
        for (var i = 0; i < coords.length; i++) {
            convertPoint(coords[i], geometry);
        }
    } else if (type === 'LineString') {
        convertLine(coords, geometry, tolerance, false);
    } else if (type === 'MultiLineString') {
        if (options.lineMetrics) {
            for (i = 0; i < coords.length; i++) {
                geometry = [];
                convertLine(coords[i], geometry, tolerance, false);
                features.push(createFeature(id, 'LineString', geometry, geojson.properties));
            }
            return;
        } else {
            convertLines(coords, geometry, tolerance, false);
        }
    } else if (type === 'Polygon') {
        convertLines(coords, geometry, tolerance, true);
    } else if (type === 'MultiPolygon') {
        for (i = 0; i < coords.length; i++) {
            var polygon = [];
            convertLines(coords[i], polygon, tolerance, true);
            geometry.push(polygon);
        }
    } else if (type === 'GeometryCollection') {
        for (i = 0; i < geojson.geometry.geometries.length; i++) {
            convertFeature(features, {
                id: id,
                geometry: geojson.geometry.geometries[i],
                properties: geojson.properties
            }, options, index);
        }
        return;
    } else {
        throw new Error('Input data is not a valid GeoJSON object.');
    }
    features.push(createFeature(id, type, geometry, geojson.properties));
}
function convertPoint(coords, out) {
    out.push(projectX(coords[0]));
    out.push(projectY(coords[1]));
    out.push(0);
}
function convertLine(ring, out, tolerance, isPolygon) {
    var x0, y0;
    var size = 0;
    for (var j = 0; j < ring.length; j++) {
        var x = projectX(ring[j][0]);
        var y = projectY(ring[j][1]);
        out.push(x);
        out.push(y);
        out.push(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;
    }
    var last = out.length - 3;
    out[2] = 1;
    simplify(out, 0, last, tolerance);
    out[last + 2] = 1;
    out.size = Math.abs(size);
    out.start = 0;
    out.end = out.size;
}
function convertLines(rings, out, tolerance, isPolygon) {
    for (var i = 0; i < rings.length; i++) {
        var geom = [];
        convertLine(rings[i], geom, tolerance, isPolygon);
        out.push(geom);
    }
}
function projectX(x) {
    return x / 360 + 0.5;
}
function projectY(y) {
    var y2 = 0.25 - y / 360;
    return y2 < 0 ? 0 : y2 > 0.5 ? 0.5 : y2;
}

function clip(features, scale, k1, k2, axis, minAll, maxAll, options) {
    k1 /= scale;
    k2 /= scale;
    if (minAll >= k1 && maxAll < k2)
        return features;
    else if (maxAll < k1 || minAll >= k2)
        return null;
    var clipped = [];
    for (var i = 0; i < features.length; i++) {
        var feature = features[i];
        var geometry = feature.geometry;
        var type = feature.type;
        var min = axis === 0 ? feature.minX : feature.minY;
        var max = axis === 0 ? feature.maxX : feature.maxY;
        if (min >= k1 && max < k2) {
            clipped.push(feature);
            continue;
        } else if (max < k1 || min >= k2) {
            continue;
        }
        var newGeometry = [];
        if (type === 'Point' || type === 'MultiPoint') {
            clipPoints(geometry, newGeometry, k1, k2, axis);
        } else if (type === 'LineString') {
            clipLine(geometry, newGeometry, k1, k2, axis, false, options.lineMetrics);
        } else if (type === 'MultiLineString') {
            clipLines(geometry, newGeometry, k1, k2, axis, false);
        } else if (type === 'Polygon') {
            clipLines(geometry, newGeometry, k1, k2, axis, true);
        } else if (type === 'MultiPolygon') {
            for (var j = 0; j < geometry.length; j++) {
                var polygon = [];
                clipLines(geometry[j], polygon, k1, k2, axis, true);
                if (polygon.length) {
                    newGeometry.push(polygon);
                }
            }
        }
        if (newGeometry.length) {
            if (options.lineMetrics && type === 'LineString') {
                for (j = 0; j < newGeometry.length; j++) {
                    clipped.push(createFeature(feature.id, type, newGeometry[j], feature.tags));
                }
                continue;
            }
            if (type === 'LineString' || type === 'MultiLineString') {
                if (newGeometry.length === 1) {
                    type = 'LineString';
                    newGeometry = newGeometry[0];
                } else {
                    type = 'MultiLineString';
                }
            }
            if (type === 'Point' || type === 'MultiPoint') {
                type = newGeometry.length === 3 ? 'Point' : 'MultiPoint';
            }
            clipped.push(createFeature(feature.id, type, newGeometry, feature.tags));
        }
    }
    return clipped.length ? clipped : null;
}
function clipPoints(geom, newGeom, k1, k2, axis) {
    for (var i = 0; i < geom.length; i += 3) {
        var a = geom[i + axis];
        if (a >= k1 && a <= k2) {
            newGeom.push(geom[i]);
            newGeom.push(geom[i + 1]);
            newGeom.push(geom[i + 2]);
        }
    }
}
function clipLine(geom, newGeom, k1, k2, axis, isPolygon, trackMetrics) {
    var slice = newSlice(geom);
    var intersect = axis === 0 ? intersectX : intersectY;
    var len = geom.start;
    var segLen, t;
    for (var i = 0; i < geom.length - 3; i += 3) {
        var ax = geom[i];
        var ay = geom[i + 1];
        var az = geom[i + 2];
        var bx = geom[i + 3];
        var by = geom[i + 4];
        var a = axis === 0 ? ax : ay;
        var b = axis === 0 ? bx : by;
        var exited = false;
        if (trackMetrics)
            segLen = Math.sqrt(Math.pow(ax - bx, 2) + Math.pow(ay - by, 2));
        if (a < k1) {
            if (b > k1) {
                t = intersect(slice, ax, ay, bx, by, k1);
                if (trackMetrics)
                    slice.start = len + segLen * t;
            }
        } else if (a > k2) {
            if (b < k2) {
                t = intersect(slice, ax, ay, bx, by, k2);
                if (trackMetrics)
                    slice.start = len + segLen * t;
            }
        } else {
            addPoint(slice, ax, ay, az);
        }
        if (b < k1 && a >= k1) {
            t = intersect(slice, ax, ay, bx, by, k1);
            exited = true;
        }
        if (b > k2 && a <= k2) {
            t = intersect(slice, ax, ay, bx, by, k2);
            exited = true;
        }
        if (!isPolygon && exited) {
            if (trackMetrics)
                slice.end = len + segLen * t;
            newGeom.push(slice);
            slice = newSlice(geom);
        }
        if (trackMetrics)
            len += segLen;
    }
    var last = geom.length - 3;
    ax = geom[last];
    ay = geom[last + 1];
    az = geom[last + 2];
    a = axis === 0 ? ax : ay;
    if (a >= k1 && a <= k2)
        addPoint(slice, ax, ay, az);
    last = slice.length - 3;
    if (isPolygon && last >= 3 && (slice[last] !== slice[0] || slice[last + 1] !== slice[1])) {
        addPoint(slice, slice[0], slice[1], slice[2]);
    }
    if (slice.length) {
        newGeom.push(slice);
    }
}
function newSlice(line) {
    var slice = [];
    slice.size = line.size;
    slice.start = line.start;
    slice.end = line.end;
    return slice;
}
function clipLines(geom, newGeom, k1, k2, axis, isPolygon) {
    for (var i = 0; i < geom.length; i++) {
        clipLine(geom[i], newGeom, k1, k2, axis, isPolygon, false);
    }
}
function addPoint(out, x, y, z) {
    out.push(x);
    out.push(y);
    out.push(z);
}
function intersectX(out, ax, ay, bx, by, x) {
    var t = (x - ax) / (bx - ax);
    out.push(x);
    out.push(ay + (by - ay) * t);
    out.push(1);
    return t;
}
function intersectY(out, ax, ay, bx, by, y) {
    var t = (y - ay) / (by - ay);
    out.push(ax + (bx - ax) * t);
    out.push(y);
    out.push(1);
    return t;
}

function wrap(features, options) {
    var buffer = options.buffer / options.extent;
    var merged = features;
    var left = clip(features, 1, -1 - buffer, buffer, 0, -1, 2, options);
    var right = clip(features, 1, 1 - buffer, 2 + buffer, 0, -1, 2, options);
    if (left || right) {
        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) {
    var newFeatures = [];
    for (var i = 0; i < features.length; i++) {
        var feature = features[i], type = feature.type;
        var newGeometry;
        if (type === 'Point' || type === 'MultiPoint' || type === 'LineString') {
            newGeometry = shiftCoords(feature.geometry, offset);
        } else if (type === 'MultiLineString' || type === 'Polygon') {
            newGeometry = [];
            for (var j = 0; j < feature.geometry.length; j++) {
                newGeometry.push(shiftCoords(feature.geometry[j], offset));
            }
        } else if (type === 'MultiPolygon') {
            newGeometry = [];
            for (j = 0; j < feature.geometry.length; j++) {
                var newPolygon = [];
                for (var k = 0; k < feature.geometry[j].length; k++) {
                    newPolygon.push(shiftCoords(feature.geometry[j][k], offset));
                }
                newGeometry.push(newPolygon);
            }
        }
        newFeatures.push(createFeature(feature.id, type, newGeometry, feature.tags));
    }
    return newFeatures;
}
function shiftCoords(points, offset) {
    var newPoints = [];
    newPoints.size = points.size;
    if (points.start !== undefined) {
        newPoints.start = points.start;
        newPoints.end = points.end;
    }
    for (var i = 0; i < points.length; i += 3) {
        newPoints.push(points[i] + offset, points[i + 1], points[i + 2]);
    }
    return newPoints;
}

function transformTile(tile, extent) {
    if (tile.transformed)
        return tile;
    var z2 = 1 << tile.z, tx = tile.x, ty = tile.y, i, j, k;
    for (i = 0; i < tile.features.length; i++) {
        var feature = tile.features[i], geom = feature.geometry, type = feature.type;
        feature.geometry = [];
        if (type === 1) {
            for (j = 0; j < geom.length; j += 2) {
                feature.geometry.push(transformPoint(geom[j], geom[j + 1], extent, z2, tx, ty));
            }
        } else {
            for (j = 0; j < geom.length; j++) {
                var ring = [];
                for (k = 0; k < geom[j].length; k += 2) {
                    ring.push(transformPoint(geom[j][k], geom[j][k + 1], extent, z2, tx, ty));
                }
                feature.geometry.push(ring);
            }
        }
    }
    tile.transformed = true;
    return tile;
}
function transformPoint(x, y, extent, z2, tx, ty) {
    return [
        Math.round(extent * (x * z2 - tx)),
        Math.round(extent * (y * z2 - ty))
    ];
}

function createTile(features, z, tx, ty, options) {
    var tolerance = z === options.maxZoom ? 0 : options.tolerance / ((1 << z) * options.extent);
    var tile = {
        features: [],
        numPoints: 0,
        numSimplified: 0,
        numFeatures: 0,
        source: null,
        x: tx,
        y: ty,
        z: z,
        transformed: false,
        minX: 2,
        minY: 1,
        maxX: -1,
        maxY: 0
    };
    for (var i = 0; i < features.length; i++) {
        tile.numFeatures++;
        addFeature(tile, features[i], tolerance, options);
        var minX = features[i].minX;
        var minY = features[i].minY;
        var maxX = features[i].maxX;
        var maxY = features[i].maxY;
        if (minX < tile.minX)
            tile.minX = minX;
        if (minY < tile.minY)
            tile.minY = minY;
        if (maxX > tile.maxX)
            tile.maxX = maxX;
        if (maxY > tile.maxY)
            tile.maxY = maxY;
    }
    return tile;
}
function addFeature(tile, feature, tolerance, options) {
    var geom = feature.geometry, type = feature.type, simplified = [];
    if (type === 'Point' || type === 'MultiPoint') {
        for (var i = 0; i < geom.length; i += 3) {
            simplified.push(geom[i]);
            simplified.push(geom[i + 1]);
            tile.numPoints++;
            tile.numSimplified++;
        }
    } else if (type === 'LineString') {
        addLine(simplified, geom, tile, tolerance, false, false);
    } else if (type === 'MultiLineString' || type === 'Polygon') {
        for (i = 0; i < geom.length; i++) {
            addLine(simplified, geom[i], tile, tolerance, type === 'Polygon', i === 0);
        }
    } else if (type === 'MultiPolygon') {
        for (var k = 0; k < geom.length; k++) {
            var polygon = geom[k];
            for (i = 0; i < polygon.length; i++) {
                addLine(simplified, polygon[i], tile, tolerance, true, i === 0);
            }
        }
    }
    if (simplified.length) {
        var tags = feature.tags || null;
        if (type === 'LineString' && options.lineMetrics) {
            tags = {};
            for (var key in feature.tags)
                tags[key] = feature.tags[key];
            tags['mapbox_clip_start'] = geom.start / geom.size;
            tags['mapbox_clip_end'] = geom.end / geom.size;
        }
        var tileFeature = {
            geometry: simplified,
            type: type === 'Polygon' || type === 'MultiPolygon' ? 3 : type === 'LineString' || type === 'MultiLineString' ? 2 : 1,
            tags: tags
        };
        if (feature.id !== null) {
            tileFeature.id = feature.id;
        }
        tile.features.push(tileFeature);
    }
}
function addLine(result, geom, tile, tolerance, isPolygon, isOuter) {
    var sqTolerance = tolerance * tolerance;
    if (tolerance > 0 && geom.size < (isPolygon ? sqTolerance : tolerance)) {
        tile.numPoints += geom.length / 3;
        return;
    }
    var ring = [];
    for (var i = 0; i < geom.length; i += 3) {
        if (tolerance === 0 || geom[i + 2] > sqTolerance) {
            tile.numSimplified++;
            ring.push(geom[i]);
            ring.push(geom[i + 1]);
        }
        tile.numPoints++;
    }
    if (isPolygon)
        rewind(ring, isOuter);
    result.push(ring);
}
function rewind(ring, clockwise) {
    var area = 0;
    for (var 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) {
        for (i = 0, len = ring.length; i < len / 2; i += 2) {
            var x = ring[i];
            var 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;
        }
    }
}

function geojsonvt(data, options) {
    return new GeoJSONVT(data, options);
}
function GeoJSONVT(data, options) {
    options = this.options = extend(Object.create(this.options), options);
    var 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.');
    var features = convert(data, options);
    this.tiles = {};
    this.tileCoords = [];
    if (debug) {
        console.timeEnd('preprocess data');
        console.log('index: maxZoom: %d, maxPoints: %d', options.indexMaxZoom, options.indexMaxPoints);
        console.time('generate tiles');
        this.stats = {};
        this.total = 0;
    }
    features = wrap(features, options);
    if (features.length)
        this.splitTile(features, 0, 0, 0);
    if (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));
    }
}
GeoJSONVT.prototype.options = {
    maxZoom: 14,
    indexMaxZoom: 5,
    indexMaxPoints: 100000,
    tolerance: 3,
    extent: 4096,
    buffer: 64,
    lineMetrics: false,
    promoteId: null,
    generateId: false,
    debug: 0
};
GeoJSONVT.prototype.splitTile = function (features, z, x, y, cz, cx, cy) {
    var stack = [
            features,
            z,
            x,
            y
        ], options = this.options, debug = options.debug;
    while (stack.length) {
        y = stack.pop();
        x = stack.pop();
        z = stack.pop();
        features = stack.pop();
        var z2 = 1 << z, id = toID(z, x, y), 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: z,
                x: x,
                y: y
            });
            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');
                }
                var key = 'z' + z;
                this.stats[key] = (this.stats[key] || 0) + 1;
                this.total++;
            }
        }
        tile.source = features;
        if (!cz) {
            if (z === options.indexMaxZoom || tile.numPoints <= options.indexMaxPoints)
                continue;
        } else {
            if (z === options.maxZoom || z === cz)
                continue;
            var m = 1 << cz - z;
            if (x !== Math.floor(cx / m) || y !== Math.floor(cy / m))
                continue;
        }
        tile.source = null;
        if (features.length === 0)
            continue;
        if (debug > 1)
            console.time('clipping');
        var k1 = 0.5 * options.buffer / options.extent, k2 = 0.5 - k1, k3 = 0.5 + k1, k4 = 1 + k1, tl, bl, tr, br, left, right;
        tl = bl = tr = br = null;
        left = clip(features, z2, x - k1, x + k3, 0, tile.minX, tile.maxX, options);
        right = clip(features, z2, x + k2, x + k4, 0, tile.minX, tile.maxX, options);
        features = null;
        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);
            left = null;
        }
        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);
            right = null;
        }
        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);
    }
};
GeoJSONVT.prototype.getTile = function (z, x, y) {
    var options = this.options, extent = options.extent, debug = options.debug;
    if (z < 0 || z > 24)
        return null;
    var z2 = 1 << z;
    x = (x % z2 + z2) % z2;
    var 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);
    var z0 = z, x0 = x, y0 = y, parent;
    while (!parent && z0 > 0) {
        z0--;
        x0 = Math.floor(x0 / 2);
        y0 = Math.floor(y0 / 2);
        parent = this.tiles[toID(z0, x0, y0)];
    }
    if (!parent || !parent.source)
        return null;
    if (debug > 1)
        console.log('found parent tile z%d-%d-%d', z0, x0, y0);
    if (debug > 1)
        console.time('drilling down');
    this.splitTile(parent.source, z0, x0, y0, z, x, y);
    if (debug > 1)
        console.timeEnd('drilling down');
    return this.tiles[id] ? transformTile(this.tiles[id], extent) : null;
};
function toID(z, x, y) {
    return ((1 << z) * y + x) * 32 + z;
}
function extend(dest, src) {
    for (var i in src)
        dest[i] = src[i];
    return dest;
}

function loadGeoJSONTile(params, callback) {
    const canonical = params.tileID.canonical;
    if (!this._geoJSONIndex) {
        return callback(null, null);
    }
    const geoJSONTile = this._geoJSONIndex.getTile(canonical.z, canonical.x, canonical.y);
    if (!geoJSONTile) {
        return callback(null, null);
    }
    const geojsonWrapper = new GeoJSONWrapper$1(geoJSONTile.features);
    let pbf = vtPbf(geojsonWrapper);
    if (pbf.byteOffset !== 0 || pbf.byteLength !== pbf.buffer.byteLength) {
        pbf = new Uint8Array(pbf);
    }
    callback(null, {
        vectorTile: geojsonWrapper,
        rawData: pbf.buffer
    });
}
class GeoJSONWorkerSource extends ref_properties.VectorTileWorkerSource {
    constructor(actor, layerIndex, availableImages, isSpriteLoaded, loadGeoJSON) {
        super(actor, layerIndex, availableImages, isSpriteLoaded, loadGeoJSONTile);
        if (loadGeoJSON) {
            this.loadGeoJSON = loadGeoJSON;
        }
    }
    loadData(params, callback) {
        const requestParam = params && params.request;
        const perf = requestParam && requestParam.collectResourceTiming;
        this.loadGeoJSON(params, (err, data) => {
            if (err || !data) {
                return callback(err);
            } else if (typeof data !== 'object') {
                return callback(new Error(`Input data given to '${ params.source }' is not a valid GeoJSON object.`));
            } else {
                geojsonRewind(data, true);
                try {
                    if (params.filter) {
                        const compiled = ref_properties.createExpression(params.filter, {
                            type: 'boolean',
                            'property-type': 'data-driven',
                            overridable: false,
                            transition: false
                        });
                        if (compiled.result === 'error')
                            throw new Error(compiled.value.map(err => `${ err.key }: ${ err.message }`).join(', '));
                        const features = data.features.filter(feature => compiled.value.evaluate({ zoom: 0 }, feature));
                        data = {
                            type: 'FeatureCollection',
                            features
                        };
                    }
                    this._geoJSONIndex = params.cluster ? new Supercluster(getSuperclusterOptions(params)).load(data.features) : geojsonvt(data, params.geojsonVtOptions);
                } catch (err) {
                    return callback(err);
                }
                this.loaded = {};
                const result = {};
                if (perf) {
                    const resourceTimingData = ref_properties.getPerformanceMeasurement(requestParam);
                    if (resourceTimingData) {
                        result.resourceTiming = {};
                        result.resourceTiming[params.source] = JSON.parse(JSON.stringify(resourceTimingData));
                    }
                }
                callback(null, result);
            }
        });
    }
    reloadTile(params, callback) {
        const loaded = this.loaded, uid = params.uid;
        if (loaded && loaded[uid]) {
            return super.reloadTile(params, callback);
        } else {
            return this.loadTile(params, callback);
        }
    }
    loadGeoJSON(params, callback) {
        if (params.request) {
            ref_properties.getJSON(params.request, callback);
        } else if (typeof params.data === 'string') {
            try {
                return callback(null, JSON.parse(params.data));
            } catch (e) {
                return callback(new Error(`Input data given to '${ params.source }' is not a valid GeoJSON object.`));
            }
        } else {
            return callback(new Error(`Input data given to '${ params.source }' is not a valid GeoJSON object.`));
        }
    }
    getClusterExpansionZoom(params, callback) {
        try {
            callback(null, this._geoJSONIndex.getClusterExpansionZoom(params.clusterId));
        } catch (e) {
            callback(e);
        }
    }
    getClusterChildren(params, callback) {
        try {
            callback(null, this._geoJSONIndex.getChildren(params.clusterId));
        } catch (e) {
            callback(e);
        }
    }
    getClusterLeaves(params, callback) {
        try {
            callback(null, this._geoJSONIndex.getLeaves(params.clusterId, params.limit, params.offset));
        } catch (e) {
            callback(e);
        }
    }
}
function getSuperclusterOptions({superclusterOptions, clusterProperties}) {
    if (!clusterProperties || !superclusterOptions)
        return superclusterOptions;
    const mapExpressions = {};
    const reduceExpressions = {};
    const globals = {
        accumulated: null,
        zoom: 0
    };
    const feature = { properties: null };
    const propertyNames = Object.keys(clusterProperties);
    for (const key of propertyNames) {
        const [operator, mapExpression] = clusterProperties[key];
        const mapExpressionParsed = ref_properties.createExpression(mapExpression);
        const reduceExpressionParsed = ref_properties.createExpression(typeof operator === 'string' ? [
            operator,
            ['accumulated'],
            [
                'get',
                key
            ]
        ] : operator);
        mapExpressions[key] = mapExpressionParsed.value;
        reduceExpressions[key] = reduceExpressionParsed.value;
    }
    superclusterOptions.map = pointProperties => {
        feature.properties = pointProperties;
        const properties = {};
        for (const key of propertyNames) {
            properties[key] = mapExpressions[key].evaluate(globals, feature);
        }
        return properties;
    };
    superclusterOptions.reduce = (accumulated, clusterProperties) => {
        feature.properties = clusterProperties;
        for (const key of propertyNames) {
            globals.accumulated = accumulated[key];
            accumulated[key] = reduceExpressions[key].evaluate(globals, feature);
        }
    };
    return superclusterOptions;
}

class Worker {
    constructor(self) {
        this.self = self;
        this.actor = new ref_properties.Actor(self, this);
        this.layerIndexes = {};
        this.availableImages = {};
        this.isSpriteLoaded = false;
        this.workerSourceTypes = {
            vector: ref_properties.VectorTileWorkerSource,
            geojson: GeoJSONWorkerSource
        };
        this.workerSources = {};
        this.demWorkerSources = {};
        this.self.registerWorkerSource = (name, WorkerSource) => {
            if (this.workerSourceTypes[name]) {
                throw new Error(`Worker source with name "${ name }" already registered.`);
            }
            this.workerSourceTypes[name] = WorkerSource;
        };
        this.self.registerRTLTextPlugin = rtlTextPlugin => {
            if (ref_properties.plugin.isParsed()) {
                throw new Error('RTL text plugin already registered.');
            }
            ref_properties.plugin['applyArabicShaping'] = rtlTextPlugin.applyArabicShaping;
            ref_properties.plugin['processBidirectionalText'] = rtlTextPlugin.processBidirectionalText;
            ref_properties.plugin['processStyledBidirectionalText'] = rtlTextPlugin.processStyledBidirectionalText;
        };
    }
    checkIfReady(mapID, unused, callback) {
        callback();
    }
    setReferrer(mapID, referrer) {
        this.referrer = referrer;
    }
    spriteLoaded(mapId, bool) {
        this.isSpriteLoaded = bool;
        for (const workerSource in this.workerSources[mapId]) {
            const ws = this.workerSources[mapId][workerSource];
            for (const source in ws) {
                if (ws[source] instanceof ref_properties.VectorTileWorkerSource) {
                    ws[source].isSpriteLoaded = bool;
                    ws[source].fire(new ref_properties.Event('isSpriteLoaded'));
                }
            }
        }
    }
    setImages(mapId, images, callback) {
        this.availableImages[mapId] = images;
        for (const workerSource in this.workerSources[mapId]) {
            const ws = this.workerSources[mapId][workerSource];
            for (const source in ws) {
                ws[source].availableImages = images;
            }
        }
        callback();
    }
    enableTerrain(mapId, enable, callback) {
        this.terrain = enable;
        callback();
    }
    setLayers(mapId, layers, callback) {
        this.getLayerIndex(mapId).replace(layers);
        callback();
    }
    updateLayers(mapId, params, callback) {
        this.getLayerIndex(mapId).update(params.layers, params.removedIds);
        callback();
    }
    loadTile(mapId, params, callback) {
        const p = this.enableTerrain ? ref_properties.extend({ enableTerrain: this.terrain }, params) : params;
        this.getWorkerSource(mapId, params.type, params.source).loadTile(p, callback);
    }
    loadDEMTile(mapId, params, callback) {
        const p = this.enableTerrain ? ref_properties.extend({ buildQuadTree: this.terrain }, params) : params;
        this.getDEMWorkerSource(mapId, params.source).loadTile(p, callback);
    }
    reloadTile(mapId, params, callback) {
        const p = this.enableTerrain ? ref_properties.extend({ enableTerrain: this.terrain }, params) : params;
        this.getWorkerSource(mapId, params.type, params.source).reloadTile(p, callback);
    }
    abortTile(mapId, params, callback) {
        this.getWorkerSource(mapId, params.type, params.source).abortTile(params, callback);
    }
    removeTile(mapId, params, callback) {
        this.getWorkerSource(mapId, params.type, params.source).removeTile(params, callback);
    }
    removeSource(mapId, params, callback) {
        if (!this.workerSources[mapId] || !this.workerSources[mapId][params.type] || !this.workerSources[mapId][params.type][params.source]) {
            return;
        }
        const worker = this.workerSources[mapId][params.type][params.source];
        delete this.workerSources[mapId][params.type][params.source];
        if (worker.removeSource !== undefined) {
            worker.removeSource(params, callback);
        } else {
            callback();
        }
    }
    loadWorkerSource(map, params, callback) {
        try {
            this.self.importScripts(params.url);
            callback();
        } catch (e) {
            callback(e.toString());
        }
    }
    syncRTLPluginState(map, state, callback) {
        try {
            ref_properties.plugin.setState(state);
            const pluginURL = ref_properties.plugin.getPluginURL();
            if (ref_properties.plugin.isLoaded() && !ref_properties.plugin.isParsed() && pluginURL != null) {
                this.self.importScripts(pluginURL);
                const complete = ref_properties.plugin.isParsed();
                const error = complete ? undefined : new Error(`RTL Text Plugin failed to import scripts from ${ pluginURL }`);
                callback(error, complete);
            }
        } catch (e) {
            callback(e.toString());
        }
    }
    getAvailableImages(mapId) {
        let availableImages = this.availableImages[mapId];
        if (!availableImages) {
            availableImages = [];
        }
        return availableImages;
    }
    getLayerIndex(mapId) {
        let layerIndexes = this.layerIndexes[mapId];
        if (!layerIndexes) {
            layerIndexes = this.layerIndexes[mapId] = new StyleLayerIndex();
        }
        return layerIndexes;
    }
    getWorkerSource(mapId, type, source) {
        if (!this.workerSources[mapId])
            this.workerSources[mapId] = {};
        if (!this.workerSources[mapId][type])
            this.workerSources[mapId][type] = {};
        if (!this.workerSources[mapId][type][source]) {
            const actor = {
                send: (type, data, callback, _, mustQueue, metadata) => {
                    this.actor.send(type, data, callback, mapId, mustQueue, metadata);
                },
                scheduler: this.actor.scheduler
            };
            this.workerSources[mapId][type][source] = new this.workerSourceTypes[type](actor, this.getLayerIndex(mapId), this.getAvailableImages(mapId), this.isSpriteLoaded);
        }
        return this.workerSources[mapId][type][source];
    }
    getDEMWorkerSource(mapId, source) {
        if (!this.demWorkerSources[mapId])
            this.demWorkerSources[mapId] = {};
        if (!this.demWorkerSources[mapId][source]) {
            this.demWorkerSources[mapId][source] = new RasterDEMTileWorkerSource();
        }
        return this.demWorkerSources[mapId][source];
    }
    enforceCacheSizeLimit(mapId, limit) {
        ref_properties.enforceCacheSizeLimit(limit);
    }
    getWorkerPerformanceMetrics(mapId, params, callback) {
        callback(undefined, void 0);
    }
}
if (typeof WorkerGlobalScope !== 'undefined' && typeof self !== 'undefined' && self instanceof WorkerGlobalScope) {
    self.worker = new Worker(self);
}

return Worker;

}));

define(['./shared'], (function (ref_properties) { 'use strict';

var supported = isSupported;
function isSupported(options) {
    return !notSupportedReason(options);
}
function notSupportedReason(options) {
    if (!isBrowser())
        return 'not a browser';
    if (!isArraySupported())
        return 'insufficent Array support';
    if (!isFunctionSupported())
        return 'insufficient Function support';
    if (!isObjectSupported())
        return 'insufficient Object support';
    if (!isJSONSupported())
        return 'insufficient JSON support';
    if (!isWorkerSupported())
        return 'insufficient worker support';
    if (!isUint8ClampedArraySupported())
        return 'insufficient Uint8ClampedArray support';
    if (!isArrayBufferSupported())
        return 'insufficient ArrayBuffer support';
    if (!isCanvasGetImageDataSupported())
        return 'insufficient Canvas/getImageData support';
    if (!isWebGLSupportedCached(options && options.failIfMajorPerformanceCaveat))
        return 'insufficient WebGL support';
    if (!isNotIE())
        return 'insufficient ECMAScript 6 support';
}
function isBrowser() {
    return typeof window !== 'undefined' && typeof document !== 'undefined';
}
function isArraySupported() {
    return Array.prototype && Array.prototype.every && Array.prototype.filter && Array.prototype.forEach && Array.prototype.indexOf && Array.prototype.lastIndexOf && Array.prototype.map && Array.prototype.some && Array.prototype.reduce && Array.prototype.reduceRight && Array.isArray;
}
function isFunctionSupported() {
    return Function.prototype && Function.prototype.bind;
}
function isObjectSupported() {
    return Object.keys && Object.create && Object.getPrototypeOf && Object.getOwnPropertyNames && Object.isSealed && Object.isFrozen && Object.isExtensible && Object.getOwnPropertyDescriptor && Object.defineProperty && Object.defineProperties && Object.seal && Object.freeze && Object.preventExtensions;
}
function isJSONSupported() {
    return 'JSON' in window && 'parse' in JSON && 'stringify' in JSON;
}
function isWorkerSupported() {
    if (!('Worker' in window && 'Blob' in window && 'URL' in window)) {
        return false;
    }
    var blob = new Blob([''], { type: 'text/javascript' });
    var workerURL = URL.createObjectURL(blob);
    var supported;
    var worker;
    try {
        worker = new Worker(workerURL);
        supported = true;
    } catch (e) {
        supported = false;
    }
    if (worker) {
        worker.terminate();
    }
    URL.revokeObjectURL(workerURL);
    return supported;
}
function isUint8ClampedArraySupported() {
    return 'Uint8ClampedArray' in window;
}
function isArrayBufferSupported() {
    return ArrayBuffer.isView;
}
function isCanvasGetImageDataSupported() {
    var canvas = document.createElement('canvas');
    canvas.width = canvas.height = 1;
    var context = canvas.getContext('2d');
    if (!context) {
        return false;
    }
    var imageData = context.getImageData(0, 0, 1, 1);
    return imageData && imageData.width === canvas.width;
}
var isWebGLSupportedCache = {};
function isWebGLSupportedCached(failIfMajorPerformanceCaveat) {
    if (isWebGLSupportedCache[failIfMajorPerformanceCaveat] === undefined) {
        isWebGLSupportedCache[failIfMajorPerformanceCaveat] = isWebGLSupported(failIfMajorPerformanceCaveat);
    }
    return isWebGLSupportedCache[failIfMajorPerformanceCaveat];
}
isSupported.webGLContextAttributes = {
    antialias: false,
    alpha: true,
    stencil: true,
    depth: true
};
function getWebGLContext(failIfMajorPerformanceCaveat) {
    var canvas = document.createElement('canvas');
    var attributes = Object.create(isSupported.webGLContextAttributes);
    attributes.failIfMajorPerformanceCaveat = failIfMajorPerformanceCaveat;
    return canvas.getContext('webgl', attributes) || canvas.getContext('experimental-webgl', attributes);
}
function isWebGLSupported(failIfMajorPerformanceCaveat) {
    var gl = getWebGLContext(failIfMajorPerformanceCaveat);
    if (!gl) {
        return false;
    }
    var shader;
    try {
        shader = gl.createShader(gl.VERTEX_SHADER);
    } catch (e) {
        return false;
    }
    if (!shader || gl.isContextLost()) {
        return false;
    }
    gl.shaderSource(shader, 'void main() {}');
    gl.compileShader(shader);
    return gl.getShaderParameter(shader, gl.COMPILE_STATUS) === true;
}
function isNotIE() {
    return !document.documentMode;
}

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;
        const keys = Object.keys(a);
        if (keys.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;
}

const DOM = {};
DOM.create = function (tagName, className, container) {
    const el = ref_properties.window.document.createElement(tagName);
    if (className !== undefined)
        el.className = className;
    if (container)
        container.appendChild(el);
    return el;
};
DOM.createNS = function (namespaceURI, tagName) {
    const el = ref_properties.window.document.createElementNS(namespaceURI, tagName);
    return el;
};
const docStyle = ref_properties.window.document && ref_properties.window.document.documentElement.style;
const selectProp = docStyle && docStyle.userSelect !== undefined ? 'userSelect' : 'WebkitUserSelect';
let userSelect;
DOM.disableDrag = function () {
    if (docStyle && selectProp) {
        userSelect = docStyle[selectProp];
        docStyle[selectProp] = 'none';
    }
};
DOM.enableDrag = function () {
    if (docStyle && selectProp) {
        docStyle[selectProp] = userSelect;
    }
};
DOM.setTransform = function (el, value) {
    el.style.transform = value;
};
let passiveSupported = false;
try {
    const options = Object.defineProperty({}, 'passive', {
        get() {
            passiveSupported = true;
        }
    });
    ref_properties.window.addEventListener('test', options, options);
    ref_properties.window.removeEventListener('test', options, options);
} catch (err) {
    passiveSupported = false;
}
DOM.addEventListener = function (target, type, callback, options = {}) {
    if ('passive' in options && passiveSupported) {
        target.addEventListener(type, callback, options);
    } else {
        target.addEventListener(type, callback, options.capture);
    }
};
DOM.removeEventListener = function (target, type, callback, options = {}) {
    if ('passive' in options && passiveSupported) {
        target.removeEventListener(type, callback, options);
    } else {
        target.removeEventListener(type, callback, options.capture);
    }
};
const suppressClick = function (e) {
    e.preventDefault();
    e.stopPropagation();
    ref_properties.window.removeEventListener('click', suppressClick, true);
};
DOM.suppressClick = function () {
    ref_properties.window.addEventListener('click', suppressClick, true);
    ref_properties.window.setTimeout(() => {
        ref_properties.window.removeEventListener('click', suppressClick, true);
    }, 0);
};
DOM.mousePos = function (el, e) {
    const rect = el.getBoundingClientRect();
    return getScaledPoint(el, rect, e);
};
DOM.touchPos = function (el, touches) {
    const rect = el.getBoundingClientRect(), points = [];
    for (let i = 0; i < touches.length; i++) {
        points.push(getScaledPoint(el, rect, touches[i]));
    }
    return points;
};
DOM.mouseButton = function (e) {
    if (typeof ref_properties.window.InstallTrigger !== 'undefined' && e.button === 2 && e.ctrlKey && ref_properties.window.navigator.platform.toUpperCase().indexOf('MAC') >= 0) {
        return 0;
    }
    return e.button;
};
DOM.remove = function (node) {
    if (node.parentNode) {
        node.parentNode.removeChild(node);
    }
};
function getScaledPoint(el, rect, e) {
    const scaling = el.offsetWidth === rect.width ? 1 : el.offsetWidth / rect.width;
    return new ref_properties.pointGeometry((e.clientX - rect.left) * scaling, (e.clientY - rect.top) * scaling);
}

function create$1() {
    var out = new ref_properties.ARRAY_TYPE(4);
    if (ref_properties.ARRAY_TYPE != Float32Array) {
        out[1] = 0;
        out[2] = 0;
    }
    out[0] = 1;
    out[3] = 1;
    return out;
}
function rotate(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;
}

class Ray {
    constructor(pos_, dir_) {
        this.pos = pos_;
        this.dir = dir_;
    }
    intersectsPlane(pt, normal, out) {
        const D = ref_properties.dot(normal, this.dir);
        if (Math.abs(D) < 0.000001) {
            return false;
        }
        const t = ref_properties.dot(ref_properties.sub(ref_properties.create(), pt, this.pos), normal) / D;
        const intersection = ref_properties.scaleAndAdd(ref_properties.create(), this.pos, this.dir, t);
        ref_properties.copy(out, intersection);
        return true;
    }
}
class Frustum {
    constructor(points_, planes_) {
        this.points = points_;
        this.planes = planes_;
    }
    static fromInvProjectionMatrix(invProj, worldSize, zoom) {
        const clipSpaceCorners = [
            [
                -1,
                1,
                -1,
                1
            ],
            [
                1,
                1,
                -1,
                1
            ],
            [
                1,
                -1,
                -1,
                1
            ],
            [
                -1,
                -1,
                -1,
                1
            ],
            [
                -1,
                1,
                1,
                1
            ],
            [
                1,
                1,
                1,
                1
            ],
            [
                1,
                -1,
                1,
                1
            ],
            [
                -1,
                -1,
                1,
                1
            ]
        ];
        const scale = Math.pow(2, zoom);
        const frustumCoords = clipSpaceCorners.map(v => {
            const s = ref_properties.transformMat4([], v, invProj);
            const k = 1 / s[3] / worldSize * scale;
            return ref_properties.mul(s, s, [
                k,
                k,
                1 / s[3],
                k
            ]);
        });
        const frustumPlanePointIndices = [
            [
                0,
                1,
                2
            ],
            [
                6,
                5,
                4
            ],
            [
                0,
                3,
                7
            ],
            [
                2,
                1,
                5
            ],
            [
                3,
                2,
                6
            ],
            [
                0,
                4,
                5
            ]
        ];
        const frustumPlanes = frustumPlanePointIndices.map(p => {
            const a = ref_properties.sub([], frustumCoords[p[0]], frustumCoords[p[1]]);
            const b = ref_properties.sub([], frustumCoords[p[2]], frustumCoords[p[1]]);
            const n = ref_properties.normalize([], ref_properties.cross([], a, b));
            const d = -ref_properties.dot(n, frustumCoords[p[1]]);
            return n.concat(d);
        });
        return new Frustum(frustumCoords, frustumPlanes);
    }
}
class Aabb {
    constructor(min_, max_) {
        this.min = min_;
        this.max = max_;
        this.center = ref_properties.scale([], ref_properties.add([], this.min, this.max), 0.5);
    }
    quadrant(index) {
        const split = [
            index % 2 === 0,
            index < 2
        ];
        const qMin = ref_properties.clone(this.min);
        const qMax = ref_properties.clone(this.max);
        for (let axis = 0; axis < split.length; axis++) {
            qMin[axis] = split[axis] ? this.min[axis] : this.center[axis];
            qMax[axis] = split[axis] ? this.center[axis] : this.max[axis];
        }
        qMax[2] = this.max[2];
        return new Aabb(qMin, qMax);
    }
    distanceX(point) {
        const pointOnAabb = Math.max(Math.min(this.max[0], point[0]), this.min[0]);
        return pointOnAabb - point[0];
    }
    distanceY(point) {
        const pointOnAabb = Math.max(Math.min(this.max[1], point[1]), this.min[1]);
        return pointOnAabb - point[1];
    }
    distanceZ(point) {
        const pointOnAabb = Math.max(Math.min(this.max[2], point[2]), this.min[2]);
        return pointOnAabb - point[2];
    }
    intersects(frustum) {
        const aabbPoints = [
            [
                this.min[0],
                this.min[1],
                this.min[2],
                1
            ],
            [
                this.max[0],
                this.min[1],
                this.min[2],
                1
            ],
            [
                this.max[0],
                this.max[1],
                this.min[2],
                1
            ],
            [
                this.min[0],
                this.max[1],
                this.min[2],
                1
            ],
            [
                this.min[0],
                this.min[1],
                this.max[2],
                1
            ],
            [
                this.max[0],
                this.min[1],
                this.max[2],
                1
            ],
            [
                this.max[0],
                this.max[1],
                this.max[2],
                1
            ],
            [
                this.min[0],
                this.max[1],
                this.max[2],
                1
            ]
        ];
        let fullyInside = true;
        for (let p = 0; p < frustum.planes.length; p++) {
            const plane = frustum.planes[p];
            let pointsInside = 0;
            for (let i = 0; i < aabbPoints.length; i++) {
                pointsInside += ref_properties.dot$1(plane, aabbPoints[i]) >= 0;
            }
            if (pointsInside === 0)
                return 0;
            if (pointsInside !== aabbPoints.length)
                fullyInside = false;
        }
        if (fullyInside)
            return 2;
        for (let axis = 0; axis < 3; axis++) {
            let projMin = Number.MAX_VALUE;
            let projMax = -Number.MAX_VALUE;
            for (let p = 0; p < frustum.points.length; p++) {
                const projectedPoint = frustum.points[p][axis] - this.min[axis];
                projMin = Math.min(projMin, projectedPoint);
                projMax = Math.max(projMax, projectedPoint);
            }
            if (projMax < 0 || projMin > this.max[axis] - this.min[axis])
                return 0;
        }
        return 1;
    }
}

function loadSprite (baseURL, requestManager, callback) {
    let json, image, error;
    const format = ref_properties.exported.devicePixelRatio > 1 ? '@2x' : '';
    let jsonRequest = ref_properties.getJSON(requestManager.transformRequest(requestManager.normalizeSpriteURL(baseURL, format, '.json'), ref_properties.ResourceType.SpriteJSON), (err, data) => {
        jsonRequest = null;
        if (!error) {
            error = err;
            json = data;
            maybeComplete();
        }
    });
    let imageRequest = ref_properties.getImage(requestManager.transformRequest(requestManager.normalizeSpriteURL(baseURL, format, '.png'), ref_properties.ResourceType.SpriteImage), (err, img) => {
        imageRequest = null;
        if (!error) {
            error = err;
            image = img;
            maybeComplete();
        }
    });
    function maybeComplete() {
        if (error) {
            callback(error);
        } else if (json && image) {
            const imageData = ref_properties.exported.getImageData(image);
            const result = {};
            for (const id in json) {
                const {width, height, x, y, sdf, pixelRatio, stretchX, stretchY, content} = json[id];
                const data = new ref_properties.RGBAImage({
                    width,
                    height
                });
                ref_properties.RGBAImage.copy(imageData, data, {
                    x,
                    y
                }, {
                    x: 0,
                    y: 0
                }, {
                    width,
                    height
                });
                result[id] = {
                    data,
                    pixelRatio,
                    sdf,
                    stretchX,
                    stretchY,
                    content
                };
            }
            callback(null, result);
        }
    }
    return {
        cancel() {
            if (jsonRequest) {
                jsonRequest.cancel();
                jsonRequest = null;
            }
            if (imageRequest) {
                imageRequest.cancel();
                imageRequest = null;
            }
        }
    };
}

function renderStyleImage(image) {
    const {userImage} = image;
    if (userImage && userImage.render) {
        const updated = userImage.render();
        if (updated) {
            image.data.replace(new Uint8Array(userImage.data.buffer));
            return true;
        }
    }
    return false;
}

const padding = 1;
class ImageManager extends ref_properties.Evented {
    constructor() {
        super();
        this.images = {};
        this.updatedImages = {};
        this.callbackDispatchedThisFrame = {};
        this.loaded = false;
        this.requestors = [];
        this.patterns = {};
        this.atlasImage = new ref_properties.RGBAImage({
            width: 1,
            height: 1
        });
        this.dirty = true;
    }
    isLoaded() {
        return this.loaded;
    }
    setLoaded(loaded) {
        if (this.loaded === loaded) {
            return;
        }
        this.loaded = loaded;
        if (loaded) {
            for (const {ids, callback} of this.requestors) {
                this._notify(ids, callback);
            }
            this.requestors = [];
        }
    }
    getImage(id) {
        return this.images[id];
    }
    addImage(id, image) {
        if (this._validate(id, image)) {
            this.images[id] = image;
        }
    }
    _validate(id, image) {
        let valid = true;
        if (!this._validateStretch(image.stretchX, image.data && image.data.width)) {
            this.fire(new ref_properties.ErrorEvent(new Error(`Image "${ id }" has invalid "stretchX" value`)));
            valid = false;
        }
        if (!this._validateStretch(image.stretchY, image.data && image.data.height)) {
            this.fire(new ref_properties.ErrorEvent(new Error(`Image "${ id }" has invalid "stretchY" value`)));
            valid = false;
        }
        if (!this._validateContent(image.content, image)) {
            this.fire(new ref_properties.ErrorEvent(new Error(`Image "${ id }" has invalid "content" value`)));
            valid = false;
        }
        return valid;
    }
    _validateStretch(stretch, size) {
        if (!stretch)
            return true;
        let last = 0;
        for (const part of stretch) {
            if (part[0] < last || part[1] < part[0] || size < part[1])
                return false;
            last = part[1];
        }
        return true;
    }
    _validateContent(content, image) {
        if (!content)
            return true;
        if (content.length !== 4)
            return false;
        if (content[0] < 0 || image.data.width < content[0])
            return false;
        if (content[1] < 0 || image.data.height < content[1])
            return false;
        if (content[2] < 0 || image.data.width < content[2])
            return false;
        if (content[3] < 0 || image.data.height < content[3])
            return false;
        if (content[2] < content[0])
            return false;
        if (content[3] < content[1])
            return false;
        return true;
    }
    updateImage(id, image) {
        const oldImage = this.images[id];
        image.version = oldImage.version + 1;
        this.images[id] = image;
        this.updatedImages[id] = true;
    }
    removeImage(id) {
        const image = this.images[id];
        delete this.images[id];
        delete this.patterns[id];
        if (image.userImage && image.userImage.onRemove) {
            image.userImage.onRemove();
        }
    }
    listImages() {
        return Object.keys(this.images);
    }
    getImages(ids, callback) {
        let hasAllDependencies = true;
        if (!this.isLoaded()) {
            for (const id of ids) {
                if (!this.images[id]) {
                    hasAllDependencies = false;
                }
            }
        }
        if (this.isLoaded() || hasAllDependencies) {
            this._notify(ids, callback);
        } else {
            this.requestors.push({
                ids,
                callback
            });
        }
    }
    _notify(ids, callback) {
        const response = {};
        for (const id of ids) {
            if (!this.images[id]) {
                this.fire(new ref_properties.Event('styleimagemissing', { id }));
            }
            const image = this.images[id];
            if (image) {
                response[id] = {
                    data: image.data.clone(),
                    pixelRatio: image.pixelRatio,
                    sdf: image.sdf,
                    version: image.version,
                    stretchX: image.stretchX,
                    stretchY: image.stretchY,
                    content: image.content,
                    hasRenderCallback: Boolean(image.userImage && image.userImage.render)
                };
            } else {
                ref_properties.warnOnce(`Image "${ id }" could not be loaded. Please make sure you have added the image with map.addImage() or a "sprite" property in your style. You can provide missing images by listening for the "styleimagemissing" map event.`);
            }
        }
        callback(null, response);
    }
    getPixelSize() {
        const {width, height} = this.atlasImage;
        return {
            width,
            height
        };
    }
    getPattern(id) {
        const pattern = this.patterns[id];
        const image = this.getImage(id);
        if (!image) {
            return null;
        }
        if (pattern && pattern.position.version === image.version) {
            return pattern.position;
        }
        if (!pattern) {
            const w = image.data.width + padding * 2;
            const h = image.data.height + padding * 2;
            const bin = {
                w,
                h,
                x: 0,
                y: 0
            };
            const position = new ref_properties.ImagePosition(bin, image);
            this.patterns[id] = {
                bin,
                position
            };
        } else {
            pattern.position.version = image.version;
        }
        this._updatePatternAtlas();
        return this.patterns[id].position;
    }
    bind(context) {
        const gl = context.gl;
        if (!this.atlasTexture) {
            this.atlasTexture = new ref_properties.Texture(context, this.atlasImage, gl.RGBA);
        } else if (this.dirty) {
            this.atlasTexture.update(this.atlasImage);
            this.dirty = false;
        }
        this.atlasTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
    }
    _updatePatternAtlas() {
        const bins = [];
        for (const id in this.patterns) {
            bins.push(this.patterns[id].bin);
        }
        const {w, h} = ref_properties.potpack(bins);
        const dst = this.atlasImage;
        dst.resize({
            width: w || 1,
            height: h || 1
        });
        for (const id in this.patterns) {
            const {bin} = this.patterns[id];
            const x = bin.x + padding;
            const y = bin.y + padding;
            const src = this.images[id].data;
            const w = src.width;
            const h = src.height;
            ref_properties.RGBAImage.copy(src, dst, {
                x: 0,
                y: 0
            }, {
                x,
                y
            }, {
                width: w,
                height: h
            });
            ref_properties.RGBAImage.copy(src, dst, {
                x: 0,
                y: h - 1
            }, {
                x,
                y: y - 1
            }, {
                width: w,
                height: 1
            });
            ref_properties.RGBAImage.copy(src, dst, {
                x: 0,
                y: 0
            }, {
                x,
                y: y + h
            }, {
                width: w,
                height: 1
            });
            ref_properties.RGBAImage.copy(src, dst, {
                x: w - 1,
                y: 0
            }, {
                x: x - 1,
                y
            }, {
                width: 1,
                height: h
            });
            ref_properties.RGBAImage.copy(src, dst, {
                x: 0,
                y: 0
            }, {
                x: x + w,
                y
            }, {
                width: 1,
                height: h
            });
        }
        this.dirty = true;
    }
    beginFrame() {
        this.callbackDispatchedThisFrame = {};
    }
    dispatchRenderCallbacks(ids) {
        for (const id of ids) {
            if (this.callbackDispatchedThisFrame[id])
                continue;
            this.callbackDispatchedThisFrame[id] = true;
            const image = this.images[id];
            const updated = renderStyleImage(image);
            if (updated) {
                this.updateImage(id, image);
            }
        }
    }
}

function sphericalToCartesian([r, azimuthal, polar]) {
    const a = ref_properties.degToRad(azimuthal + 90), p = ref_properties.degToRad(polar);
    return {
        x: r * Math.cos(a) * Math.sin(p),
        y: r * Math.sin(a) * Math.sin(p),
        z: r * Math.cos(p),
        azimuthal,
        polar
    };
}
class LightPositionProperty {
    constructor() {
        this.specification = ref_properties.spec.light.position;
    }
    possiblyEvaluate(value, parameters) {
        return sphericalToCartesian(value.expression.evaluate(parameters));
    }
    interpolate(a, b, t) {
        return {
            x: ref_properties.number(a.x, b.x, t),
            y: ref_properties.number(a.y, b.y, t),
            z: ref_properties.number(a.z, b.z, t),
            azimuthal: ref_properties.number(a.azimuthal, b.azimuthal, t),
            polar: ref_properties.number(a.polar, b.polar, t)
        };
    }
}
const properties$1 = new ref_properties.Properties({
    'anchor': new ref_properties.DataConstantProperty(ref_properties.spec.light.anchor),
    'position': new LightPositionProperty(),
    'color': new ref_properties.DataConstantProperty(ref_properties.spec.light.color),
    'intensity': new ref_properties.DataConstantProperty(ref_properties.spec.light.intensity)
});
const TRANSITION_SUFFIX$2 = '-transition';
class Light extends ref_properties.Evented {
    constructor(lightOptions) {
        super();
        this._transitionable = new ref_properties.Transitionable(properties$1);
        this.setLight(lightOptions);
        this._transitioning = this._transitionable.untransitioned();
    }
    getLight() {
        return this._transitionable.serialize();
    }
    setLight(light, options = {}) {
        if (this._validate(ref_properties.validateLight, light, options)) {
            return;
        }
        for (const name in light) {
            const value = light[name];
            if (ref_properties.endsWith(name, TRANSITION_SUFFIX$2)) {
                this._transitionable.setTransition(name.slice(0, -TRANSITION_SUFFIX$2.length), value);
            } else {
                this._transitionable.setValue(name, value);
            }
        }
    }
    updateTransitions(parameters) {
        this._transitioning = this._transitionable.transitioned(parameters, this._transitioning);
    }
    hasTransition() {
        return this._transitioning.hasTransition();
    }
    recalculate(parameters) {
        this.properties = this._transitioning.possiblyEvaluate(parameters);
    }
    _validate(validate, value, options) {
        if (options && options.validate === false) {
            return false;
        }
        return ref_properties.emitValidationErrors(this, validate.call(ref_properties.validateStyle, ref_properties.extend({
            value,
            style: {
                glyphs: true,
                sprite: true
            },
            styleSpec: ref_properties.spec
        })));
    }
}

const properties = new ref_properties.Properties({
    'source': new ref_properties.DataConstantProperty(ref_properties.spec.terrain.source),
    'exaggeration': new ref_properties.DataConstantProperty(ref_properties.spec.terrain.exaggeration)
});
const TRANSITION_SUFFIX$1 = '-transition';
class Terrain$1 extends ref_properties.Evented {
    constructor(terrainOptions) {
        super();
        this._transitionable = new ref_properties.Transitionable(properties);
        this.set(terrainOptions);
        this._transitioning = this._transitionable.untransitioned();
    }
    get() {
        return this._transitionable.serialize();
    }
    set(terrain) {
        for (const name in terrain) {
            const value = terrain[name];
            if (ref_properties.endsWith(name, TRANSITION_SUFFIX$1)) {
                this._transitionable.setTransition(name.slice(0, -TRANSITION_SUFFIX$1.length), value);
            } else {
                this._transitionable.setValue(name, value);
            }
        }
    }
    updateTransitions(parameters) {
        this._transitioning = this._transitionable.transitioned(parameters, this._transitioning);
    }
    hasTransition() {
        return this._transitioning.hasTransition();
    }
    recalculate(parameters) {
        this.properties = this._transitioning.possiblyEvaluate(parameters);
    }
}

const FOG_PITCH_START = 45;
const FOG_PITCH_END = 65;
const FOG_SYMBOL_CLIPPING_THRESHOLD = 0.9;
function getFogOpacity(state, pos, pitch, fov) {
    const fogPitchOpacity = ref_properties.smoothstep(FOG_PITCH_START, FOG_PITCH_END, pitch);
    const [start, end] = getFovAdjustedFogRange(state, fov);
    const decay = 6;
    const depth = ref_properties.length(pos);
    const fogRange = (depth - start) / (end - start);
    let falloff = 1 - Math.min(1, Math.exp(-decay * fogRange));
    falloff *= falloff * falloff;
    falloff = Math.min(1, 1.00747 * falloff);
    return falloff * fogPitchOpacity * state.alpha;
}
function getFovAdjustedFogRange(state, fov) {
    const shift = 0.5 / Math.tan(fov * 0.5);
    return [
        state.range[0] + shift,
        state.range[1] + shift
    ];
}
function getFogOpacityAtTileCoord(state, x, y, z, tileId, transform) {
    const mat = transform.calculateFogTileMatrix(tileId);
    const pos = [
        x,
        y,
        z
    ];
    ref_properties.transformMat4$1(pos, pos, mat);
    return getFogOpacity(state, pos, transform.pitch, transform._fov);
}
function getFogOpacityAtLngLat(state, lngLat, transform) {
    const meters = ref_properties.MercatorCoordinate.fromLngLat(lngLat);
    const elevation = transform.elevation ? transform.elevation.getAtPointOrZero(meters) : 0;
    const pos = [
        meters.x,
        meters.y,
        elevation
    ];
    ref_properties.transformMat4$1(pos, pos, transform.mercatorFogMatrix);
    return getFogOpacity(state, pos, transform.pitch, transform._fov);
}

const fogProperties = new ref_properties.Properties({
    'range': new ref_properties.DataConstantProperty(ref_properties.spec.fog.range),
    'color': new ref_properties.DataConstantProperty(ref_properties.spec.fog.color),
    'horizon-blend': new ref_properties.DataConstantProperty(ref_properties.spec.fog['horizon-blend'])
});
const TRANSITION_SUFFIX = '-transition';
class Fog extends ref_properties.Evented {
    constructor(fogOptions) {
        super();
        this._transitionable = new ref_properties.Transitionable(fogProperties);
        this.set(fogOptions);
        this._transitioning = this._transitionable.untransitioned();
    }
    get state() {
        return {
            range: this.properties.get('range'),
            horizonBlend: this.properties.get('horizon-blend'),
            alpha: this.properties.get('color').a
        };
    }
    get() {
        return this._transitionable.serialize();
    }
    set(fog) {
        if (this._validate(ref_properties.validateFog, fog)) {
            return;
        }
        for (const name in fog) {
            const value = fog[name];
            if (ref_properties.endsWith(name, TRANSITION_SUFFIX)) {
                this._transitionable.setTransition(name.slice(0, -TRANSITION_SUFFIX.length), value);
            } else {
                this._transitionable.setValue(name, value);
            }
        }
    }
    getOpacity(pitch) {
        const fogColor = this.properties && this.properties.get('color') || 1;
        const pitchFactor = ref_properties.smoothstep(FOG_PITCH_START, FOG_PITCH_END, pitch);
        return pitchFactor * fogColor.a;
    }
    getOpacityAtLatLng(lngLat, transform) {
        return getFogOpacityAtLngLat(this.state, lngLat, transform);
    }
    getFovAdjustedRange(fov) {
        return getFovAdjustedFogRange(this.state, fov);
    }
    updateTransitions(parameters) {
        this._transitioning = this._transitionable.transitioned(parameters, this._transitioning);
    }
    hasTransition() {
        return this._transitioning.hasTransition();
    }
    recalculate(parameters) {
        this.properties = this._transitioning.possiblyEvaluate(parameters);
    }
    _validate(validate, value, options) {
        if (options && options.validate === false) {
            return false;
        }
        return ref_properties.emitValidationErrors(this, validate.call(ref_properties.validateStyle, ref_properties.extend({
            value,
            style: {
                glyphs: true,
                sprite: true
            },
            styleSpec: ref_properties.spec
        })));
    }
}

class Dispatcher {
    constructor(workerPool, parent) {
        this.workerPool = workerPool;
        this.actors = [];
        this.currentActor = 0;
        this.id = ref_properties.uniqueId();
        const workers = this.workerPool.acquire(this.id);
        for (let i = 0; i < workers.length; i++) {
            const worker = workers[i];
            const actor = new Dispatcher.Actor(worker, parent, this.id);
            actor.name = `Worker ${ i }`;
            this.actors.push(actor);
        }
        this.ready = false;
        this.broadcast('checkIfReady', null, () => {
            this.ready = true;
        });
    }
    broadcast(type, data, cb) {
        cb = cb || function () {
        };
        ref_properties.asyncAll(this.actors, (actor, done) => {
            actor.send(type, data, done);
        }, cb);
    }
    getActor() {
        this.currentActor = (this.currentActor + 1) % this.actors.length;
        return this.actors[this.currentActor];
    }
    remove() {
        this.actors.forEach(actor => {
            actor.remove();
        });
        this.actors = [];
        this.workerPool.release(this.id);
    }
}
Dispatcher.Actor = ref_properties.Actor;

function pixelsToTileUnits (tile, pixelValue, z) {
    return pixelValue * (ref_properties.EXTENT / (tile.tileSize * Math.pow(2, z - tile.tileID.overscaledZ)));
}

class QueryGeometry {
    constructor(screenBounds, cameraPoint, aboveHorizon, transform) {
        this.screenBounds = screenBounds;
        this.cameraPoint = cameraPoint;
        this._screenRaycastCache = {};
        this._cameraRaycastCache = {};
        this.isAboveHorizon = aboveHorizon;
        this.screenGeometry = this.bufferedScreenGeometry(0);
        this.screenGeometryMercator = this.screenGeometry.map(p => transform.pointCoordinate3D(p));
        this.cameraGeometry = this.bufferedCameraGeometry(0);
    }
    static createFromScreenPoints(geometry, transform) {
        let screenGeometry;
        let aboveHorizon;
        if (geometry instanceof ref_properties.pointGeometry || typeof geometry[0] === 'number') {
            const pt = ref_properties.pointGeometry.convert(geometry);
            screenGeometry = [ref_properties.pointGeometry.convert(geometry)];
            aboveHorizon = transform.isPointAboveHorizon(pt);
        } else {
            const tl = ref_properties.pointGeometry.convert(geometry[0]);
            const br = ref_properties.pointGeometry.convert(geometry[1]);
            screenGeometry = [
                tl,
                br
            ];
            aboveHorizon = ref_properties.polygonizeBounds(tl, br).every(p => transform.isPointAboveHorizon(p));
        }
        return new QueryGeometry(screenGeometry, transform.getCameraPoint(), aboveHorizon, transform);
    }
    isPointQuery() {
        return this.screenBounds.length === 1;
    }
    bufferedScreenGeometry(buffer) {
        return ref_properties.polygonizeBounds(this.screenBounds[0], this.screenBounds.length === 1 ? this.screenBounds[0] : this.screenBounds[1], buffer);
    }
    bufferedCameraGeometry(buffer) {
        const min = this.screenBounds[0];
        const max = this.screenBounds.length === 1 ? this.screenBounds[0].add(new ref_properties.pointGeometry(1, 1)) : this.screenBounds[1];
        const cameraPolygon = ref_properties.polygonizeBounds(min, max, 0, false);
        if (this.cameraPoint.y > max.y) {
            if (this.cameraPoint.x > min.x && this.cameraPoint.x < max.x) {
                cameraPolygon.splice(3, 0, this.cameraPoint);
            } else if (this.cameraPoint.x >= max.x) {
                cameraPolygon[2] = this.cameraPoint;
            } else if (this.cameraPoint.x <= min.x) {
                cameraPolygon[3] = this.cameraPoint;
            }
        }
        return ref_properties.bufferConvexPolygon(cameraPolygon, buffer);
    }
    containsTile(tile, transform, use3D) {
        const bias = 1;
        const padding = tile.queryPadding + bias;
        const geometryForTileCheck = use3D ? this._bufferedCameraMercator(padding, transform).map(p => tile.tileID.getTilePoint(p)) : this._bufferedScreenMercator(padding, transform).map(p => tile.tileID.getTilePoint(p));
        const tilespaceVec3s = this.screenGeometryMercator.map(p => tile.tileID.getTileVec3(p));
        const tilespaceGeometry = tilespaceVec3s.map(v => new ref_properties.pointGeometry(v[0], v[1]));
        const cameraMercator = transform.getFreeCameraOptions().position || new ref_properties.MercatorCoordinate(0, 0, 0);
        const tilespaceCameraPosition = tile.tileID.getTileVec3(cameraMercator);
        const tilespaceRays = tilespaceVec3s.map(tileVec => {
            const dir = ref_properties.sub(tileVec, tileVec, tilespaceCameraPosition);
            ref_properties.normalize(dir, dir);
            return new Ray(tilespaceCameraPosition, dir);
        });
        const pixelToTileUnitsFactor = pixelsToTileUnits(tile, 1, transform.zoom);
        if (ref_properties.polygonIntersectsBox(geometryForTileCheck, 0, 0, ref_properties.EXTENT, ref_properties.EXTENT)) {
            return {
                queryGeometry: this,
                tilespaceGeometry,
                tilespaceRays,
                bufferedTilespaceGeometry: geometryForTileCheck,
                bufferedTilespaceBounds: clampBoundsToTileExtents(ref_properties.getBounds(geometryForTileCheck)),
                tile,
                tileID: tile.tileID,
                pixelToTileUnitsFactor
            };
        }
    }
    _bufferedScreenMercator(padding, transform) {
        const key = cacheKey(padding);
        if (this._screenRaycastCache[key]) {
            return this._screenRaycastCache[key];
        } else {
            const poly = this.bufferedScreenGeometry(padding).map(p => transform.pointCoordinate3D(p));
            this._screenRaycastCache[key] = poly;
            return poly;
        }
    }
    _bufferedCameraMercator(padding, transform) {
        const key = cacheKey(padding);
        if (this._cameraRaycastCache[key]) {
            return this._cameraRaycastCache[key];
        } else {
            const poly = this.bufferedCameraGeometry(padding).map(p => transform.pointCoordinate3D(p));
            this._cameraRaycastCache[key] = poly;
            return poly;
        }
    }
}
function cacheKey(padding) {
    return padding * 100 | 0;
}
function clampBoundsToTileExtents(bounds) {
    bounds.min.x = ref_properties.clamp(bounds.min.x, 0, ref_properties.EXTENT);
    bounds.min.y = ref_properties.clamp(bounds.min.y, 0, ref_properties.EXTENT);
    bounds.max.x = ref_properties.clamp(bounds.max.x, 0, ref_properties.EXTENT);
    bounds.max.y = ref_properties.clamp(bounds.max.y, 0, ref_properties.EXTENT);
    return bounds;
}

function loadTileJSON (options, requestManager, callback) {
    const loaded = function (err, tileJSON) {
        if (err) {
            return callback(err);
        } else if (tileJSON) {
            const result = ref_properties.pick(ref_properties.extend(tileJSON, options), [
                'tiles',
                'minzoom',
                'maxzoom',
                'attribution',
                'mapbox_logo',
                'bounds',
                'scheme',
                'tileSize',
                'encoding',
                'zoomOffset'
            ]);
            if (tileJSON.vector_layers) {
                result.vectorLayers = tileJSON.vector_layers;
                result.vectorLayerIds = result.vectorLayers.map(layer => {
                    return layer.id;
                });
            }
            result.tiles = requestManager.canonicalizeTileset(result, options.url);
            callback(null, result);
        }
    };
    if (options.url) {
        return ref_properties.getJSON(requestManager.transformRequest(requestManager.normalizeSourceURL(options.url), ref_properties.ResourceType.Source), loaded);
    } else {
        return ref_properties.exported.frame(() => loaded(null, options));
    }
}

class TileBounds {
    constructor(bounds, minzoom, maxzoom) {
        this.bounds = ref_properties.LngLatBounds.convert(this.validateBounds(bounds));
        this.minzoom = minzoom || 0;
        this.maxzoom = maxzoom || 24;
    }
    validateBounds(bounds) {
        if (!Array.isArray(bounds) || bounds.length !== 4)
            return [
                -180,
                -90,
                180,
                90
            ];
        return [
            Math.max(-180, bounds[0]),
            Math.max(-90, bounds[1]),
            Math.min(180, bounds[2]),
            Math.min(90, bounds[3])
        ];
    }
    contains(tileID) {
        const worldSize = Math.pow(2, tileID.z);
        const level = {
            minX: Math.floor(ref_properties.mercatorXfromLng(this.bounds.getWest()) * worldSize),
            minY: Math.floor(ref_properties.mercatorYfromLat(this.bounds.getNorth()) * worldSize),
            maxX: Math.ceil(ref_properties.mercatorXfromLng(this.bounds.getEast()) * worldSize),
            maxY: Math.ceil(ref_properties.mercatorYfromLat(this.bounds.getSouth()) * worldSize)
        };
        const hit = tileID.x >= level.minX && tileID.x < level.maxX && tileID.y >= level.minY && tileID.y < level.maxY;
        return hit;
    }
}

class IndexBuffer {
    constructor(context, array, dynamicDraw) {
        this.context = context;
        const gl = context.gl;
        this.buffer = gl.createBuffer();
        this.dynamicDraw = Boolean(dynamicDraw);
        this.context.unbindVAO();
        context.bindElementBuffer.set(this.buffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, array.arrayBuffer, this.dynamicDraw ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW);
        if (!this.dynamicDraw) {
            delete array.arrayBuffer;
        }
    }
    bind() {
        this.context.bindElementBuffer.set(this.buffer);
    }
    updateData(array) {
        const gl = this.context.gl;
        this.context.unbindVAO();
        this.bind();
        gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, array.arrayBuffer);
    }
    destroy() {
        const gl = this.context.gl;
        if (this.buffer) {
            gl.deleteBuffer(this.buffer);
            delete this.buffer;
        }
    }
}

const AttributeType = {
    Int8: 'BYTE',
    Uint8: 'UNSIGNED_BYTE',
    Int16: 'SHORT',
    Uint16: 'UNSIGNED_SHORT',
    Int32: 'INT',
    Uint32: 'UNSIGNED_INT',
    Float32: 'FLOAT'
};
class VertexBuffer {
    constructor(context, array, attributes, dynamicDraw) {
        this.length = array.length;
        this.attributes = attributes;
        this.itemSize = array.bytesPerElement;
        this.dynamicDraw = dynamicDraw;
        this.context = context;
        const gl = context.gl;
        this.buffer = gl.createBuffer();
        context.bindVertexBuffer.set(this.buffer);
        gl.bufferData(gl.ARRAY_BUFFER, array.arrayBuffer, this.dynamicDraw ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW);
        if (!this.dynamicDraw) {
            delete array.arrayBuffer;
        }
    }
    bind() {
        this.context.bindVertexBuffer.set(this.buffer);
    }
    updateData(array) {
        const gl = this.context.gl;
        this.bind();
        gl.bufferSubData(gl.ARRAY_BUFFER, 0, array.arrayBuffer);
    }
    enableAttributes(gl, program) {
        for (let j = 0; j < this.attributes.length; j++) {
            const member = this.attributes[j];
            const attribIndex = program.attributes[member.name];
            if (attribIndex !== undefined) {
                gl.enableVertexAttribArray(attribIndex);
            }
        }
    }
    setVertexAttribPointers(gl, program, vertexOffset) {
        for (let j = 0; j < this.attributes.length; j++) {
            const member = this.attributes[j];
            const attribIndex = program.attributes[member.name];
            if (attribIndex !== undefined) {
                gl.vertexAttribPointer(attribIndex, member.components, gl[AttributeType[member.type]], false, this.itemSize, member.offset + this.itemSize * (vertexOffset || 0));
            }
        }
    }
    destroy() {
        const gl = this.context.gl;
        if (this.buffer) {
            gl.deleteBuffer(this.buffer);
            delete this.buffer;
        }
    }
}

class BaseValue {
    constructor(context) {
        this.gl = context.gl;
        this.default = this.getDefault();
        this.current = this.default;
        this.dirty = false;
    }
    get() {
        return this.current;
    }
    set(value) {
    }
    getDefault() {
        return this.default;
    }
    setDefault() {
        this.set(this.default);
    }
}
class ClearColor extends BaseValue {
    getDefault() {
        return ref_properties.Color.transparent;
    }
    set(v) {
        const c = this.current;
        if (v.r === c.r && v.g === c.g && v.b === c.b && v.a === c.a && !this.dirty)
            return;
        this.gl.clearColor(v.r, v.g, v.b, v.a);
        this.current = v;
        this.dirty = false;
    }
}
class ClearDepth extends BaseValue {
    getDefault() {
        return 1;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        this.gl.clearDepth(v);
        this.current = v;
        this.dirty = false;
    }
}
class ClearStencil extends BaseValue {
    getDefault() {
        return 0;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        this.gl.clearStencil(v);
        this.current = v;
        this.dirty = false;
    }
}
class ColorMask extends BaseValue {
    getDefault() {
        return [
            true,
            true,
            true,
            true
        ];
    }
    set(v) {
        const c = this.current;
        if (v[0] === c[0] && v[1] === c[1] && v[2] === c[2] && v[3] === c[3] && !this.dirty)
            return;
        this.gl.colorMask(v[0], v[1], v[2], v[3]);
        this.current = v;
        this.dirty = false;
    }
}
class DepthMask extends BaseValue {
    getDefault() {
        return true;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        this.gl.depthMask(v);
        this.current = v;
        this.dirty = false;
    }
}
class StencilMask extends BaseValue {
    getDefault() {
        return 255;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        this.gl.stencilMask(v);
        this.current = v;
        this.dirty = false;
    }
}
class StencilFunc extends BaseValue {
    getDefault() {
        return {
            func: this.gl.ALWAYS,
            ref: 0,
            mask: 255
        };
    }
    set(v) {
        const c = this.current;
        if (v.func === c.func && v.ref === c.ref && v.mask === c.mask && !this.dirty)
            return;
        this.gl.stencilFunc(v.func, v.ref, v.mask);
        this.current = v;
        this.dirty = false;
    }
}
class StencilOp extends BaseValue {
    getDefault() {
        const gl = this.gl;
        return [
            gl.KEEP,
            gl.KEEP,
            gl.KEEP
        ];
    }
    set(v) {
        const c = this.current;
        if (v[0] === c[0] && v[1] === c[1] && v[2] === c[2] && !this.dirty)
            return;
        this.gl.stencilOp(v[0], v[1], v[2]);
        this.current = v;
        this.dirty = false;
    }
}
class StencilTest extends BaseValue {
    getDefault() {
        return false;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        const gl = this.gl;
        if (v) {
            gl.enable(gl.STENCIL_TEST);
        } else {
            gl.disable(gl.STENCIL_TEST);
        }
        this.current = v;
        this.dirty = false;
    }
}
class DepthRange extends BaseValue {
    getDefault() {
        return [
            0,
            1
        ];
    }
    set(v) {
        const c = this.current;
        if (v[0] === c[0] && v[1] === c[1] && !this.dirty)
            return;
        this.gl.depthRange(v[0], v[1]);
        this.current = v;
        this.dirty = false;
    }
}
class DepthTest extends BaseValue {
    getDefault() {
        return false;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        const gl = this.gl;
        if (v) {
            gl.enable(gl.DEPTH_TEST);
        } else {
            gl.disable(gl.DEPTH_TEST);
        }
        this.current = v;
        this.dirty = false;
    }
}
class DepthFunc extends BaseValue {
    getDefault() {
        return this.gl.LESS;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        this.gl.depthFunc(v);
        this.current = v;
        this.dirty = false;
    }
}
class Blend extends BaseValue {
    getDefault() {
        return false;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        const gl = this.gl;
        if (v) {
            gl.enable(gl.BLEND);
        } else {
            gl.disable(gl.BLEND);
        }
        this.current = v;
        this.dirty = false;
    }
}
class BlendFunc extends BaseValue {
    getDefault() {
        const gl = this.gl;
        return [
            gl.ONE,
            gl.ZERO
        ];
    }
    set(v) {
        const c = this.current;
        if (v[0] === c[0] && v[1] === c[1] && !this.dirty)
            return;
        this.gl.blendFunc(v[0], v[1]);
        this.current = v;
        this.dirty = false;
    }
}
class BlendColor extends BaseValue {
    getDefault() {
        return ref_properties.Color.transparent;
    }
    set(v) {
        const c = this.current;
        if (v.r === c.r && v.g === c.g && v.b === c.b && v.a === c.a && !this.dirty)
            return;
        this.gl.blendColor(v.r, v.g, v.b, v.a);
        this.current = v;
        this.dirty = false;
    }
}
class BlendEquation extends BaseValue {
    getDefault() {
        return this.gl.FUNC_ADD;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        this.gl.blendEquation(v);
        this.current = v;
        this.dirty = false;
    }
}
class CullFace extends BaseValue {
    getDefault() {
        return false;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        const gl = this.gl;
        if (v) {
            gl.enable(gl.CULL_FACE);
        } else {
            gl.disable(gl.CULL_FACE);
        }
        this.current = v;
        this.dirty = false;
    }
}
class CullFaceSide extends BaseValue {
    getDefault() {
        return this.gl.BACK;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        this.gl.cullFace(v);
        this.current = v;
        this.dirty = false;
    }
}
class FrontFace extends BaseValue {
    getDefault() {
        return this.gl.CCW;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        this.gl.frontFace(v);
        this.current = v;
        this.dirty = false;
    }
}
class Program$1 extends BaseValue {
    getDefault() {
        return null;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        this.gl.useProgram(v);
        this.current = v;
        this.dirty = false;
    }
}
class ActiveTextureUnit extends BaseValue {
    getDefault() {
        return this.gl.TEXTURE0;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        this.gl.activeTexture(v);
        this.current = v;
        this.dirty = false;
    }
}
class Viewport extends BaseValue {
    getDefault() {
        const gl = this.gl;
        return [
            0,
            0,
            gl.drawingBufferWidth,
            gl.drawingBufferHeight
        ];
    }
    set(v) {
        const c = this.current;
        if (v[0] === c[0] && v[1] === c[1] && v[2] === c[2] && v[3] === c[3] && !this.dirty)
            return;
        this.gl.viewport(v[0], v[1], v[2], v[3]);
        this.current = v;
        this.dirty = false;
    }
}
class BindFramebuffer extends BaseValue {
    getDefault() {
        return null;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        const gl = this.gl;
        gl.bindFramebuffer(gl.FRAMEBUFFER, v);
        this.current = v;
        this.dirty = false;
    }
}
class BindRenderbuffer extends BaseValue {
    getDefault() {
        return null;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        const gl = this.gl;
        gl.bindRenderbuffer(gl.RENDERBUFFER, v);
        this.current = v;
        this.dirty = false;
    }
}
class BindTexture extends BaseValue {
    getDefault() {
        return null;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        const gl = this.gl;
        gl.bindTexture(gl.TEXTURE_2D, v);
        this.current = v;
        this.dirty = false;
    }
}
class BindVertexBuffer extends BaseValue {
    getDefault() {
        return null;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        const gl = this.gl;
        gl.bindBuffer(gl.ARRAY_BUFFER, v);
        this.current = v;
        this.dirty = false;
    }
}
class BindElementBuffer extends BaseValue {
    getDefault() {
        return null;
    }
    set(v) {
        const gl = this.gl;
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, v);
        this.current = v;
        this.dirty = false;
    }
}
class BindVertexArrayOES extends BaseValue {
    constructor(context) {
        super(context);
        this.vao = context.extVertexArrayObject;
    }
    getDefault() {
        return null;
    }
    set(v) {
        if (!this.vao || v === this.current && !this.dirty)
            return;
        this.vao.bindVertexArrayOES(v);
        this.current = v;
        this.dirty = false;
    }
}
class PixelStoreUnpack extends BaseValue {
    getDefault() {
        return 4;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        const gl = this.gl;
        gl.pixelStorei(gl.UNPACK_ALIGNMENT, v);
        this.current = v;
        this.dirty = false;
    }
}
class PixelStoreUnpackPremultiplyAlpha extends BaseValue {
    getDefault() {
        return false;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        const gl = this.gl;
        gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, v);
        this.current = v;
        this.dirty = false;
    }
}
class PixelStoreUnpackFlipY extends BaseValue {
    getDefault() {
        return false;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        const gl = this.gl;
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, v);
        this.current = v;
        this.dirty = false;
    }
}
class FramebufferAttachment extends BaseValue {
    constructor(context, parent) {
        super(context);
        this.context = context;
        this.parent = parent;
    }
    getDefault() {
        return null;
    }
}
class ColorAttachment extends FramebufferAttachment {
    setDirty() {
        this.dirty = true;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        this.context.bindFramebuffer.set(this.parent);
        const gl = this.gl;
        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, v, 0);
        this.current = v;
        this.dirty = false;
    }
}
class DepthAttachment extends FramebufferAttachment {
    attachment() {
        return this.gl.DEPTH_ATTACHMENT;
    }
    set(v) {
        if (v === this.current && !this.dirty)
            return;
        this.context.bindFramebuffer.set(this.parent);
        const gl = this.gl;
        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, this.attachment(), gl.RENDERBUFFER, v);
        this.current = v;
        this.dirty = false;
    }
}
class DepthStencilAttachment extends DepthAttachment {
    attachment() {
        return this.gl.DEPTH_STENCIL_ATTACHMENT;
    }
}

class Framebuffer {
    constructor(context, width, height, hasDepth) {
        this.context = context;
        this.width = width;
        this.height = height;
        const gl = context.gl;
        const fbo = this.framebuffer = gl.createFramebuffer();
        this.colorAttachment = new ColorAttachment(context, fbo);
        if (hasDepth) {
            this.depthAttachment = new DepthAttachment(context, fbo);
        }
    }
    destroy() {
        const gl = this.context.gl;
        const texture = this.colorAttachment.get();
        if (texture)
            gl.deleteTexture(texture);
        if (this.depthAttachment) {
            const renderbuffer = this.depthAttachment.get();
            if (renderbuffer)
                gl.deleteRenderbuffer(renderbuffer);
        }
        gl.deleteFramebuffer(this.framebuffer);
    }
}

class Context {
    constructor(gl) {
        this.gl = gl;
        this.extVertexArrayObject = this.gl.getExtension('OES_vertex_array_object');
        this.clearColor = new ClearColor(this);
        this.clearDepth = new ClearDepth(this);
        this.clearStencil = new ClearStencil(this);
        this.colorMask = new ColorMask(this);
        this.depthMask = new DepthMask(this);
        this.stencilMask = new StencilMask(this);
        this.stencilFunc = new StencilFunc(this);
        this.stencilOp = new StencilOp(this);
        this.stencilTest = new StencilTest(this);
        this.depthRange = new DepthRange(this);
        this.depthTest = new DepthTest(this);
        this.depthFunc = new DepthFunc(this);
        this.blend = new Blend(this);
        this.blendFunc = new BlendFunc(this);
        this.blendColor = new BlendColor(this);
        this.blendEquation = new BlendEquation(this);
        this.cullFace = new CullFace(this);
        this.cullFaceSide = new CullFaceSide(this);
        this.frontFace = new FrontFace(this);
        this.program = new Program$1(this);
        this.activeTexture = new ActiveTextureUnit(this);
        this.viewport = new Viewport(this);
        this.bindFramebuffer = new BindFramebuffer(this);
        this.bindRenderbuffer = new BindRenderbuffer(this);
        this.bindTexture = new BindTexture(this);
        this.bindVertexBuffer = new BindVertexBuffer(this);
        this.bindElementBuffer = new BindElementBuffer(this);
        this.bindVertexArrayOES = this.extVertexArrayObject && new BindVertexArrayOES(this);
        this.pixelStoreUnpack = new PixelStoreUnpack(this);
        this.pixelStoreUnpackPremultiplyAlpha = new PixelStoreUnpackPremultiplyAlpha(this);
        this.pixelStoreUnpackFlipY = new PixelStoreUnpackFlipY(this);
        this.extTextureFilterAnisotropic = gl.getExtension('EXT_texture_filter_anisotropic') || gl.getExtension('MOZ_EXT_texture_filter_anisotropic') || gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic');
        if (this.extTextureFilterAnisotropic) {
            this.extTextureFilterAnisotropicMax = gl.getParameter(this.extTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
        }
        this.extTextureFilterAnisotropicForceOff = false;
        this.extTextureHalfFloat = gl.getExtension('OES_texture_half_float');
        if (this.extTextureHalfFloat) {
            gl.getExtension('OES_texture_half_float_linear');
            this.extRenderToTextureHalfFloat = gl.getExtension('EXT_color_buffer_half_float');
        }
        this.extTimerQuery = gl.getExtension('EXT_disjoint_timer_query');
        this.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
    }
    setDefault() {
        this.unbindVAO();
        this.clearColor.setDefault();
        this.clearDepth.setDefault();
        this.clearStencil.setDefault();
        this.colorMask.setDefault();
        this.depthMask.setDefault();
        this.stencilMask.setDefault();
        this.stencilFunc.setDefault();
        this.stencilOp.setDefault();
        this.stencilTest.setDefault();
        this.depthRange.setDefault();
        this.depthTest.setDefault();
        this.depthFunc.setDefault();
        this.blend.setDefault();
        this.blendFunc.setDefault();
        this.blendColor.setDefault();
        this.blendEquation.setDefault();
        this.cullFace.setDefault();
        this.cullFaceSide.setDefault();
        this.frontFace.setDefault();
        this.program.setDefault();
        this.activeTexture.setDefault();
        this.bindFramebuffer.setDefault();
        this.pixelStoreUnpack.setDefault();
        this.pixelStoreUnpackPremultiplyAlpha.setDefault();
        this.pixelStoreUnpackFlipY.setDefault();
    }
    setDirty() {
        this.clearColor.dirty = true;
        this.clearDepth.dirty = true;
        this.clearStencil.dirty = true;
        this.colorMask.dirty = true;
        this.depthMask.dirty = true;
        this.stencilMask.dirty = true;
        this.stencilFunc.dirty = true;
        this.stencilOp.dirty = true;
        this.stencilTest.dirty = true;
        this.depthRange.dirty = true;
        this.depthTest.dirty = true;
        this.depthFunc.dirty = true;
        this.blend.dirty = true;
        this.blendFunc.dirty = true;
        this.blendColor.dirty = true;
        this.blendEquation.dirty = true;
        this.cullFace.dirty = true;
        this.cullFaceSide.dirty = true;
        this.frontFace.dirty = true;
        this.program.dirty = true;
        this.activeTexture.dirty = true;
        this.viewport.dirty = true;
        this.bindFramebuffer.dirty = true;
        this.bindRenderbuffer.dirty = true;
        this.bindTexture.dirty = true;
        this.bindVertexBuffer.dirty = true;
        this.bindElementBuffer.dirty = true;
        if (this.extVertexArrayObject) {
            this.bindVertexArrayOES.dirty = true;
        }
        this.pixelStoreUnpack.dirty = true;
        this.pixelStoreUnpackPremultiplyAlpha.dirty = true;
        this.pixelStoreUnpackFlipY.dirty = true;
    }
    createIndexBuffer(array, dynamicDraw) {
        return new IndexBuffer(this, array, dynamicDraw);
    }
    createVertexBuffer(array, attributes, dynamicDraw) {
        return new VertexBuffer(this, array, attributes, dynamicDraw);
    }
    createRenderbuffer(storageFormat, width, height) {
        const gl = this.gl;
        const rbo = gl.createRenderbuffer();
        this.bindRenderbuffer.set(rbo);
        gl.renderbufferStorage(gl.RENDERBUFFER, storageFormat, width, height);
        this.bindRenderbuffer.set(null);
        return rbo;
    }
    createFramebuffer(width, height, hasDepth) {
        return new Framebuffer(this, width, height, hasDepth);
    }
    clear({color, depth, stencil}) {
        const gl = this.gl;
        let mask = 0;
        if (color) {
            mask |= gl.COLOR_BUFFER_BIT;
            this.clearColor.set(color);
            this.colorMask.set([
                true,
                true,
                true,
                true
            ]);
        }
        if (typeof depth !== 'undefined') {
            mask |= gl.DEPTH_BUFFER_BIT;
            this.depthRange.set([
                0,
                1
            ]);
            this.clearDepth.set(depth);
            this.depthMask.set(true);
        }
        if (typeof stencil !== 'undefined') {
            mask |= gl.STENCIL_BUFFER_BIT;
            this.clearStencil.set(stencil);
            this.stencilMask.set(255);
        }
        gl.clear(mask);
    }
    setCullFace(cullFaceMode) {
        if (cullFaceMode.enable === false) {
            this.cullFace.set(false);
        } else {
            this.cullFace.set(true);
            this.cullFaceSide.set(cullFaceMode.mode);
            this.frontFace.set(cullFaceMode.frontFace);
        }
    }
    setDepthMode(depthMode) {
        if (depthMode.func === this.gl.ALWAYS && !depthMode.mask) {
            this.depthTest.set(false);
        } else {
            this.depthTest.set(true);
            this.depthFunc.set(depthMode.func);
            this.depthMask.set(depthMode.mask);
            this.depthRange.set(depthMode.range);
        }
    }
    setStencilMode(stencilMode) {
        if (stencilMode.test.func === this.gl.ALWAYS && !stencilMode.mask) {
            this.stencilTest.set(false);
        } else {
            this.stencilTest.set(true);
            this.stencilMask.set(stencilMode.mask);
            this.stencilOp.set([
                stencilMode.fail,
                stencilMode.depthFail,
                stencilMode.pass
            ]);
            this.stencilFunc.set({
                func: stencilMode.test.func,
                ref: stencilMode.ref,
                mask: stencilMode.test.mask
            });
        }
    }
    setColorMode(colorMode) {
        if (deepEqual(colorMode.blendFunction, ref_properties.ColorMode.Replace)) {
            this.blend.set(false);
        } else {
            this.blend.set(true);
            this.blendFunc.set(colorMode.blendFunction);
            this.blendColor.set(colorMode.blendColor);
        }
        this.colorMask.set(colorMode.mask);
    }
    unbindVAO() {
        if (this.extVertexArrayObject) {
            this.bindVertexArrayOES.set(null);
        }
    }
}

class VectorTileSource extends ref_properties.Evented {
    constructor(id, options, dispatcher, eventedParent) {
        super();
        this.id = id;
        this.dispatcher = dispatcher;
        this.type = 'vector';
        this.minzoom = 0;
        this.maxzoom = 22;
        this.scheme = 'xyz';
        this.tileSize = 512;
        this.zoomOffset = 0;
        this.reparseOverscaled = true;
        this.isTileClipped = true;
        this._loaded = false;
        ref_properties.extend(this, ref_properties.pick(options, [
            'url',
            'scheme',
            'tileSize',
            'promoteId',
            'zoomOffset'
        ]));
        this._options = ref_properties.extend({ type: 'vector' }, options);
        this._collectResourceTiming = options.collectResourceTiming;
        if (this.tileSize !== 512) {
            throw new Error('vector tile sources must have a tileSize of 512');
        }
        this.setEventedParent(eventedParent);
        this._tileWorkers = {};
        this._deduped = new ref_properties.DedupedRequest();
    }
    load() {
        this._loaded = false;
        this.fire(new ref_properties.Event('dataloading', { dataType: 'source' }));
        this._tileJSONRequest = loadTileJSON(this._options, this.map._requestManager, (err, tileJSON) => {
            this._tileJSONRequest = null;
            this._loaded = true;
            if (err) {
                this.fire(new ref_properties.ErrorEvent(err));
            } else if (tileJSON) {
                ref_properties.extend(this, tileJSON);
                if (tileJSON.bounds)
                    this.tileBounds = new TileBounds(tileJSON.bounds, this.minzoom, this.maxzoom);
                ref_properties.postTurnstileEvent(tileJSON.tiles, this.map._requestManager._customAccessToken);
                this.fire(new ref_properties.Event('data', {
                    dataType: 'source',
                    sourceDataType: 'metadata'
                }));
                this.fire(new ref_properties.Event('data', {
                    dataType: 'source',
                    sourceDataType: 'content'
                }));
            }
        });
    }
    loaded() {
        return this._loaded;
    }
    hasTile(tileID) {
        return !this.tileBounds || this.tileBounds.contains(tileID.canonical);
    }
    onAdd(map) {
        this.map = map;
        this.load();
    }
    setSourceProperty(callback) {
        if (this._tileJSONRequest) {
            this._tileJSONRequest.cancel();
        }
        callback();
        const sourceCaches = this.map.style._getSourceCaches(this.id);
        for (const sourceCache of sourceCaches) {
            sourceCache.clearTiles();
        }
        this.load();
    }
    setTiles(tiles) {
        this.setSourceProperty(() => {
            this._options.tiles = tiles;
        });
        return this;
    }
    setUrl(url) {
        this.setSourceProperty(() => {
            this.url = url;
            this._options.url = url;
        });
        return this;
    }
    onRemove() {
        if (this._tileJSONRequest) {
            this._tileJSONRequest.cancel();
            this._tileJSONRequest = null;
        }
    }
    serialize() {
        return ref_properties.extend({}, this._options);
    }
    loadTile(tile, callback) {
        const url = this.map._requestManager.normalizeTileURL(tile.tileID.canonical.url(this.tiles, this.scheme, this.zoomOffset));
        const request = this.map._requestManager.transformRequest(url, ref_properties.ResourceType.Tile);
        const params = {
            request,
            data: undefined,
            uid: tile.uid,
            tileID: tile.tileID,
            tileZoom: tile.tileZoom,
            zoom: tile.tileID.overscaledZ,
            tileSize: this.tileSize * tile.tileID.overscaleFactor(),
            type: this.type,
            source: this.id,
            pixelRatio: ref_properties.exported.devicePixelRatio,
            showCollisionBoxes: this.map.showCollisionBoxes,
            promoteId: this.promoteId,
            isSymbolTile: tile.isSymbolTile
        };
        params.request.collectResourceTiming = this._collectResourceTiming;
        if (!tile.actor || tile.state === 'expired') {
            tile.actor = this._tileWorkers[url] = this._tileWorkers[url] || this.dispatcher.getActor();
            if (!this.dispatcher.ready) {
                const cancel = ref_properties.loadVectorTile.call({ deduped: this._deduped }, params, (err, data) => {
                    if (err || !data) {
                        done.call(this, err);
                    } else {
                        params.data = {
                            cacheControl: data.cacheControl,
                            expires: data.expires,
                            rawData: data.rawData.slice(0)
                        };
                        if (tile.actor)
                            tile.actor.send('loadTile', params, done.bind(this), undefined, true);
                    }
                }, true);
                tile.request = { cancel };
            } else {
                tile.request = tile.actor.send('loadTile', params, done.bind(this), undefined, true);
            }
        } else if (tile.state === 'loading') {
            tile.reloadCallback = callback;
        } else {
            tile.request = tile.actor.send('reloadTile', params, done.bind(this));
        }
        function done(err, data) {
            delete tile.request;
            if (tile.aborted)
                return callback(null);
            if (err && err.status !== 404) {
                return callback(err);
            }
            if (data && data.resourceTiming)
                tile.resourceTiming = data.resourceTiming;
            if (this.map._refreshExpiredTiles && data)
                tile.setExpiryData(data);
            tile.loadVectorData(data, this.map.painter);
            ref_properties.cacheEntryPossiblyAdded(this.dispatcher);
            callback(null);
            if (tile.reloadCallback) {
                this.loadTile(tile, tile.reloadCallback);
                tile.reloadCallback = null;
            }
        }
    }
    abortTile(tile) {
        if (tile.request) {
            tile.request.cancel();
            delete tile.request;
        }
        if (tile.actor) {
            tile.actor.send('abortTile', {
                uid: tile.uid,
                type: this.type,
                source: this.id
            });
        }
    }
    unloadTile(tile) {
        tile.unloadVectorData();
        if (tile.actor) {
            tile.actor.send('removeTile', {
                uid: tile.uid,
                type: this.type,
                source: this.id
            });
        }
    }
    hasTransition() {
        return false;
    }
    afterUpdate() {
        this._tileWorkers = {};
    }
}

class RasterTileSource extends ref_properties.Evented {
    constructor(id, options, dispatcher, eventedParent) {
        super();
        this.id = id;
        this.dispatcher = dispatcher;
        this.setEventedParent(eventedParent);
        this.type = 'raster';
        this.minzoom = 0;
        this.maxzoom = 22;
        this.roundZoom = true;
        this.scheme = 'xyz';
        this.tileSize = 512;
        this.zoomOffset = 0;
        this._loaded = false;
        this._options = ref_properties.extend({ type: 'raster' }, options);
        ref_properties.extend(this, ref_properties.pick(options, [
            'url',
            'scheme',
            'tileSize',
            'zoomOffset'
        ]));
    }
    load() {
        this._loaded = false;
        this.fire(new ref_properties.Event('dataloading', { dataType: 'source' }));
        this._tileJSONRequest = loadTileJSON(this._options, this.map._requestManager, (err, tileJSON) => {
            this._tileJSONRequest = null;
            this._loaded = true;
            if (err) {
                this.fire(new ref_properties.ErrorEvent(err));
            } else if (tileJSON) {
                ref_properties.extend(this, tileJSON);
                if (tileJSON.bounds)
                    this.tileBounds = new TileBounds(tileJSON.bounds, this.minzoom, this.maxzoom);
                ref_properties.postTurnstileEvent(tileJSON.tiles);
                this.fire(new ref_properties.Event('data', {
                    dataType: 'source',
                    sourceDataType: 'metadata'
                }));
                this.fire(new ref_properties.Event('data', {
                    dataType: 'source',
                    sourceDataType: 'content'
                }));
            }
        });
    }
    loaded() {
        return this._loaded;
    }
    onAdd(map) {
        this.map = map;
        this.load();
    }
    onRemove() {
        if (this._tileJSONRequest) {
            this._tileJSONRequest.cancel();
            this._tileJSONRequest = null;
        }
    }
    serialize() {
        return ref_properties.extend({}, this._options);
    }
    hasTile(tileID) {
        return !this.tileBounds || this.tileBounds.contains(tileID.canonical);
    }
    loadTile(tile, callback) {
        const use2x = ref_properties.exported.devicePixelRatio >= 2;
        const url = this.map._requestManager.normalizeTileURL(tile.tileID.canonical.url(this.tiles, this.scheme, this.zoomOffset), use2x, this.tileSize);
        tile.request = ref_properties.getImage(this.map._requestManager.transformRequest(url, ref_properties.ResourceType.Tile), (err, img, cacheControl, expires) => {
            delete tile.request;
            if (tile.aborted) {
                tile.state = 'unloaded';
                callback(null);
            } else if (err) {
                tile.state = 'errored';
                callback(err);
            } else if (img) {
                if (this.map._refreshExpiredTiles)
                    tile.setExpiryData({
                        cacheControl,
                        expires
                    });
                const context = this.map.painter.context;
                const gl = context.gl;
                tile.texture = this.map.painter.getTileTexture(img.width);
                if (tile.texture) {
                    tile.texture.update(img, { useMipmap: true });
                } else {
                    tile.texture = new ref_properties.Texture(context, img, gl.RGBA, { useMipmap: true });
                    tile.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
                    if (context.extTextureFilterAnisotropic) {
                        gl.texParameterf(gl.TEXTURE_2D, context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, context.extTextureFilterAnisotropicMax);
                    }
                }
                tile.state = 'loaded';
                ref_properties.cacheEntryPossiblyAdded(this.dispatcher);
                callback(null);
            }
        });
    }
    abortTile(tile, callback) {
        if (tile.request) {
            tile.request.cancel();
            delete tile.request;
        }
        callback();
    }
    unloadTile(tile, callback) {
        if (tile.texture)
            this.map.painter.saveTileTexture(tile.texture);
        callback();
    }
    hasTransition() {
        return false;
    }
}

let supportsOffscreenCanvas;
function offscreenCanvasSupported() {
    if (supportsOffscreenCanvas == null) {
        supportsOffscreenCanvas = ref_properties.window.OffscreenCanvas && new ref_properties.window.OffscreenCanvas(1, 1).getContext('2d') && typeof ref_properties.window.createImageBitmap === 'function';
    }
    return supportsOffscreenCanvas;
}

class RasterDEMTileSource extends RasterTileSource {
    constructor(id, options, dispatcher, eventedParent) {
        super(id, options, dispatcher, eventedParent);
        this.type = 'raster-dem';
        this.maxzoom = 22;
        this._options = ref_properties.extend({ type: 'raster-dem' }, options);
        this.encoding = options.encoding || 'mapbox';
    }
    loadTile(tile, callback) {
        const url = this.map._requestManager.normalizeTileURL(tile.tileID.canonical.url(this.tiles, this.scheme, this.zoomOffset), false, this.tileSize);
        tile.request = ref_properties.getImage(this.map._requestManager.transformRequest(url, ref_properties.ResourceType.Tile), imageLoaded.bind(this));
        function imageLoaded(err, img, cacheControl, expires) {
            delete tile.request;
            if (tile.aborted) {
                tile.state = 'unloaded';
                callback(null);
            } else if (err) {
                tile.state = 'errored';
                callback(err);
            } else if (img) {
                if (this.map._refreshExpiredTiles)
                    tile.setExpiryData({
                        cacheControl,
                        expires
                    });
                const transfer = ref_properties.window.ImageBitmap && img instanceof ref_properties.window.ImageBitmap && offscreenCanvasSupported();
                const buffer = (img.width - ref_properties.prevPowerOfTwo(img.width)) / 2;
                const padding = 1 - buffer;
                const borderReady = padding < 1;
                if (!borderReady && !tile.neighboringTiles) {
                    tile.neighboringTiles = this._getNeighboringTiles(tile.tileID);
                }
                const rawImageData = transfer ? img : ref_properties.exported.getImageData(img, padding);
                const params = {
                    uid: tile.uid,
                    coord: tile.tileID,
                    source: this.id,
                    rawImageData,
                    encoding: this.encoding,
                    padding
                };
                if (!tile.actor || tile.state === 'expired') {
                    tile.actor = this.dispatcher.getActor();
                    tile.actor.send('loadDEMTile', params, done.bind(this), undefined, true);
                }
            }
        }
        function done(err, dem) {
            if (err) {
                tile.state = 'errored';
                callback(err);
            }
            if (dem) {
                tile.dem = dem;
                tile.dem.onDeserialize();
                tile.needsHillshadePrepare = true;
                tile.needsDEMTextureUpload = true;
                tile.state = 'loaded';
                callback(null);
            }
        }
    }
    _getNeighboringTiles(tileID) {
        const canonical = tileID.canonical;
        const dim = Math.pow(2, canonical.z);
        const px = (canonical.x - 1 + dim) % dim;
        const pxw = canonical.x === 0 ? tileID.wrap - 1 : tileID.wrap;
        const nx = (canonical.x + 1 + dim) % dim;
        const nxw = canonical.x + 1 === dim ? tileID.wrap + 1 : tileID.wrap;
        const neighboringTiles = {};
        neighboringTiles[new ref_properties.OverscaledTileID(tileID.overscaledZ, pxw, canonical.z, px, canonical.y).key] = { backfilled: false };
        neighboringTiles[new ref_properties.OverscaledTileID(tileID.overscaledZ, nxw, canonical.z, nx, canonical.y).key] = { backfilled: false };
        if (canonical.y > 0) {
            neighboringTiles[new ref_properties.OverscaledTileID(tileID.overscaledZ, pxw, canonical.z, px, canonical.y - 1).key] = { backfilled: false };
            neighboringTiles[new ref_properties.OverscaledTileID(tileID.overscaledZ, tileID.wrap, canonical.z, canonical.x, canonical.y - 1).key] = { backfilled: false };
            neighboringTiles[new ref_properties.OverscaledTileID(tileID.overscaledZ, nxw, canonical.z, nx, canonical.y - 1).key] = { backfilled: false };
        }
        if (canonical.y + 1 < dim) {
            neighboringTiles[new ref_properties.OverscaledTileID(tileID.overscaledZ, pxw, canonical.z, px, canonical.y + 1).key] = { backfilled: false };
            neighboringTiles[new ref_properties.OverscaledTileID(tileID.overscaledZ, tileID.wrap, canonical.z, canonical.x, canonical.y + 1).key] = { backfilled: false };
            neighboringTiles[new ref_properties.OverscaledTileID(tileID.overscaledZ, nxw, canonical.z, nx, canonical.y + 1).key] = { backfilled: false };
        }
        return neighboringTiles;
    }
    unloadTile(tile) {
        if (tile.demTexture)
            this.map.painter.saveTileTexture(tile.demTexture);
        if (tile.fbo) {
            tile.fbo.destroy();
            delete tile.fbo;
        }
        if (tile.dem)
            delete tile.dem;
        delete tile.neighboringTiles;
        tile.state = 'unloaded';
    }
}

class GeoJSONSource extends ref_properties.Evented {
    constructor(id, options, dispatcher, eventedParent) {
        super();
        this.id = id;
        this.type = 'geojson';
        this.minzoom = 0;
        this.maxzoom = 18;
        this.tileSize = 512;
        this.isTileClipped = true;
        this.reparseOverscaled = true;
        this._loaded = false;
        this.actor = dispatcher.getActor();
        this.setEventedParent(eventedParent);
        this._data = options.data;
        this._options = ref_properties.extend({}, options);
        this._collectResourceTiming = options.collectResourceTiming;
        if (options.maxzoom !== undefined)
            this.maxzoom = options.maxzoom;
        if (options.type)
            this.type = options.type;
        if (options.attribution)
            this.attribution = options.attribution;
        this.promoteId = options.promoteId;
        const scale = ref_properties.EXTENT / this.tileSize;
        this.workerOptions = ref_properties.extend({
            source: this.id,
            cluster: options.cluster || false,
            geojsonVtOptions: {
                buffer: (options.buffer !== undefined ? options.buffer : 128) * scale,
                tolerance: (options.tolerance !== undefined ? options.tolerance : 0.375) * scale,
                extent: ref_properties.EXTENT,
                maxZoom: this.maxzoom,
                lineMetrics: options.lineMetrics || false,
                generateId: options.generateId || false
            },
            superclusterOptions: {
                maxZoom: options.clusterMaxZoom !== undefined ? options.clusterMaxZoom : this.maxzoom - 1,
                minPoints: Math.max(2, options.clusterMinPoints || 2),
                extent: ref_properties.EXTENT,
                radius: (options.clusterRadius !== undefined ? options.clusterRadius : 50) * scale,
                log: false,
                generateId: options.generateId || false
            },
            clusterProperties: options.clusterProperties,
            filter: options.filter
        }, options.workerOptions);
    }
    onAdd(map) {
        this.map = map;
        this.setData(this._data);
    }
    setData(data) {
        this._data = data;
        this._updateWorkerData();
        return this;
    }
    getClusterExpansionZoom(clusterId, callback) {
        this.actor.send('geojson.getClusterExpansionZoom', {
            clusterId,
            source: this.id
        }, callback);
        return this;
    }
    getClusterChildren(clusterId, callback) {
        this.actor.send('geojson.getClusterChildren', {
            clusterId,
            source: this.id
        }, callback);
        return this;
    }
    getClusterLeaves(clusterId, limit, offset, callback) {
        this.actor.send('geojson.getClusterLeaves', {
            source: this.id,
            clusterId,
            limit,
            offset
        }, callback);
        return this;
    }
    _updateWorkerData() {
        if (this._pendingLoad) {
            this._coalesce = true;
            return;
        }
        this.fire(new ref_properties.Event('dataloading', { dataType: 'source' }));
        this._loaded = false;
        const options = ref_properties.extend({}, this.workerOptions);
        const data = this._data;
        if (typeof data === 'string') {
            options.request = this.map._requestManager.transformRequest(ref_properties.exported.resolveURL(data), ref_properties.ResourceType.Source);
            options.request.collectResourceTiming = this._collectResourceTiming;
        } else {
            options.data = JSON.stringify(data);
        }
        this._pendingLoad = this.actor.send(`${ this.type }.loadData`, options, (err, result) => {
            this._loaded = true;
            this._pendingLoad = null;
            if (err) {
                this.fire(new ref_properties.ErrorEvent(err));
            } else {
                const data = {
                    dataType: 'source',
                    sourceDataType: this._metadataFired ? 'content' : 'metadata'
                };
                if (this._collectResourceTiming && result && result.resourceTiming && result.resourceTiming[this.id]) {
                    data.resourceTiming = result.resourceTiming[this.id];
                }
                this.fire(new ref_properties.Event('data', data));
                this._metadataFired = true;
            }
            if (this._coalesce) {
                this._updateWorkerData();
                this._coalesce = false;
            }
        });
    }
    loaded() {
        return this._loaded;
    }
    loadTile(tile, callback) {
        const message = !tile.actor ? 'loadTile' : 'reloadTile';
        tile.actor = this.actor;
        const params = {
            type: this.type,
            uid: tile.uid,
            tileID: tile.tileID,
            tileZoom: tile.tileZoom,
            zoom: tile.tileID.overscaledZ,
            maxZoom: this.maxzoom,
            tileSize: this.tileSize,
            source: this.id,
            pixelRatio: ref_properties.exported.devicePixelRatio,
            showCollisionBoxes: this.map.showCollisionBoxes,
            promoteId: this.promoteId
        };
        tile.request = this.actor.send(message, params, (err, data) => {
            delete tile.request;
            tile.unloadVectorData();
            if (tile.aborted) {
                return callback(null);
            }
            if (err) {
                return callback(err);
            }
            tile.loadVectorData(data, this.map.painter, message === 'reloadTile');
            return callback(null);
        }, undefined, message === 'loadTile');
    }
    abortTile(tile) {
        if (tile.request) {
            tile.request.cancel();
            delete tile.request;
        }
        tile.aborted = true;
    }
    unloadTile(tile) {
        tile.unloadVectorData();
        this.actor.send('removeTile', {
            uid: tile.uid,
            type: this.type,
            source: this.id
        });
    }
    onRemove() {
        if (this._pendingLoad) {
            this._pendingLoad.cancel();
        }
    }
    serialize() {
        return ref_properties.extend({}, this._options, {
            type: this.type,
            data: this._data
        });
    }
    hasTransition() {
        return false;
    }
}

var rasterBoundsAttributes = ref_properties.createLayout([
    {
        name: 'a_pos',
        type: 'Int16',
        components: 2
    },
    {
        name: 'a_texture_pos',
        type: 'Int16',
        components: 2
    }
]);

class ImageSource extends ref_properties.Evented {
    constructor(id, options, dispatcher, eventedParent) {
        super();
        this.id = id;
        this.dispatcher = dispatcher;
        this.coordinates = options.coordinates;
        this.type = 'image';
        this.minzoom = 0;
        this.maxzoom = 22;
        this.tileSize = 512;
        this.tiles = {};
        this._loaded = false;
        this.setEventedParent(eventedParent);
        this.options = options;
    }
    load(newCoordinates, successCallback) {
        this._loaded = false;
        this.fire(new ref_properties.Event('dataloading', { dataType: 'source' }));
        this.url = this.options.url;
        ref_properties.getImage(this.map._requestManager.transformRequest(this.url, ref_properties.ResourceType.Image), (err, image) => {
            this._loaded = true;
            if (err) {
                this.fire(new ref_properties.ErrorEvent(err));
            } else if (image) {
                this.image = image;
                if (newCoordinates) {
                    this.coordinates = newCoordinates;
                }
                if (successCallback) {
                    successCallback();
                }
                this._finishLoading();
            }
        });
    }
    loaded() {
        return this._loaded;
    }
    updateImage(options) {
        if (!this.image || !options.url) {
            return this;
        }
        this.options.url = options.url;
        this.load(options.coordinates, () => {
            this.texture = null;
        });
        return this;
    }
    _finishLoading() {
        if (this.map) {
            this.setCoordinates(this.coordinates);
            this.fire(new ref_properties.Event('data', {
                dataType: 'source',
                sourceDataType: 'metadata'
            }));
        }
    }
    onAdd(map) {
        this.map = map;
        this.load();
    }
    setCoordinates(coordinates) {
        this.coordinates = coordinates;
        const cornerCoords = coordinates.map(ref_properties.MercatorCoordinate.fromLngLat);
        this.tileID = getCoordinatesCenterTileID(cornerCoords);
        this.minzoom = this.maxzoom = this.tileID.z;
        const tileCoords = cornerCoords.map(coord => this.tileID.getTilePoint(coord)._round());
        this._boundsArray = new ref_properties.StructArrayLayout4i8();
        this._boundsArray.emplaceBack(tileCoords[0].x, tileCoords[0].y, 0, 0);
        this._boundsArray.emplaceBack(tileCoords[1].x, tileCoords[1].y, ref_properties.EXTENT, 0);
        this._boundsArray.emplaceBack(tileCoords[3].x, tileCoords[3].y, 0, ref_properties.EXTENT);
        this._boundsArray.emplaceBack(tileCoords[2].x, tileCoords[2].y, ref_properties.EXTENT, ref_properties.EXTENT);
        if (this.boundsBuffer) {
            this.boundsBuffer.destroy();
            delete this.boundsBuffer;
        }
        this.fire(new ref_properties.Event('data', {
            dataType: 'source',
            sourceDataType: 'content'
        }));
        return this;
    }
    prepare() {
        if (Object.keys(this.tiles).length === 0 || !this.image) {
            return;
        }
        const context = this.map.painter.context;
        const gl = context.gl;
        if (!this.boundsBuffer) {
            this.boundsBuffer = context.createVertexBuffer(this._boundsArray, rasterBoundsAttributes.members);
        }
        if (!this.boundsSegments) {
            this.boundsSegments = ref_properties.SegmentVector.simpleSegment(0, 0, 4, 2);
        }
        if (!this.texture) {
            this.texture = new ref_properties.Texture(context, this.image, gl.RGBA);
            this.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
        }
        for (const w in this.tiles) {
            const tile = this.tiles[w];
            if (tile.state !== 'loaded') {
                tile.state = 'loaded';
                tile.texture = this.texture;
            }
        }
    }
    loadTile(tile, callback) {
        if (this.tileID && this.tileID.equals(tile.tileID.canonical)) {
            this.tiles[String(tile.tileID.wrap)] = tile;
            tile.buckets = {};
            callback(null);
        } else {
            tile.state = 'errored';
            callback(null);
        }
    }
    serialize() {
        return {
            type: 'image',
            url: this.options.url,
            coordinates: this.coordinates
        };
    }
    hasTransition() {
        return false;
    }
}
function getCoordinatesCenterTileID(coords) {
    let minX = Infinity;
    let minY = Infinity;
    let maxX = -Infinity;
    let maxY = -Infinity;
    for (const coord of coords) {
        minX = Math.min(minX, coord.x);
        minY = Math.min(minY, coord.y);
        maxX = Math.max(maxX, coord.x);
        maxY = Math.max(maxY, coord.y);
    }
    const dx = maxX - minX;
    const dy = maxY - minY;
    const dMax = Math.max(dx, dy);
    const zoom = Math.max(0, Math.floor(-Math.log(dMax) / Math.LN2));
    const tilesAtZoom = Math.pow(2, zoom);
    return new ref_properties.CanonicalTileID(zoom, Math.floor((minX + maxX) / 2 * tilesAtZoom), Math.floor((minY + maxY) / 2 * tilesAtZoom));
}

class VideoSource extends ImageSource {
    constructor(id, options, dispatcher, eventedParent) {
        super(id, options, dispatcher, eventedParent);
        this.roundZoom = true;
        this.type = 'video';
        this.options = options;
    }
    load() {
        this._loaded = false;
        const options = this.options;
        this.urls = [];
        for (const url of options.urls) {
            this.urls.push(this.map._requestManager.transformRequest(url, ref_properties.ResourceType.Source).url);
        }
        ref_properties.getVideo(this.urls, (err, video) => {
            this._loaded = true;
            if (err) {
                this.fire(new ref_properties.ErrorEvent(err));
            } else if (video) {
                this.video = video;
                this.video.loop = true;
                this.video.addEventListener('playing', () => {
                    this.map.triggerRepaint();
                });
                if (this.map) {
                    this.video.play();
                }
                this._finishLoading();
            }
        });
    }
    pause() {
        if (this.video) {
            this.video.pause();
        }
    }
    play() {
        if (this.video) {
            this.video.play();
        }
    }
    seek(seconds) {
        if (this.video) {
            const seekableRange = this.video.seekable;
            if (seconds < seekableRange.start(0) || seconds > seekableRange.end(0)) {
                this.fire(new ref_properties.ErrorEvent(new ref_properties.ValidationError(`sources.${ this.id }`, null, `Playback for this video can be set only between the ${ seekableRange.start(0) } and ${ seekableRange.end(0) }-second mark.`)));
            } else
                this.video.currentTime = seconds;
        }
    }
    getVideo() {
        return this.video;
    }
    onAdd(map) {
        if (this.map)
            return;
        this.map = map;
        this.load();
        if (this.video) {
            this.video.play();
            this.setCoordinates(this.coordinates);
        }
    }
    prepare() {
        if (Object.keys(this.tiles).length === 0 || this.video.readyState < 2) {
            return;
        }
        const context = this.map.painter.context;
        const gl = context.gl;
        if (!this.boundsBuffer) {
            this.boundsBuffer = context.createVertexBuffer(this._boundsArray, rasterBoundsAttributes.members);
        }
        if (!this.boundsSegments) {
            this.boundsSegments = ref_properties.SegmentVector.simpleSegment(0, 0, 4, 2);
        }
        if (!this.texture) {
            this.texture = new ref_properties.Texture(context, this.video, gl.RGBA);
            this.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
        } else if (!this.video.paused) {
            this.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
            gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, this.video);
        }
        for (const w in this.tiles) {
            const tile = this.tiles[w];
            if (tile.state !== 'loaded') {
                tile.state = 'loaded';
                tile.texture = this.texture;
            }
        }
    }
    serialize() {
        return {
            type: 'video',
            urls: this.urls,
            coordinates: this.coordinates
        };
    }
    hasTransition() {
        return this.video && !this.video.paused;
    }
}

class CanvasSource extends ImageSource {
    constructor(id, options, dispatcher, eventedParent) {
        super(id, options, dispatcher, eventedParent);
        if (!options.coordinates) {
            this.fire(new ref_properties.ErrorEvent(new ref_properties.ValidationError(`sources.${ id }`, null, 'missing required property "coordinates"')));
        } else if (!Array.isArray(options.coordinates) || options.coordinates.length !== 4 || options.coordinates.some(c => !Array.isArray(c) || c.length !== 2 || c.some(l => typeof l !== 'number'))) {
            this.fire(new ref_properties.ErrorEvent(new ref_properties.ValidationError(`sources.${ id }`, null, '"coordinates" property must be an array of 4 longitude/latitude array pairs')));
        }
        if (options.animate && typeof options.animate !== 'boolean') {
            this.fire(new ref_properties.ErrorEvent(new ref_properties.ValidationError(`sources.${ id }`, null, 'optional "animate" property must be a boolean value')));
        }
        if (!options.canvas) {
            this.fire(new ref_properties.ErrorEvent(new ref_properties.ValidationError(`sources.${ id }`, null, 'missing required property "canvas"')));
        } else if (typeof options.canvas !== 'string' && !(options.canvas instanceof ref_properties.window.HTMLCanvasElement)) {
            this.fire(new ref_properties.ErrorEvent(new ref_properties.ValidationError(`sources.${ id }`, null, '"canvas" must be either a string representing the ID of the canvas element from which to read, or an HTMLCanvasElement instance')));
        }
        this.options = options;
        this.animate = options.animate !== undefined ? options.animate : true;
    }
    load() {
        this._loaded = true;
        if (!this.canvas) {
            this.canvas = this.options.canvas instanceof ref_properties.window.HTMLCanvasElement ? this.options.canvas : ref_properties.window.document.getElementById(this.options.canvas);
        }
        this.width = this.canvas.width;
        this.height = this.canvas.height;
        if (this._hasInvalidDimensions()) {
            this.fire(new ref_properties.ErrorEvent(new Error('Canvas dimensions cannot be less than or equal to zero.')));
            return;
        }
        this.play = function () {
            this._playing = true;
            this.map.triggerRepaint();
        };
        this.pause = function () {
            if (this._playing) {
                this.prepare();
                this._playing = false;
            }
        };
        this._finishLoading();
    }
    getCanvas() {
        return this.canvas;
    }
    onAdd(map) {
        this.map = map;
        this.load();
        if (this.canvas) {
            if (this.animate)
                this.play();
        }
    }
    onRemove() {
        this.pause();
    }
    prepare() {
        let resize = false;
        if (this.canvas.width !== this.width) {
            this.width = this.canvas.width;
            resize = true;
        }
        if (this.canvas.height !== this.height) {
            this.height = this.canvas.height;
            resize = true;
        }
        if (this._hasInvalidDimensions())
            return;
        if (Object.keys(this.tiles).length === 0)
            return;
        const context = this.map.painter.context;
        const gl = context.gl;
        if (!this.boundsBuffer) {
            this.boundsBuffer = context.createVertexBuffer(this._boundsArray, rasterBoundsAttributes.members);
        }
        if (!this.boundsSegments) {
            this.boundsSegments = ref_properties.SegmentVector.simpleSegment(0, 0, 4, 2);
        }
        if (!this.texture) {
            this.texture = new ref_properties.Texture(context, this.canvas, gl.RGBA, { premultiply: true });
        } else if (resize || this._playing) {
            this.texture.update(this.canvas, { premultiply: true });
        }
        for (const w in this.tiles) {
            const tile = this.tiles[w];
            if (tile.state !== 'loaded') {
                tile.state = 'loaded';
                tile.texture = this.texture;
            }
        }
    }
    serialize() {
        return {
            type: 'canvas',
            coordinates: this.coordinates
        };
    }
    hasTransition() {
        return this._playing;
    }
    _hasInvalidDimensions() {
        for (const x of [
                this.canvas.width,
                this.canvas.height
            ]) {
            if (isNaN(x) || x <= 0)
                return true;
        }
        return false;
    }
}

const sourceTypes = {
    vector: VectorTileSource,
    raster: RasterTileSource,
    'raster-dem': RasterDEMTileSource,
    geojson: GeoJSONSource,
    video: VideoSource,
    image: ImageSource,
    canvas: CanvasSource
};
const create = function (id, specification, dispatcher, eventedParent) {
    const source = new sourceTypes[specification.type](id, specification, dispatcher, eventedParent);
    if (source.id !== id) {
        throw new Error(`Expected Source id to be ${ id } instead of ${ source.id }`);
    }
    ref_properties.bindAll([
        'load',
        'abort',
        'unload',
        'serialize',
        'prepare'
    ], source);
    return source;
};
const getType = function (name) {
    return sourceTypes[name];
};
const setType = function (name, type) {
    sourceTypes[name] = type;
};

function getPixelPosMatrix(transform, tileID) {
    const t = ref_properties.identity([]);
    ref_properties.scale$1(t, t, [
        transform.width * 0.5,
        -transform.height * 0.5,
        1
    ]);
    ref_properties.translate(t, t, [
        1,
        -1,
        0
    ]);
    return ref_properties.multiply(t, t, transform.calculateProjMatrix(tileID.toUnwrapped()));
}
function queryRenderedFeatures(sourceCache, styleLayers, serializedLayers, queryGeometry, params, transform, use3DQuery, visualizeQueryGeometry = false) {
    const tileResults = sourceCache.tilesIn(queryGeometry, use3DQuery, visualizeQueryGeometry);
    tileResults.sort(sortTilesIn);
    const renderedFeatureLayers = [];
    for (const tileResult of tileResults) {
        renderedFeatureLayers.push({
            wrappedTileID: tileResult.tile.tileID.wrapped().key,
            queryResults: tileResult.tile.queryRenderedFeatures(styleLayers, serializedLayers, sourceCache._state, tileResult, params, transform, getPixelPosMatrix(sourceCache.transform, tileResult.tile.tileID), visualizeQueryGeometry)
        });
    }
    const result = mergeRenderedFeatureLayers(renderedFeatureLayers);
    for (const layerID in result) {
        result[layerID].forEach(featureWrapper => {
            const feature = featureWrapper.feature;
            const state = sourceCache.getFeatureState(feature.layer['source-layer'], feature.id);
            feature.source = feature.layer.source;
            if (feature.layer['source-layer']) {
                feature.sourceLayer = feature.layer['source-layer'];
            }
            feature.state = state;
        });
    }
    return result;
}
function queryRenderedSymbols(styleLayers, serializedLayers, getLayerSourceCache, queryGeometry, params, collisionIndex, retainedQueryData) {
    const result = {};
    const renderedSymbols = collisionIndex.queryRenderedSymbols(queryGeometry);
    const bucketQueryData = [];
    for (const bucketInstanceId of Object.keys(renderedSymbols).map(Number)) {
        bucketQueryData.push(retainedQueryData[bucketInstanceId]);
    }
    bucketQueryData.sort(sortTilesIn);
    for (const queryData of bucketQueryData) {
        const bucketSymbols = queryData.featureIndex.lookupSymbolFeatures(renderedSymbols[queryData.bucketInstanceId], serializedLayers, queryData.bucketIndex, queryData.sourceLayerIndex, params.filter, params.layers, params.availableImages, styleLayers);
        for (const layerID in bucketSymbols) {
            const resultFeatures = result[layerID] = result[layerID] || [];
            const layerSymbols = bucketSymbols[layerID];
            layerSymbols.sort((a, b) => {
                const featureSortOrder = queryData.featureSortOrder;
                if (featureSortOrder) {
                    const sortedA = featureSortOrder.indexOf(a.featureIndex);
                    const sortedB = featureSortOrder.indexOf(b.featureIndex);
                    return sortedB - sortedA;
                } else {
                    return b.featureIndex - a.featureIndex;
                }
            });
            for (const symbolFeature of layerSymbols) {
                resultFeatures.push(symbolFeature);
            }
        }
    }
    for (const layerName in result) {
        result[layerName].forEach(featureWrapper => {
            const feature = featureWrapper.feature;
            const layer = styleLayers[layerName];
            const sourceCache = getLayerSourceCache(layer);
            const state = sourceCache.getFeatureState(feature.layer['source-layer'], feature.id);
            feature.source = feature.layer.source;
            if (feature.layer['source-layer']) {
                feature.sourceLayer = feature.layer['source-layer'];
            }
            feature.state = state;
        });
    }
    return result;
}
function querySourceFeatures(sourceCache, params) {
    const tiles = sourceCache.getRenderableIds().map(id => {
        return sourceCache.getTileByID(id);
    });
    const result = [];
    const dataTiles = {};
    for (let i = 0; i < tiles.length; i++) {
        const tile = tiles[i];
        const dataID = tile.tileID.canonical.key;
        if (!dataTiles[dataID]) {
            dataTiles[dataID] = true;
            tile.querySourceFeatures(result, params);
        }
    }
    return result;
}
function sortTilesIn(a, b) {
    const idA = a.tileID;
    const idB = b.tileID;
    return idA.overscaledZ - idB.overscaledZ || idA.canonical.y - idB.canonical.y || idA.wrap - idB.wrap || idA.canonical.x - idB.canonical.x;
}
function mergeRenderedFeatureLayers(tiles) {
    const result = {};
    const wrappedIDLayerMap = {};
    for (const tile of tiles) {
        const queryResults = tile.queryResults;
        const wrappedID = tile.wrappedTileID;
        const wrappedIDLayers = wrappedIDLayerMap[wrappedID] = wrappedIDLayerMap[wrappedID] || {};
        for (const layerID in queryResults) {
            const tileFeatures = queryResults[layerID];
            const wrappedIDFeatures = wrappedIDLayers[layerID] = wrappedIDLayers[layerID] || {};
            const resultFeatures = result[layerID] = result[layerID] || [];
            for (const tileFeature of tileFeatures) {
                if (!wrappedIDFeatures[tileFeature.featureIndex]) {
                    wrappedIDFeatures[tileFeature.featureIndex] = true;
                    resultFeatures.push(tileFeature);
                }
            }
        }
    }
    return result;
}

function WebWorker () {
    return exported.workerClass != null ? new exported.workerClass() : new ref_properties.window.Worker(exported.workerUrl);
}

const PRELOAD_POOL_ID = 'mapboxgl_preloaded_worker_pool';
class WorkerPool {
    constructor() {
        this.active = {};
    }
    acquire(mapId) {
        if (!this.workers) {
            this.workers = [];
            while (this.workers.length < WorkerPool.workerCount) {
                this.workers.push(new WebWorker());
            }
        }
        this.active[mapId] = true;
        return this.workers.slice();
    }
    release(mapId) {
        delete this.active[mapId];
        if (this.numActive() === 0) {
            this.workers.forEach(w => {
                w.terminate();
            });
            this.workers = null;
        }
    }
    isPreloaded() {
        return !!this.active[PRELOAD_POOL_ID];
    }
    numActive() {
        return Object.keys(this.active).length;
    }
}
WorkerPool.workerCount = 2;

let globalWorkerPool;
function getGlobalWorkerPool() {
    if (!globalWorkerPool) {
        globalWorkerPool = new WorkerPool();
    }
    return globalWorkerPool;
}
function prewarm() {
    const workerPool = getGlobalWorkerPool();
    workerPool.acquire(PRELOAD_POOL_ID);
}
function clearPrewarmedResources() {
    const pool = globalWorkerPool;
    if (pool) {
        if (pool.isPreloaded() && pool.numActive() === 1) {
            pool.release(PRELOAD_POOL_ID);
            globalWorkerPool = null;
        } else {
            console.warn('Could not clear WebWorkers since there are active Map instances that still reference it. The pre-warmed WebWorker pool can only be cleared when all map instances have been removed with map.remove()');
        }
    }
}

function deref(layer, parent) {
    const result = {};
    for (const k in layer) {
        if (k !== 'ref') {
            result[k] = layer[k];
        }
    }
    ref_properties.refProperties.forEach(k => {
        if (k in parent) {
            result[k] = parent[k];
        }
    });
    return result;
}
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;
}

function emptyStyle() {
    const style = {};
    const version = ref_properties.spec['$version'];
    for (const styleKey in ref_properties.spec['$root']) {
        const spec = ref_properties.spec['$root'][styleKey];
        if (spec.required) {
            let value = null;
            if (styleKey === 'version') {
                value = version;
            } else {
                if (spec.type === 'array') {
                    value = [];
                } else {
                    value = {};
                }
            }
            if (value != null) {
                style[styleKey] = value;
            }
        }
    }
    return style;
}

const operations = {
    setStyle: 'setStyle',
    addLayer: 'addLayer',
    removeLayer: 'removeLayer',
    setPaintProperty: 'setPaintProperty',
    setLayoutProperty: 'setLayoutProperty',
    setFilter: 'setFilter',
    addSource: 'addSource',
    removeSource: 'removeSource',
    setGeoJSONSourceData: 'setGeoJSONSourceData',
    setLayerZoomRange: 'setLayerZoomRange',
    setLayerProperty: 'setLayerProperty',
    setCenter: 'setCenter',
    setZoom: 'setZoom',
    setBearing: 'setBearing',
    setPitch: 'setPitch',
    setSprite: 'setSprite',
    setGlyphs: 'setGlyphs',
    setTransition: 'setTransition',
    setLight: 'setLight',
    setTerrain: 'setTerrain',
    setFog: 'setFog'
};
function addSource(sourceId, after, commands) {
    commands.push({
        command: operations.addSource,
        args: [
            sourceId,
            after[sourceId]
        ]
    });
}
function removeSource(sourceId, commands, sourcesRemoved) {
    commands.push({
        command: operations.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 (!before[sourceId].hasOwnProperty(prop))
            continue;
        if (prop !== 'data' && !deepEqual(before[sourceId][prop], after[sourceId][prop])) {
            return false;
        }
    }
    for (prop in after[sourceId]) {
        if (!after[sourceId].hasOwnProperty(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 (!before.hasOwnProperty(sourceId))
            continue;
        if (!after.hasOwnProperty(sourceId)) {
            removeSource(sourceId, commands, sourcesRemoved);
        }
    }
    for (sourceId in after) {
        if (!after.hasOwnProperty(sourceId))
            continue;
        if (!before.hasOwnProperty(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)) {
                commands.push({
                    command: operations.setGeoJSONSourceData,
                    args: [
                        sourceId,
                        after[sourceId].data
                    ]
                });
            } else {
                updateSource(sourceId, after, commands, sourcesRemoved);
            }
        }
    }
}
function diffLayerPropertyChanges(before, after, commands, layerId, klass, command) {
    before = before || {};
    after = after || {};
    let prop;
    for (prop in before) {
        if (!before.hasOwnProperty(prop))
            continue;
        if (!deepEqual(before[prop], after[prop])) {
            commands.push({
                command,
                args: [
                    layerId,
                    prop,
                    after[prop],
                    klass
                ]
            });
        }
    }
    for (prop in after) {
        if (!after.hasOwnProperty(prop) || before.hasOwnProperty(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 i, d, layerId, beforeLayer, afterLayer, insertBeforeLayerId, prop;
    for (i = 0, d = 0; i < beforeOrder.length; i++) {
        layerId = beforeOrder[i];
        if (!afterIndex.hasOwnProperty(layerId)) {
            commands.push({
                command: operations.removeLayer,
                args: [layerId]
            });
            tracker.splice(tracker.indexOf(layerId, d), 1);
        } else {
            d++;
        }
    }
    for (i = 0, d = 0; i < afterOrder.length; i++) {
        layerId = afterOrder[afterOrder.length - 1 - i];
        if (tracker[tracker.length - 1 - i] === layerId)
            continue;
        if (beforeIndex.hasOwnProperty(layerId)) {
            commands.push({
                command: operations.removeLayer,
                args: [layerId]
            });
            tracker.splice(tracker.lastIndexOf(layerId, tracker.length - d), 1);
        } else {
            d++;
        }
        insertBeforeLayerId = tracker[tracker.length - i];
        commands.push({
            command: operations.addLayer,
            args: [
                afterIndex[layerId],
                insertBeforeLayerId
            ]
        });
        tracker.splice(tracker.length - i, 0, layerId);
        clean[layerId] = true;
    }
    for (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)) {
            commands.push({
                command: operations.removeLayer,
                args: [layerId]
            });
            insertBeforeLayerId = tracker[tracker.lastIndexOf(layerId) + 1];
            commands.push({
                command: operations.addLayer,
                args: [
                    afterLayer,
                    insertBeforeLayerId
                ]
            });
            continue;
        }
        diffLayerPropertyChanges(beforeLayer.layout, afterLayer.layout, commands, layerId, null, operations.setLayoutProperty);
        diffLayerPropertyChanges(beforeLayer.paint, afterLayer.paint, commands, layerId, null, operations.setPaintProperty);
        if (!deepEqual(beforeLayer.filter, afterLayer.filter)) {
            commands.push({
                command: operations.setFilter,
                args: [
                    layerId,
                    afterLayer.filter
                ]
            });
        }
        if (!deepEqual(beforeLayer.minzoom, afterLayer.minzoom) || !deepEqual(beforeLayer.maxzoom, afterLayer.maxzoom)) {
            commands.push({
                command: operations.setLayerZoomRange,
                args: [
                    layerId,
                    afterLayer.minzoom,
                    afterLayer.maxzoom
                ]
            });
        }
        for (prop in beforeLayer) {
            if (!beforeLayer.hasOwnProperty(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), operations.setPaintProperty);
            } else if (!deepEqual(beforeLayer[prop], afterLayer[prop])) {
                commands.push({
                    command: operations.setLayerProperty,
                    args: [
                        layerId,
                        prop,
                        afterLayer[prop]
                    ]
                });
            }
        }
        for (prop in afterLayer) {
            if (!afterLayer.hasOwnProperty(prop) || beforeLayer.hasOwnProperty(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), operations.setPaintProperty);
            } else if (!deepEqual(beforeLayer[prop], afterLayer[prop])) {
                commands.push({
                    command: operations.setLayerProperty,
                    args: [
                        layerId,
                        prop,
                        afterLayer[prop]
                    ]
                });
            }
        }
    }
}
function diffStyles(before, after) {
    if (!before)
        return [{
                command: operations.setStyle,
                args: [after]
            }];
    let commands = [];
    try {
        if (!deepEqual(before.version, after.version)) {
            return [{
                    command: operations.setStyle,
                    args: [after]
                }];
        }
        if (!deepEqual(before.center, after.center)) {
            commands.push({
                command: operations.setCenter,
                args: [after.center]
            });
        }
        if (!deepEqual(before.zoom, after.zoom)) {
            commands.push({
                command: operations.setZoom,
                args: [after.zoom]
            });
        }
        if (!deepEqual(before.bearing, after.bearing)) {
            commands.push({
                command: operations.setBearing,
                args: [after.bearing]
            });
        }
        if (!deepEqual(before.pitch, after.pitch)) {
            commands.push({
                command: operations.setPitch,
                args: [after.pitch]
            });
        }
        if (!deepEqual(before.sprite, after.sprite)) {
            commands.push({
                command: operations.setSprite,
                args: [after.sprite]
            });
        }
        if (!deepEqual(before.glyphs, after.glyphs)) {
            commands.push({
                command: operations.setGlyphs,
                args: [after.glyphs]
            });
        }
        if (!deepEqual(before.transition, after.transition)) {
            commands.push({
                command: operations.setTransition,
                args: [after.transition]
            });
        }
        if (!deepEqual(before.light, after.light)) {
            commands.push({
                command: operations.setLight,
                args: [after.light]
            });
        }
        if (!deepEqual(before.fog, after.fog)) {
            commands.push({
                command: operations.setFog,
                args: [after.fog]
            });
        }
        const sourcesRemoved = {};
        const removeOrAddSourceCommands = [];
        diffSources(before.sources, after.sources, removeOrAddSourceCommands, sourcesRemoved);
        const beforeLayers = [];
        if (before.layers) {
            before.layers.forEach(layer => {
                if (sourcesRemoved[layer.source]) {
                    commands.push({
                        command: operations.removeLayer,
                        args: [layer.id]
                    });
                } else {
                    beforeLayers.push(layer);
                }
            });
        }
        let beforeTerrain = before.terrain;
        if (beforeTerrain) {
            if (sourcesRemoved[beforeTerrain.source]) {
                commands.push({
                    command: operations.setTerrain,
                    args: [undefined]
                });
                beforeTerrain = undefined;
            }
        }
        commands = commands.concat(removeOrAddSourceCommands);
        if (!deepEqual(beforeTerrain, after.terrain)) {
            commands.push({
                command: operations.setTerrain,
                args: [after.terrain]
            });
        }
        diffLayers(beforeLayers, after.layers, commands);
    } catch (e) {
        console.warn('Unable to compute style diff:', e);
        commands = [{
                command: operations.setStyle,
                args: [after]
            }];
    }
    return commands;
}

class PathInterpolator {
    constructor(points_, padding_) {
        this.reset(points_, padding_);
    }
    reset(points_, padding_) {
        this.points = points_ || [];
        this._distances = [0];
        for (let i = 1; i < this.points.length; i++) {
            this._distances[i] = this._distances[i - 1] + this.points[i].dist(this.points[i - 1]);
        }
        this.length = this._distances[this._distances.length - 1];
        this.padding = Math.min(padding_ || 0, this.length * 0.5);
        this.paddedLength = this.length - this.padding * 2;
    }
    lerp(t) {
        if (this.points.length === 1) {
            return this.points[0];
        }
        t = ref_properties.clamp(t, 0, 1);
        let currentIndex = 1;
        let distOfCurrentIdx = this._distances[currentIndex];
        const distToTarget = t * this.paddedLength + this.padding;
        while (distOfCurrentIdx < distToTarget && currentIndex < this._distances.length) {
            distOfCurrentIdx = this._distances[++currentIndex];
        }
        const idxOfPrevPoint = currentIndex - 1;
        const distOfPrevIdx = this._distances[idxOfPrevPoint];
        const segmentLength = distOfCurrentIdx - distOfPrevIdx;
        const segmentT = segmentLength > 0 ? (distToTarget - distOfPrevIdx) / segmentLength : 0;
        return this.points[idxOfPrevPoint].mult(1 - segmentT).add(this.points[currentIndex].mult(segmentT));
    }
}

class GridIndex {
    constructor(width, height, cellSize) {
        const boxCells = this.boxCells = [];
        const circleCells = this.circleCells = [];
        this.xCellCount = Math.ceil(width / cellSize);
        this.yCellCount = Math.ceil(height / cellSize);
        for (let i = 0; i < this.xCellCount * this.yCellCount; i++) {
            boxCells.push([]);
            circleCells.push([]);
        }
        this.circleKeys = [];
        this.boxKeys = [];
        this.bboxes = [];
        this.circles = [];
        this.width = width;
        this.height = height;
        this.xScale = this.xCellCount / width;
        this.yScale = this.yCellCount / height;
        this.boxUid = 0;
        this.circleUid = 0;
    }
    keysLength() {
        return this.boxKeys.length + this.circleKeys.length;
    }
    insert(key, x1, y1, x2, y2) {
        this._forEachCell(x1, y1, x2, y2, this._insertBoxCell, this.boxUid++);
        this.boxKeys.push(key);
        this.bboxes.push(x1);
        this.bboxes.push(y1);
        this.bboxes.push(x2);
        this.bboxes.push(y2);
    }
    insertCircle(key, x, y, radius) {
        this._forEachCell(x - radius, y - radius, x + radius, y + radius, this._insertCircleCell, this.circleUid++);
        this.circleKeys.push(key);
        this.circles.push(x);
        this.circles.push(y);
        this.circles.push(radius);
    }
    _insertBoxCell(x1, y1, x2, y2, cellIndex, uid) {
        this.boxCells[cellIndex].push(uid);
    }
    _insertCircleCell(x1, y1, x2, y2, cellIndex, uid) {
        this.circleCells[cellIndex].push(uid);
    }
    _query(x1, y1, x2, y2, hitTest, predicate) {
        if (x2 < 0 || x1 > this.width || y2 < 0 || y1 > this.height) {
            return hitTest ? false : [];
        }
        const result = [];
        if (x1 <= 0 && y1 <= 0 && this.width <= x2 && this.height <= y2) {
            if (hitTest) {
                return true;
            }
            for (let boxUid = 0; boxUid < this.boxKeys.length; boxUid++) {
                result.push({
                    key: this.boxKeys[boxUid],
                    x1: this.bboxes[boxUid * 4],
                    y1: this.bboxes[boxUid * 4 + 1],
                    x2: this.bboxes[boxUid * 4 + 2],
                    y2: this.bboxes[boxUid * 4 + 3]
                });
            }
            for (let circleUid = 0; circleUid < this.circleKeys.length; circleUid++) {
                const x = this.circles[circleUid * 3];
                const y = this.circles[circleUid * 3 + 1];
                const radius = this.circles[circleUid * 3 + 2];
                result.push({
                    key: this.circleKeys[circleUid],
                    x1: x - radius,
                    y1: y - radius,
                    x2: x + radius,
                    y2: y + radius
                });
            }
            return predicate ? result.filter(predicate) : result;
        } else {
            const queryArgs = {
                hitTest,
                seenUids: {
                    box: {},
                    circle: {}
                }
            };
            this._forEachCell(x1, y1, x2, y2, this._queryCell, result, queryArgs, predicate);
            return hitTest ? result.length > 0 : result;
        }
    }
    _queryCircle(x, y, radius, hitTest, predicate) {
        const x1 = x - radius;
        const x2 = x + radius;
        const y1 = y - radius;
        const y2 = y + radius;
        if (x2 < 0 || x1 > this.width || y2 < 0 || y1 > this.height) {
            return hitTest ? false : [];
        }
        const result = [];
        const queryArgs = {
            hitTest,
            circle: {
                x,
                y,
                radius
            },
            seenUids: {
                box: {},
                circle: {}
            }
        };
        this._forEachCell(x1, y1, x2, y2, this._queryCellCircle, result, queryArgs, predicate);
        return hitTest ? result.length > 0 : result;
    }
    query(x1, y1, x2, y2, predicate) {
        return this._query(x1, y1, x2, y2, false, predicate);
    }
    hitTest(x1, y1, x2, y2, predicate) {
        return this._query(x1, y1, x2, y2, true, predicate);
    }
    hitTestCircle(x, y, radius, predicate) {
        return this._queryCircle(x, y, radius, true, predicate);
    }
    _queryCell(x1, y1, x2, y2, cellIndex, result, queryArgs, predicate) {
        const seenUids = queryArgs.seenUids;
        const boxCell = this.boxCells[cellIndex];
        if (boxCell !== null) {
            const bboxes = this.bboxes;
            for (const boxUid of boxCell) {
                if (!seenUids.box[boxUid]) {
                    seenUids.box[boxUid] = true;
                    const offset = boxUid * 4;
                    if (x1 <= bboxes[offset + 2] && y1 <= bboxes[offset + 3] && x2 >= bboxes[offset + 0] && y2 >= bboxes[offset + 1] && (!predicate || predicate(this.boxKeys[boxUid]))) {
                        if (queryArgs.hitTest) {
                            result.push(true);
                            return true;
                        } else {
                            result.push({
                                key: this.boxKeys[boxUid],
                                x1: bboxes[offset],
                                y1: bboxes[offset + 1],
                                x2: bboxes[offset + 2],
                                y2: bboxes[offset + 3]
                            });
                        }
                    }
                }
            }
        }
        const circleCell = this.circleCells[cellIndex];
        if (circleCell !== null) {
            const circles = this.circles;
            for (const circleUid of circleCell) {
                if (!seenUids.circle[circleUid]) {
                    seenUids.circle[circleUid] = true;
                    const offset = circleUid * 3;
                    if (this._circleAndRectCollide(circles[offset], circles[offset + 1], circles[offset + 2], x1, y1, x2, y2) && (!predicate || predicate(this.circleKeys[circleUid]))) {
                        if (queryArgs.hitTest) {
                            result.push(true);
                            return true;
                        } else {
                            const x = circles[offset];
                            const y = circles[offset + 1];
                            const radius = circles[offset + 2];
                            result.push({
                                key: this.circleKeys[circleUid],
                                x1: x - radius,
                                y1: y - radius,
                                x2: x + radius,
                                y2: y + radius
                            });
                        }
                    }
                }
            }
        }
    }
    _queryCellCircle(x1, y1, x2, y2, cellIndex, result, queryArgs, predicate) {
        const circle = queryArgs.circle;
        const seenUids = queryArgs.seenUids;
        const boxCell = this.boxCells[cellIndex];
        if (boxCell !== null) {
            const bboxes = this.bboxes;
            for (const boxUid of boxCell) {
                if (!seenUids.box[boxUid]) {
                    seenUids.box[boxUid] = true;
                    const offset = boxUid * 4;
                    if (this._circleAndRectCollide(circle.x, circle.y, circle.radius, bboxes[offset + 0], bboxes[offset + 1], bboxes[offset + 2], bboxes[offset + 3]) && (!predicate || predicate(this.boxKeys[boxUid]))) {
                        result.push(true);
                        return true;
                    }
                }
            }
        }
        const circleCell = this.circleCells[cellIndex];
        if (circleCell !== null) {
            const circles = this.circles;
            for (const circleUid of circleCell) {
                if (!seenUids.circle[circleUid]) {
                    seenUids.circle[circleUid] = true;
                    const offset = circleUid * 3;
                    if (this._circlesCollide(circles[offset], circles[offset + 1], circles[offset + 2], circle.x, circle.y, circle.radius) && (!predicate || predicate(this.circleKeys[circleUid]))) {
                        result.push(true);
                        return true;
                    }
                }
            }
        }
    }
    _forEachCell(x1, y1, x2, y2, fn, arg1, arg2, predicate) {
        const cx1 = this._convertToXCellCoord(x1);
        const cy1 = this._convertToYCellCoord(y1);
        const cx2 = this._convertToXCellCoord(x2);
        const cy2 = this._convertToYCellCoord(y2);
        for (let x = cx1; x <= cx2; x++) {
            for (let y = cy1; y <= cy2; y++) {
                const cellIndex = this.xCellCount * y + x;
                if (fn.call(this, x1, y1, x2, y2, cellIndex, arg1, arg2, predicate))
                    return;
            }
        }
    }
    _convertToXCellCoord(x) {
        return Math.max(0, Math.min(this.xCellCount - 1, Math.floor(x * this.xScale)));
    }
    _convertToYCellCoord(y) {
        return Math.max(0, Math.min(this.yCellCount - 1, Math.floor(y * this.yScale)));
    }
    _circlesCollide(x1, y1, r1, x2, y2, r2) {
        const dx = x2 - x1;
        const dy = y2 - y1;
        const bothRadii = r1 + r2;
        return bothRadii * bothRadii > dx * dx + dy * dy;
    }
    _circleAndRectCollide(circleX, circleY, radius, x1, y1, x2, y2) {
        const halfRectWidth = (x2 - x1) / 2;
        const distX = Math.abs(circleX - (x1 + halfRectWidth));
        if (distX > halfRectWidth + radius) {
            return false;
        }
        const halfRectHeight = (y2 - y1) / 2;
        const distY = Math.abs(circleY - (y1 + halfRectHeight));
        if (distY > halfRectHeight + radius) {
            return false;
        }
        if (distX <= halfRectWidth || distY <= halfRectHeight) {
            return true;
        }
        const dx = distX - halfRectWidth;
        const dy = distY - halfRectHeight;
        return dx * dx + dy * dy <= radius * radius;
    }
}

function getLabelPlaneMatrix(posMatrix, pitchWithMap, rotateWithMap, transform, pixelsToTileUnits) {
    const m = ref_properties.create$1();
    if (pitchWithMap) {
        ref_properties.scale$1(m, m, [
            1 / pixelsToTileUnits,
            1 / pixelsToTileUnits,
            1
        ]);
        if (!rotateWithMap) {
            ref_properties.rotateZ(m, m, transform.angle);
        }
    } else {
        ref_properties.multiply(m, transform.labelPlaneMatrix, posMatrix);
    }
    return m;
}
function getGlCoordMatrix(posMatrix, pitchWithMap, rotateWithMap, transform, pixelsToTileUnits) {
    if (pitchWithMap) {
        const m = ref_properties.clone$1(posMatrix);
        ref_properties.scale$1(m, m, [
            pixelsToTileUnits,
            pixelsToTileUnits,
            1
        ]);
        if (!rotateWithMap) {
            ref_properties.rotateZ(m, m, -transform.angle);
        }
        return m;
    } else {
        return transform.glCoordMatrix;
    }
}
function project(point, matrix, elevation = 0) {
    const pos = [
        point.x,
        point.y,
        elevation,
        1
    ];
    if (elevation) {
        ref_properties.transformMat4(pos, pos, matrix);
    } else {
        xyTransformMat4(pos, pos, matrix);
    }
    const w = pos[3];
    return {
        point: new ref_properties.pointGeometry(pos[0] / w, pos[1] / w),
        signedDistanceFromCamera: w
    };
}
function getPerspectiveRatio(cameraToCenterDistance, signedDistanceFromCamera) {
    return Math.min(0.5 + 0.5 * (cameraToCenterDistance / signedDistanceFromCamera), 1.5);
}
function isVisible(anchorPos, clippingBuffer) {
    const x = anchorPos[0] / anchorPos[3];
    const y = anchorPos[1] / anchorPos[3];
    const inPaddedViewport = x >= -clippingBuffer[0] && x <= clippingBuffer[0] && y >= -clippingBuffer[1] && y <= clippingBuffer[1];
    return inPaddedViewport;
}
function updateLineLabels(bucket, posMatrix, painter, isText, labelPlaneMatrix, glCoordMatrix, pitchWithMap, keepUpright, getElevation) {
    const sizeData = isText ? bucket.textSizeData : bucket.iconSizeData;
    const partiallyEvaluatedSize = ref_properties.evaluateSizeForZoom(sizeData, painter.transform.zoom);
    const clippingBuffer = [
        256 / painter.width * 2 + 1,
        256 / painter.height * 2 + 1
    ];
    const dynamicLayoutVertexArray = isText ? bucket.text.dynamicLayoutVertexArray : bucket.icon.dynamicLayoutVertexArray;
    dynamicLayoutVertexArray.clear();
    const lineVertexArray = bucket.lineVertexArray;
    const placedSymbols = isText ? bucket.text.placedSymbolArray : bucket.icon.placedSymbolArray;
    const aspectRatio = painter.transform.width / painter.transform.height;
    let useVertical = false;
    for (let s = 0; s < placedSymbols.length; s++) {
        const symbol = placedSymbols.get(s);
        if (symbol.hidden || symbol.writingMode === ref_properties.WritingMode.vertical && !useVertical) {
            hideGlyphs(symbol.numGlyphs, dynamicLayoutVertexArray);
            continue;
        }
        useVertical = false;
        const elevation = getElevation ? getElevation({
            x: symbol.anchorX,
            y: symbol.anchorY
        }) : 0;
        const anchorPos = [
            symbol.anchorX,
            symbol.anchorY,
            elevation,
            1
        ];
        ref_properties.transformMat4(anchorPos, anchorPos, posMatrix);
        if (!isVisible(anchorPos, clippingBuffer)) {
            hideGlyphs(symbol.numGlyphs, dynamicLayoutVertexArray);
            continue;
        }
        const cameraToAnchorDistance = anchorPos[3];
        const perspectiveRatio = getPerspectiveRatio(painter.transform.cameraToCenterDistance, cameraToAnchorDistance);
        const fontSize = ref_properties.evaluateSizeForFeature(sizeData, partiallyEvaluatedSize, symbol);
        const pitchScaledFontSize = pitchWithMap ? fontSize / perspectiveRatio : fontSize * perspectiveRatio;
        const tileAnchorPoint = new ref_properties.pointGeometry(symbol.anchorX, symbol.anchorY);
        const transformedTileAnchor = project(tileAnchorPoint, labelPlaneMatrix, elevation);
        if (transformedTileAnchor.signedDistanceFromCamera <= 0) {
            hideGlyphs(symbol.numGlyphs, dynamicLayoutVertexArray);
            continue;
        }
        const anchorPoint = transformedTileAnchor.point;
        let projectionCache = {};
        const getElevationForPlacement = pitchWithMap ? null : getElevation;
        const placeUnflipped = placeGlyphsAlongLine(symbol, pitchScaledFontSize, false, keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, bucket.glyphOffsetArray, lineVertexArray, dynamicLayoutVertexArray, anchorPoint, tileAnchorPoint, projectionCache, aspectRatio, getElevationForPlacement);
        useVertical = placeUnflipped.useVertical;
        if (getElevationForPlacement && placeUnflipped.needsFlipping)
            projectionCache = {};
        if (placeUnflipped.notEnoughRoom || useVertical || placeUnflipped.needsFlipping && placeGlyphsAlongLine(symbol, pitchScaledFontSize, true, keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, bucket.glyphOffsetArray, lineVertexArray, dynamicLayoutVertexArray, anchorPoint, tileAnchorPoint, projectionCache, aspectRatio, getElevationForPlacement).notEnoughRoom) {
            hideGlyphs(symbol.numGlyphs, dynamicLayoutVertexArray);
        }
    }
    if (isText) {
        bucket.text.dynamicLayoutVertexBuffer.updateData(dynamicLayoutVertexArray);
    } else {
        bucket.icon.dynamicLayoutVertexBuffer.updateData(dynamicLayoutVertexArray);
    }
}
function placeFirstAndLastGlyph(fontScale, glyphOffsetArray, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol, lineVertexArray, labelPlaneMatrix, projectionCache, getElevation, returnPathInTileCoords) {
    const glyphEndIndex = symbol.glyphStartIndex + symbol.numGlyphs;
    const lineStartIndex = symbol.lineStartIndex;
    const lineEndIndex = symbol.lineStartIndex + symbol.lineLength;
    const firstGlyphOffset = glyphOffsetArray.getoffsetX(symbol.glyphStartIndex);
    const lastGlyphOffset = glyphOffsetArray.getoffsetX(glyphEndIndex - 1);
    const firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment, lineStartIndex, lineEndIndex, lineVertexArray, labelPlaneMatrix, projectionCache, getElevation, returnPathInTileCoords, true);
    if (!firstPlacedGlyph)
        return null;
    const lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment, lineStartIndex, lineEndIndex, lineVertexArray, labelPlaneMatrix, projectionCache, getElevation, returnPathInTileCoords, true);
    if (!lastPlacedGlyph)
        return null;
    return {
        first: firstPlacedGlyph,
        last: lastPlacedGlyph
    };
}
function requiresOrientationChange(writingMode, firstPoint, lastPoint, aspectRatio) {
    if (writingMode === ref_properties.WritingMode.horizontal) {
        const rise = Math.abs(lastPoint.y - firstPoint.y);
        const run = Math.abs(lastPoint.x - firstPoint.x) * aspectRatio;
        if (rise > run) {
            return { useVertical: true };
        }
    }
    if (writingMode === ref_properties.WritingMode.vertical ? firstPoint.y < lastPoint.y : firstPoint.x > lastPoint.x) {
        return { needsFlipping: true };
    }
    return null;
}
function placeGlyphsAlongLine(symbol, fontSize, flip, keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, glyphOffsetArray, lineVertexArray, dynamicLayoutVertexArray, anchorPoint, tileAnchorPoint, projectionCache, aspectRatio, getElevation) {
    const fontScale = fontSize / 24;
    const lineOffsetX = symbol.lineOffsetX * fontScale;
    const lineOffsetY = symbol.lineOffsetY * fontScale;
    let placedGlyphs;
    if (symbol.numGlyphs > 1) {
        const glyphEndIndex = symbol.glyphStartIndex + symbol.numGlyphs;
        const lineStartIndex = symbol.lineStartIndex;
        const lineEndIndex = symbol.lineStartIndex + symbol.lineLength;
        const firstAndLastGlyph = placeFirstAndLastGlyph(fontScale, glyphOffsetArray, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol, lineVertexArray, labelPlaneMatrix, projectionCache, getElevation);
        if (!firstAndLastGlyph) {
            return { notEnoughRoom: true };
        }
        const firstPoint = project(firstAndLastGlyph.first.point, glCoordMatrix).point;
        const lastPoint = project(firstAndLastGlyph.last.point, glCoordMatrix).point;
        if (keepUpright && !flip) {
            const orientationChange = requiresOrientationChange(symbol.writingMode, firstPoint, lastPoint, aspectRatio);
            if (orientationChange) {
                return orientationChange;
            }
        }
        placedGlyphs = [firstAndLastGlyph.first];
        for (let glyphIndex = symbol.glyphStartIndex + 1; glyphIndex < glyphEndIndex - 1; glyphIndex++) {
            placedGlyphs.push(placeGlyphAlongLine(fontScale * glyphOffsetArray.getoffsetX(glyphIndex), lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment, lineStartIndex, lineEndIndex, lineVertexArray, labelPlaneMatrix, projectionCache, getElevation));
        }
        placedGlyphs.push(firstAndLastGlyph.last);
    } else {
        if (keepUpright && !flip) {
            const a = project(tileAnchorPoint, posMatrix).point;
            const tileVertexIndex = symbol.lineStartIndex + symbol.segment + 1;
            const tileSegmentEnd = new ref_properties.pointGeometry(lineVertexArray.getx(tileVertexIndex), lineVertexArray.gety(tileVertexIndex));
            const projectedVertex = project(tileSegmentEnd, posMatrix);
            const b = projectedVertex.signedDistanceFromCamera > 0 ? projectedVertex.point : projectTruncatedLineSegment(tileAnchorPoint, tileSegmentEnd, a, 1, posMatrix);
            const orientationChange = requiresOrientationChange(symbol.writingMode, a, b, aspectRatio);
            if (orientationChange) {
                return orientationChange;
            }
        }
        const singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetArray.getoffsetX(symbol.glyphStartIndex), lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment, symbol.lineStartIndex, symbol.lineStartIndex + symbol.lineLength, lineVertexArray, labelPlaneMatrix, projectionCache, getElevation);
        if (!singleGlyph)
            return { notEnoughRoom: true };
        placedGlyphs = [singleGlyph];
    }
    for (const glyph of placedGlyphs) {
        ref_properties.addDynamicAttributes(dynamicLayoutVertexArray, glyph.point, glyph.angle);
    }
    return {};
}
function projectTruncatedLineSegment(previousTilePoint, currentTilePoint, previousProjectedPoint, minimumLength, projectionMatrix, getElevation) {
    const unitVertex = previousTilePoint.add(previousTilePoint.sub(currentTilePoint)._unit());
    const projectedUnitVertex = project(unitVertex, projectionMatrix, getElevation ? getElevation(unitVertex) : 0).point;
    const projectedUnitSegment = previousProjectedPoint.sub(projectedUnitVertex);
    return previousProjectedPoint.add(projectedUnitSegment._mult(minimumLength / projectedUnitSegment.mag()));
}
function interpolate(p1, p2, a) {
    const b = 1 - a;
    return new ref_properties.pointGeometry(p1.x * b + p2.x * a, p1.y * b + p2.y * a);
}
function placeGlyphAlongLine(offsetX, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, anchorSegment, lineStartIndex, lineEndIndex, lineVertexArray, labelPlaneMatrix, projectionCache, getElevation, returnPathInTileCoords, endGlyph) {
    const combinedOffsetX = flip ? offsetX - lineOffsetX : offsetX + lineOffsetX;
    let dir = combinedOffsetX > 0 ? 1 : -1;
    let angle = 0;
    if (flip) {
        dir *= -1;
        angle = Math.PI;
    }
    if (dir < 0)
        angle += Math.PI;
    let currentIndex = dir > 0 ? lineStartIndex + anchorSegment : lineStartIndex + anchorSegment + 1;
    let current = anchorPoint;
    let prev = anchorPoint;
    let distanceToPrev = 0;
    let currentSegmentDistance = 0;
    const absOffsetX = Math.abs(combinedOffsetX);
    const pathVertices = [];
    const tilePath = [];
    let currentVertex = tileAnchorPoint;
    const previousTilePoint = () => {
        const previousLineVertexIndex = currentIndex - dir;
        return distanceToPrev === 0 ? tileAnchorPoint : new ref_properties.pointGeometry(lineVertexArray.getx(previousLineVertexIndex), lineVertexArray.gety(previousLineVertexIndex));
    };
    const getTruncatedLineSegment = () => {
        return projectTruncatedLineSegment(previousTilePoint(), currentVertex, prev, absOffsetX - distanceToPrev + 1, labelPlaneMatrix, getElevation);
    };
    while (distanceToPrev + currentSegmentDistance <= absOffsetX) {
        currentIndex += dir;
        if (currentIndex < lineStartIndex || currentIndex >= lineEndIndex)
            return null;
        prev = current;
        pathVertices.push(current);
        if (returnPathInTileCoords)
            tilePath.push(currentVertex || previousTilePoint());
        current = projectionCache[currentIndex];
        if (current === undefined) {
            currentVertex = new ref_properties.pointGeometry(lineVertexArray.getx(currentIndex), lineVertexArray.gety(currentIndex));
            const projection = project(currentVertex, labelPlaneMatrix, getElevation ? getElevation(currentVertex) : 0);
            if (projection.signedDistanceFromCamera > 0) {
                current = projectionCache[currentIndex] = projection.point;
            } else {
                current = getTruncatedLineSegment();
            }
        } else {
            currentVertex = null;
        }
        distanceToPrev += currentSegmentDistance;
        currentSegmentDistance = prev.dist(current);
    }
    if (endGlyph && getElevation) {
        currentVertex = currentVertex || new ref_properties.pointGeometry(lineVertexArray.getx(currentIndex), lineVertexArray.gety(currentIndex));
        projectionCache[currentIndex] = current = projectionCache[currentIndex] === undefined ? current : getTruncatedLineSegment();
        currentSegmentDistance = prev.dist(current);
    }
    const segmentInterpolationT = (absOffsetX - distanceToPrev) / currentSegmentDistance;
    const prevToCurrent = current.sub(prev);
    const p = prevToCurrent.mult(segmentInterpolationT)._add(prev);
    if (lineOffsetY)
        p._add(prevToCurrent._unit()._perp()._mult(lineOffsetY * dir));
    const segmentAngle = angle + Math.atan2(current.y - prev.y, current.x - prev.x);
    pathVertices.push(p);
    if (returnPathInTileCoords) {
        currentVertex = currentVertex || new ref_properties.pointGeometry(lineVertexArray.getx(currentIndex), lineVertexArray.gety(currentIndex));
        const prevVertex = tilePath.length > 0 ? tilePath[tilePath.length - 1] : currentVertex;
        tilePath.push(interpolate(prevVertex, currentVertex, segmentInterpolationT));
    }
    return {
        point: p,
        angle: segmentAngle,
        path: pathVertices,
        tilePath
    };
}
const hiddenGlyphAttributes = new Float32Array([
    -Infinity,
    -Infinity,
    0,
    -Infinity,
    -Infinity,
    0,
    -Infinity,
    -Infinity,
    0,
    -Infinity,
    -Infinity,
    0
]);
function hideGlyphs(num, dynamicLayoutVertexArray) {
    for (let i = 0; i < num; i++) {
        const offset = dynamicLayoutVertexArray.length;
        dynamicLayoutVertexArray.resize(offset + 4);
        dynamicLayoutVertexArray.float32.set(hiddenGlyphAttributes, offset * 3);
    }
}
function xyTransformMat4(out, a, m) {
    const x = a[0], y = a[1];
    out[0] = m[0] * x + m[4] * y + m[12];
    out[1] = m[1] * x + m[5] * y + m[13];
    out[3] = m[3] * x + m[7] * y + m[15];
    return out;
}

const viewportPadding = 100;
class CollisionIndex {
    constructor(transform, fogState, grid = new GridIndex(transform.width + 2 * viewportPadding, transform.height + 2 * viewportPadding, 25), ignoredGrid = new GridIndex(transform.width + 2 * viewportPadding, transform.height + 2 * viewportPadding, 25)) {
        this.transform = transform;
        this.grid = grid;
        this.ignoredGrid = ignoredGrid;
        this.pitchfactor = Math.cos(transform._pitch) * transform.cameraToCenterDistance;
        this.screenRightBoundary = transform.width + viewportPadding;
        this.screenBottomBoundary = transform.height + viewportPadding;
        this.gridRightBoundary = transform.width + 2 * viewportPadding;
        this.gridBottomBoundary = transform.height + 2 * viewportPadding;
        this.fogState = fogState;
    }
    placeCollisionBox(scale, collisionBox, shift, allowOverlap, textPixelRatio, posMatrix, collisionGroupPredicate) {
        const projectedPoint = this.projectAndGetPerspectiveRatio(posMatrix, collisionBox.anchorPointX, collisionBox.anchorPointY, collisionBox.elevation, collisionBox.tileID);
        const tileToViewport = textPixelRatio * projectedPoint.perspectiveRatio;
        const tlX = (collisionBox.x1 * scale + shift.x - collisionBox.padding) * tileToViewport + projectedPoint.point.x;
        const tlY = (collisionBox.y1 * scale + shift.y - collisionBox.padding) * tileToViewport + projectedPoint.point.y;
        const brX = (collisionBox.x2 * scale + shift.x + collisionBox.padding) * tileToViewport + projectedPoint.point.x;
        const brY = (collisionBox.y2 * scale + shift.y + collisionBox.padding) * tileToViewport + projectedPoint.point.y;
        const minPerspectiveRatio = 0.55;
        const isClipped = projectedPoint.perspectiveRatio <= minPerspectiveRatio || projectedPoint.aboveHorizon;
        if (!this.isInsideGrid(tlX, tlY, brX, brY) || !allowOverlap && this.grid.hitTest(tlX, tlY, brX, brY, collisionGroupPredicate) || isClipped) {
            return {
                box: [],
                offscreen: false
            };
        }
        return {
            box: [
                tlX,
                tlY,
                brX,
                brY
            ],
            offscreen: this.isOffscreen(tlX, tlY, brX, brY)
        };
    }
    placeCollisionCircles(allowOverlap, symbol, lineVertexArray, glyphOffsetArray, fontSize, posMatrix, labelPlaneMatrix, labelToScreenMatrix, showCollisionCircles, pitchWithMap, collisionGroupPredicate, circlePixelDiameter, textPixelPadding, tileID) {
        const placedCollisionCircles = [];
        const elevation = this.transform.elevation;
        const getElevation = elevation ? p => elevation.getAtTileOffset(tileID, p.x, p.y) : _ => 0;
        const tileUnitAnchorPoint = new ref_properties.pointGeometry(symbol.anchorX, symbol.anchorY);
        const anchorElevation = getElevation(tileUnitAnchorPoint);
        const screenAnchorPoint = this.projectAndGetPerspectiveRatio(posMatrix, tileUnitAnchorPoint.x, tileUnitAnchorPoint.y, anchorElevation, tileID);
        const {perspectiveRatio} = screenAnchorPoint;
        const labelPlaneFontSize = pitchWithMap ? fontSize / perspectiveRatio : fontSize * perspectiveRatio;
        const labelPlaneFontScale = labelPlaneFontSize / ref_properties.ONE_EM;
        const labelPlaneAnchorPoint = project(tileUnitAnchorPoint, labelPlaneMatrix, anchorElevation).point;
        const projectionCache = {};
        const lineOffsetX = symbol.lineOffsetX * labelPlaneFontScale;
        const lineOffsetY = symbol.lineOffsetY * labelPlaneFontScale;
        const firstAndLastGlyph = screenAnchorPoint.signedDistanceFromCamera > 0 ? placeFirstAndLastGlyph(labelPlaneFontScale, glyphOffsetArray, lineOffsetX, lineOffsetY, false, labelPlaneAnchorPoint, tileUnitAnchorPoint, symbol, lineVertexArray, labelPlaneMatrix, projectionCache, elevation && !pitchWithMap ? getElevation : null, pitchWithMap && !!elevation) : null;
        let collisionDetected = false;
        let inGrid = false;
        let entirelyOffscreen = true;
        if (firstAndLastGlyph && !screenAnchorPoint.aboveHorizon) {
            const radius = circlePixelDiameter * 0.5 * perspectiveRatio + textPixelPadding;
            const screenPlaneMin = new ref_properties.pointGeometry(-viewportPadding, -viewportPadding);
            const screenPlaneMax = new ref_properties.pointGeometry(this.screenRightBoundary, this.screenBottomBoundary);
            const interpolator = new PathInterpolator();
            const first = firstAndLastGlyph.first;
            const last = firstAndLastGlyph.last;
            let projectedPath = [];
            for (let i = first.path.length - 1; i >= 1; i--) {
                projectedPath.push(first.path[i]);
            }
            for (let i = 1; i < last.path.length; i++) {
                projectedPath.push(last.path[i]);
            }
            const circleDist = radius * 2.5;
            if (labelToScreenMatrix) {
                const screenSpacePath = elevation ? projectedPath.map((p, index) => {
                    const z = getElevation(index < first.path.length - 1 ? first.tilePath[first.path.length - 1 - index] : last.tilePath[index - first.path.length + 2]);
                    return project(p, labelToScreenMatrix, z);
                }) : projectedPath.map(p => project(p, labelToScreenMatrix));
                if (screenSpacePath.some(point => point.signedDistanceFromCamera <= 0)) {
                    projectedPath = [];
                } else {
                    projectedPath = screenSpacePath.map(p => p.point);
                }
            }
            let segments = [];
            if (projectedPath.length > 0) {
                const minPoint = projectedPath[0].clone();
                const maxPoint = projectedPath[0].clone();
                for (let i = 1; i < projectedPath.length; i++) {
                    minPoint.x = Math.min(minPoint.x, projectedPath[i].x);
                    minPoint.y = Math.min(minPoint.y, projectedPath[i].y);
                    maxPoint.x = Math.max(maxPoint.x, projectedPath[i].x);
                    maxPoint.y = Math.max(maxPoint.y, projectedPath[i].y);
                }
                if (minPoint.x >= screenPlaneMin.x && maxPoint.x <= screenPlaneMax.x && minPoint.y >= screenPlaneMin.y && maxPoint.y <= screenPlaneMax.y) {
                    segments = [projectedPath];
                } else if (maxPoint.x < screenPlaneMin.x || minPoint.x > screenPlaneMax.x || maxPoint.y < screenPlaneMin.y || minPoint.y > screenPlaneMax.y) {
                    segments = [];
                } else {
                    segments = ref_properties.clipLine([projectedPath], screenPlaneMin.x, screenPlaneMin.y, screenPlaneMax.x, screenPlaneMax.y);
                }
            }
            for (const seg of segments) {
                interpolator.reset(seg, radius * 0.25);
                let numCircles = 0;
                if (interpolator.length <= 0.5 * radius) {
                    numCircles = 1;
                } else {
                    numCircles = Math.ceil(interpolator.paddedLength / circleDist) + 1;
                }
                for (let i = 0; i < numCircles; i++) {
                    const t = i / Math.max(numCircles - 1, 1);
                    const circlePosition = interpolator.lerp(t);
                    const centerX = circlePosition.x + viewportPadding;
                    const centerY = circlePosition.y + viewportPadding;
                    placedCollisionCircles.push(centerX, centerY, radius, 0);
                    const x1 = centerX - radius;
                    const y1 = centerY - radius;
                    const x2 = centerX + radius;
                    const y2 = centerY + radius;
                    entirelyOffscreen = entirelyOffscreen && this.isOffscreen(x1, y1, x2, y2);
                    inGrid = inGrid || this.isInsideGrid(x1, y1, x2, y2);
                    if (!allowOverlap) {
                        if (this.grid.hitTestCircle(centerX, centerY, radius, collisionGroupPredicate)) {
                            collisionDetected = true;
                            if (!showCollisionCircles) {
                                return {
                                    circles: [],
                                    offscreen: false,
                                    collisionDetected
                                };
                            }
                        }
                    }
                }
            }
        }
        return {
            circles: !showCollisionCircles && collisionDetected || !inGrid ? [] : placedCollisionCircles,
            offscreen: entirelyOffscreen,
            collisionDetected
        };
    }
    queryRenderedSymbols(viewportQueryGeometry) {
        if (viewportQueryGeometry.length === 0 || this.grid.keysLength() === 0 && this.ignoredGrid.keysLength() === 0) {
            return {};
        }
        const query = [];
        let minX = Infinity;
        let minY = Infinity;
        let maxX = -Infinity;
        let maxY = -Infinity;
        for (const point of viewportQueryGeometry) {
            const gridPoint = new ref_properties.pointGeometry(point.x + viewportPadding, point.y + viewportPadding);
            minX = Math.min(minX, gridPoint.x);
            minY = Math.min(minY, gridPoint.y);
            maxX = Math.max(maxX, gridPoint.x);
            maxY = Math.max(maxY, gridPoint.y);
            query.push(gridPoint);
        }
        const features = this.grid.query(minX, minY, maxX, maxY).concat(this.ignoredGrid.query(minX, minY, maxX, maxY));
        const seenFeatures = {};
        const result = {};
        for (const feature of features) {
            const featureKey = feature.key;
            if (seenFeatures[featureKey.bucketInstanceId] === undefined) {
                seenFeatures[featureKey.bucketInstanceId] = {};
            }
            if (seenFeatures[featureKey.bucketInstanceId][featureKey.featureIndex]) {
                continue;
            }
            const bbox = [
                new ref_properties.pointGeometry(feature.x1, feature.y1),
                new ref_properties.pointGeometry(feature.x2, feature.y1),
                new ref_properties.pointGeometry(feature.x2, feature.y2),
                new ref_properties.pointGeometry(feature.x1, feature.y2)
            ];
            if (!ref_properties.polygonIntersectsPolygon(query, bbox)) {
                continue;
            }
            seenFeatures[featureKey.bucketInstanceId][featureKey.featureIndex] = true;
            if (result[featureKey.bucketInstanceId] === undefined) {
                result[featureKey.bucketInstanceId] = [];
            }
            result[featureKey.bucketInstanceId].push(featureKey.featureIndex);
        }
        return result;
    }
    insertCollisionBox(collisionBox, ignorePlacement, bucketInstanceId, featureIndex, collisionGroupID) {
        const grid = ignorePlacement ? this.ignoredGrid : this.grid;
        const key = {
            bucketInstanceId,
            featureIndex,
            collisionGroupID
        };
        grid.insert(key, collisionBox[0], collisionBox[1], collisionBox[2], collisionBox[3]);
    }
    insertCollisionCircles(collisionCircles, ignorePlacement, bucketInstanceId, featureIndex, collisionGroupID) {
        const grid = ignorePlacement ? this.ignoredGrid : this.grid;
        const key = {
            bucketInstanceId,
            featureIndex,
            collisionGroupID
        };
        for (let k = 0; k < collisionCircles.length; k += 4) {
            grid.insertCircle(key, collisionCircles[k], collisionCircles[k + 1], collisionCircles[k + 2]);
        }
    }
    projectAndGetPerspectiveRatio(posMatrix, x, y, elevation, tileID) {
        const p = [
            x,
            y,
            elevation || 0,
            1
        ];
        let aboveHorizon = false;
        if (elevation || this.transform.pitch > 0) {
            ref_properties.transformMat4(p, p, posMatrix);
            let behindFog = false;
            if (this.fogState && tileID) {
                const fogOpacity = getFogOpacityAtTileCoord(this.fogState, x, y, elevation || 0, tileID.toUnwrapped(), this.transform);
                behindFog = fogOpacity > FOG_SYMBOL_CLIPPING_THRESHOLD;
            }
            aboveHorizon = p[2] > p[3] || behindFog;
        } else {
            xyTransformMat4(p, p, posMatrix);
        }
        const a = new ref_properties.pointGeometry((p[0] / p[3] + 1) / 2 * this.transform.width + viewportPadding, (-p[1] / p[3] + 1) / 2 * this.transform.height + viewportPadding);
        return {
            point: a,
            perspectiveRatio: Math.min(0.5 + 0.5 * (this.transform.cameraToCenterDistance / p[3]), 1.5),
            signedDistanceFromCamera: p[3],
            aboveHorizon
        };
    }
    isOffscreen(x1, y1, x2, y2) {
        return x2 < viewportPadding || x1 >= this.screenRightBoundary || y2 < viewportPadding || y1 > this.screenBottomBoundary;
    }
    isInsideGrid(x1, y1, x2, y2) {
        return x2 >= 0 && x1 < this.gridRightBoundary && y2 >= 0 && y1 < this.gridBottomBoundary;
    }
    getViewportMatrix() {
        const m = ref_properties.identity([]);
        ref_properties.translate(m, m, [
            -viewportPadding,
            -viewportPadding,
            0
        ]);
        return m;
    }
}

class OpacityState {
    constructor(prevState, increment, placed, skipFade) {
        if (prevState) {
            this.opacity = Math.max(0, Math.min(1, prevState.opacity + (prevState.placed ? increment : -increment)));
        } else {
            this.opacity = skipFade && placed ? 1 : 0;
        }
        this.placed = placed;
    }
    isHidden() {
        return this.opacity === 0 && !this.placed;
    }
}
class JointOpacityState {
    constructor(prevState, increment, placedText, placedIcon, skipFade) {
        this.text = new OpacityState(prevState ? prevState.text : null, increment, placedText, skipFade);
        this.icon = new OpacityState(prevState ? prevState.icon : null, increment, placedIcon, skipFade);
    }
    isHidden() {
        return this.text.isHidden() && this.icon.isHidden();
    }
}
class JointPlacement {
    constructor(text, icon, skipFade) {
        this.text = text;
        this.icon = icon;
        this.skipFade = skipFade;
    }
}
class CollisionCircleArray {
    constructor() {
        this.invProjMatrix = ref_properties.create$1();
        this.viewportMatrix = ref_properties.create$1();
        this.circles = [];
    }
}
class RetainedQueryData {
    constructor(bucketInstanceId, featureIndex, sourceLayerIndex, bucketIndex, tileID) {
        this.bucketInstanceId = bucketInstanceId;
        this.featureIndex = featureIndex;
        this.sourceLayerIndex = sourceLayerIndex;
        this.bucketIndex = bucketIndex;
        this.tileID = tileID;
    }
}
class CollisionGroups {
    constructor(crossSourceCollisions) {
        this.crossSourceCollisions = crossSourceCollisions;
        this.maxGroupID = 0;
        this.collisionGroups = {};
    }
    get(sourceID) {
        if (!this.crossSourceCollisions) {
            if (!this.collisionGroups[sourceID]) {
                const nextGroupID = ++this.maxGroupID;
                this.collisionGroups[sourceID] = {
                    ID: nextGroupID,
                    predicate: key => {
                        return key.collisionGroupID === nextGroupID;
                    }
                };
            }
            return this.collisionGroups[sourceID];
        } else {
            return {
                ID: 0,
                predicate: null
            };
        }
    }
}
function calculateVariableLayoutShift(anchor, width, height, textOffset, textBoxScale) {
    const {horizontalAlign, verticalAlign} = ref_properties.getAnchorAlignment(anchor);
    const shiftX = -(horizontalAlign - 0.5) * width;
    const shiftY = -(verticalAlign - 0.5) * height;
    const offset = ref_properties.evaluateVariableOffset(anchor, textOffset);
    return new ref_properties.pointGeometry(shiftX + offset[0] * textBoxScale, shiftY + offset[1] * textBoxScale);
}
function offsetShift(shiftX, shiftY, rotateWithMap, pitchWithMap, angle) {
    const shift = new ref_properties.pointGeometry(shiftX, shiftY);
    if (rotateWithMap) {
        shift._rotate(pitchWithMap ? angle : -angle);
    }
    return shift;
}
class Placement {
    constructor(transform, fadeDuration, crossSourceCollisions, prevPlacement, fogState) {
        this.transform = transform.clone();
        this.collisionIndex = new CollisionIndex(this.transform, fogState);
        this.placements = {};
        this.opacities = {};
        this.variableOffsets = {};
        this.stale = false;
        this.commitTime = 0;
        this.fadeDuration = fadeDuration;
        this.retainedQueryData = {};
        this.collisionGroups = new CollisionGroups(crossSourceCollisions);
        this.collisionCircleArrays = {};
        this.prevPlacement = prevPlacement;
        if (prevPlacement) {
            prevPlacement.prevPlacement = undefined;
        }
        this.placedOrientations = {};
    }
    getBucketParts(results, styleLayer, tile, sortAcrossTiles) {
        const symbolBucket = tile.getBucket(styleLayer);
        const bucketFeatureIndex = tile.latestFeatureIndex;
        if (!symbolBucket || !bucketFeatureIndex || styleLayer.id !== symbolBucket.layerIds[0])
            return;
        const collisionBoxArray = tile.collisionBoxArray;
        const layout = symbolBucket.layers[0].layout;
        const scale = Math.pow(2, this.transform.zoom - tile.tileID.overscaledZ);
        const textPixelRatio = tile.tileSize / ref_properties.EXTENT;
        const posMatrix = this.transform.calculateProjMatrix(tile.tileID.toUnwrapped());
        const pitchWithMap = layout.get('text-pitch-alignment') === 'map';
        const rotateWithMap = layout.get('text-rotation-alignment') === 'map';
        const pixelsToTiles = pixelsToTileUnits(tile, 1, this.transform.zoom);
        const textLabelPlaneMatrix = getLabelPlaneMatrix(posMatrix, pitchWithMap, rotateWithMap, this.transform, pixelsToTiles);
        let labelToScreenMatrix = null;
        if (pitchWithMap) {
            const glMatrix = getGlCoordMatrix(posMatrix, pitchWithMap, rotateWithMap, this.transform, pixelsToTiles);
            labelToScreenMatrix = ref_properties.multiply([], this.transform.labelPlaneMatrix, glMatrix);
        }
        this.retainedQueryData[symbolBucket.bucketInstanceId] = new RetainedQueryData(symbolBucket.bucketInstanceId, bucketFeatureIndex, symbolBucket.sourceLayerIndex, symbolBucket.index, tile.tileID);
        const parameters = {
            bucket: symbolBucket,
            layout,
            posMatrix,
            textLabelPlaneMatrix,
            labelToScreenMatrix,
            scale,
            textPixelRatio,
            holdingForFade: tile.holdingForFade(),
            collisionBoxArray,
            partiallyEvaluatedTextSize: ref_properties.evaluateSizeForZoom(symbolBucket.textSizeData, this.transform.zoom),
            partiallyEvaluatedIconSize: ref_properties.evaluateSizeForZoom(symbolBucket.iconSizeData, this.transform.zoom),
            collisionGroup: this.collisionGroups.get(symbolBucket.sourceID)
        };
        if (sortAcrossTiles) {
            for (const range of symbolBucket.sortKeyRanges) {
                const {sortKey, symbolInstanceStart, symbolInstanceEnd} = range;
                results.push({
                    sortKey,
                    symbolInstanceStart,
                    symbolInstanceEnd,
                    parameters
                });
            }
        } else {
            results.push({
                symbolInstanceStart: 0,
                symbolInstanceEnd: symbolBucket.symbolInstances.length,
                parameters
            });
        }
    }
    attemptAnchorPlacement(anchor, textBox, width, height, textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, posMatrix, collisionGroup, textAllowOverlap, symbolInstance, symbolIndex, bucket, orientation, iconBox, textSize, iconSize) {
        const textOffset = [
            symbolInstance.textOffset0,
            symbolInstance.textOffset1
        ];
        const textScale = bucket.getSymbolInstanceTextSize(textSize, symbolInstance, this.transform.zoom, symbolIndex);
        const shift = calculateVariableLayoutShift(anchor, width, height, textOffset, textBoxScale);
        const placedGlyphBoxes = this.collisionIndex.placeCollisionBox(textScale, textBox, offsetShift(shift.x, shift.y, rotateWithMap, pitchWithMap, this.transform.angle), textAllowOverlap, textPixelRatio, posMatrix, collisionGroup.predicate);
        if (iconBox) {
            const placedIconBoxes = this.collisionIndex.placeCollisionBox(bucket.getSymbolInstanceIconSize(iconSize, this.transform.zoom, symbolIndex), iconBox, offsetShift(shift.x, shift.y, rotateWithMap, pitchWithMap, this.transform.angle), textAllowOverlap, textPixelRatio, posMatrix, collisionGroup.predicate);
            if (placedIconBoxes.box.length === 0)
                return;
        }
        if (placedGlyphBoxes.box.length > 0) {
            let prevAnchor;
            if (this.prevPlacement && this.prevPlacement.variableOffsets[symbolInstance.crossTileID] && this.prevPlacement.placements[symbolInstance.crossTileID] && this.prevPlacement.placements[symbolInstance.crossTileID].text) {
                prevAnchor = this.prevPlacement.variableOffsets[symbolInstance.crossTileID].anchor;
            }
            this.variableOffsets[symbolInstance.crossTileID] = {
                textOffset,
                width,
                height,
                anchor,
                textBoxScale,
                prevAnchor
            };
            this.markUsedJustification(bucket, anchor, symbolInstance, orientation);
            if (bucket.allowVerticalPlacement) {
                this.markUsedOrientation(bucket, orientation, symbolInstance);
                this.placedOrientations[symbolInstance.crossTileID] = orientation;
            }
            return {
                shift,
                placedGlyphBoxes
            };
        }
    }
    placeLayerBucketPart(bucketPart, seenCrossTileIDs, showCollisionBoxes) {
        const {bucket, layout, posMatrix, textLabelPlaneMatrix, labelToScreenMatrix, textPixelRatio, holdingForFade, collisionBoxArray, partiallyEvaluatedTextSize, partiallyEvaluatedIconSize, collisionGroup} = bucketPart.parameters;
        const textOptional = layout.get('text-optional');
        const iconOptional = layout.get('icon-optional');
        const textAllowOverlap = layout.get('text-allow-overlap');
        const iconAllowOverlap = layout.get('icon-allow-overlap');
        const rotateWithMap = layout.get('text-rotation-alignment') === 'map';
        const pitchWithMap = layout.get('text-pitch-alignment') === 'map';
        const hasIconTextFit = layout.get('icon-text-fit') !== 'none';
        const zOrderByViewportY = layout.get('symbol-z-order') === 'viewport-y';
        const alwaysShowText = textAllowOverlap && (iconAllowOverlap || !bucket.hasIconData() || iconOptional);
        const alwaysShowIcon = iconAllowOverlap && (textAllowOverlap || !bucket.hasTextData() || textOptional);
        if (!bucket.collisionArrays && collisionBoxArray) {
            bucket.deserializeCollisionBoxes(collisionBoxArray);
        }
        if (showCollisionBoxes) {
            bucket.updateCollisionDebugBuffers(this.transform.zoom, collisionBoxArray);
        }
        const placeSymbol = (symbolInstance, symbolIndex, collisionArrays) => {
            if (seenCrossTileIDs[symbolInstance.crossTileID])
                return;
            if (holdingForFade) {
                this.placements[symbolInstance.crossTileID] = new JointPlacement(false, false, false);
                return;
            }
            let placeText = false;
            let placeIcon = false;
            let offscreen = true;
            let shift = null;
            let placed = {
                box: null,
                offscreen: null
            };
            let placedVerticalText = {
                box: null,
                offscreen: null
            };
            let placedGlyphBoxes = null;
            let placedGlyphCircles = null;
            let placedIconBoxes = null;
            let textFeatureIndex = 0;
            let verticalTextFeatureIndex = 0;
            let iconFeatureIndex = 0;
            if (collisionArrays.textFeatureIndex) {
                textFeatureIndex = collisionArrays.textFeatureIndex;
            } else if (symbolInstance.useRuntimeCollisionCircles) {
                textFeatureIndex = symbolInstance.featureIndex;
            }
            if (collisionArrays.verticalTextFeatureIndex) {
                verticalTextFeatureIndex = collisionArrays.verticalTextFeatureIndex;
            }
            const updateBoxData = box => {
                box.tileID = this.retainedQueryData[bucket.bucketInstanceId].tileID;
                if (!this.transform.elevation && !box.elevation)
                    return;
                box.elevation = this.transform.elevation ? this.transform.elevation.getAtTileOffset(this.retainedQueryData[bucket.bucketInstanceId].tileID, box.anchorPointX, box.anchorPointY) : 0;
            };
            const textBox = collisionArrays.textBox;
            if (textBox) {
                updateBoxData(textBox);
                const updatePreviousOrientationIfNotPlaced = isPlaced => {
                    let previousOrientation = ref_properties.WritingMode.horizontal;
                    if (bucket.allowVerticalPlacement && !isPlaced && this.prevPlacement) {
                        const prevPlacedOrientation = this.prevPlacement.placedOrientations[symbolInstance.crossTileID];
                        if (prevPlacedOrientation) {
                            this.placedOrientations[symbolInstance.crossTileID] = prevPlacedOrientation;
                            previousOrientation = prevPlacedOrientation;
                            this.markUsedOrientation(bucket, previousOrientation, symbolInstance);
                        }
                    }
                    return previousOrientation;
                };
                const placeTextForPlacementModes = (placeHorizontalFn, placeVerticalFn) => {
                    if (bucket.allowVerticalPlacement && symbolInstance.numVerticalGlyphVertices > 0 && collisionArrays.verticalTextBox) {
                        for (const placementMode of bucket.writingModes) {
                            if (placementMode === ref_properties.WritingMode.vertical) {
                                placed = placeVerticalFn();
                                placedVerticalText = placed;
                            } else {
                                placed = placeHorizontalFn();
                            }
                            if (placed && placed.box && placed.box.length)
                                break;
                        }
                    } else {
                        placed = placeHorizontalFn();
                    }
                };
                if (!layout.get('text-variable-anchor')) {
                    const placeBox = (collisionTextBox, orientation) => {
                        const textScale = bucket.getSymbolInstanceTextSize(partiallyEvaluatedTextSize, symbolInstance, this.transform.zoom, symbolIndex);
                        const placedFeature = this.collisionIndex.placeCollisionBox(textScale, collisionTextBox, new ref_properties.pointGeometry(0, 0), textAllowOverlap, textPixelRatio, posMatrix, collisionGroup.predicate);
                        if (placedFeature && placedFeature.box && placedFeature.box.length) {
                            this.markUsedOrientation(bucket, orientation, symbolInstance);
                            this.placedOrientations[symbolInstance.crossTileID] = orientation;
                        }
                        return placedFeature;
                    };
                    const placeHorizontal = () => {
                        return placeBox(textBox, ref_properties.WritingMode.horizontal);
                    };
                    const placeVertical = () => {
                        const verticalTextBox = collisionArrays.verticalTextBox;
                        if (bucket.allowVerticalPlacement && symbolInstance.numVerticalGlyphVertices > 0 && verticalTextBox) {
                            updateBoxData(verticalTextBox);
                            return placeBox(verticalTextBox, ref_properties.WritingMode.vertical);
                        }
                        return {
                            box: null,
                            offscreen: null
                        };
                    };
                    placeTextForPlacementModes(placeHorizontal, placeVertical);
                    updatePreviousOrientationIfNotPlaced(placed && placed.box && placed.box.length);
                } else {
                    let anchors = layout.get('text-variable-anchor');
                    if (this.prevPlacement && this.prevPlacement.variableOffsets[symbolInstance.crossTileID]) {
                        const prevOffsets = this.prevPlacement.variableOffsets[symbolInstance.crossTileID];
                        if (anchors.indexOf(prevOffsets.anchor) > 0) {
                            anchors = anchors.filter(anchor => anchor !== prevOffsets.anchor);
                            anchors.unshift(prevOffsets.anchor);
                        }
                    }
                    const placeBoxForVariableAnchors = (collisionTextBox, collisionIconBox, orientation) => {
                        const textBoxScale = symbolInstance.textBoxScale;
                        const width = (collisionTextBox.x2 - collisionTextBox.x1) * textBoxScale + 2 * collisionTextBox.padding;
                        const height = (collisionTextBox.y2 - collisionTextBox.y1) * textBoxScale + 2 * collisionTextBox.padding;
                        const variableIconBox = hasIconTextFit && !iconAllowOverlap ? collisionIconBox : null;
                        if (variableIconBox)
                            updateBoxData(variableIconBox);
                        let placedBox = {
                            box: [],
                            offscreen: false
                        };
                        const placementAttempts = textAllowOverlap ? anchors.length * 2 : anchors.length;
                        for (let i = 0; i < placementAttempts; ++i) {
                            const anchor = anchors[i % anchors.length];
                            const allowOverlap = i >= anchors.length;
                            const result = this.attemptAnchorPlacement(anchor, collisionTextBox, width, height, textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, posMatrix, collisionGroup, allowOverlap, symbolInstance, symbolIndex, bucket, orientation, variableIconBox, partiallyEvaluatedTextSize, partiallyEvaluatedIconSize);
                            if (result) {
                                placedBox = result.placedGlyphBoxes;
                                if (placedBox && placedBox.box && placedBox.box.length) {
                                    placeText = true;
                                    shift = result.shift;
                                    break;
                                }
                            }
                        }
                        return placedBox;
                    };
                    const placeHorizontal = () => {
                        return placeBoxForVariableAnchors(textBox, collisionArrays.iconBox, ref_properties.WritingMode.horizontal);
                    };
                    const placeVertical = () => {
                        const verticalTextBox = collisionArrays.verticalTextBox;
                        if (verticalTextBox)
                            updateBoxData(verticalTextBox);
                        const wasPlaced = placed && placed.box && placed.box.length;
                        if (bucket.allowVerticalPlacement && !wasPlaced && symbolInstance.numVerticalGlyphVertices > 0 && verticalTextBox) {
                            return placeBoxForVariableAnchors(verticalTextBox, collisionArrays.verticalIconBox, ref_properties.WritingMode.vertical);
                        }
                        return {
                            box: null,
                            offscreen: null
                        };
                    };
                    placeTextForPlacementModes(placeHorizontal, placeVertical);
                    if (placed) {
                        placeText = placed.box;
                        offscreen = placed.offscreen;
                    }
                    const prevOrientation = updatePreviousOrientationIfNotPlaced(placed && placed.box);
                    if (!placeText && this.prevPlacement) {
                        const prevOffset = this.prevPlacement.variableOffsets[symbolInstance.crossTileID];
                        if (prevOffset) {
                            this.variableOffsets[symbolInstance.crossTileID] = prevOffset;
                            this.markUsedJustification(bucket, prevOffset.anchor, symbolInstance, prevOrientation);
                        }
                    }
                }
            }
            placedGlyphBoxes = placed;
            placeText = placedGlyphBoxes && placedGlyphBoxes.box && placedGlyphBoxes.box.length > 0;
            offscreen = placedGlyphBoxes && placedGlyphBoxes.offscreen;
            if (symbolInstance.useRuntimeCollisionCircles) {
                const placedSymbol = bucket.text.placedSymbolArray.get(symbolInstance.centerJustifiedTextSymbolIndex);
                const fontSize = ref_properties.evaluateSizeForFeature(bucket.textSizeData, partiallyEvaluatedTextSize, placedSymbol);
                const textPixelPadding = layout.get('text-padding');
                const circlePixelDiameter = symbolInstance.collisionCircleDiameter;
                placedGlyphCircles = this.collisionIndex.placeCollisionCircles(textAllowOverlap, placedSymbol, bucket.lineVertexArray, bucket.glyphOffsetArray, fontSize, posMatrix, textLabelPlaneMatrix, labelToScreenMatrix, showCollisionBoxes, pitchWithMap, collisionGroup.predicate, circlePixelDiameter, textPixelPadding, this.retainedQueryData[bucket.bucketInstanceId].tileID);
                placeText = textAllowOverlap || placedGlyphCircles.circles.length > 0 && !placedGlyphCircles.collisionDetected;
                offscreen = offscreen && placedGlyphCircles.offscreen;
            }
            if (collisionArrays.iconFeatureIndex) {
                iconFeatureIndex = collisionArrays.iconFeatureIndex;
            }
            if (collisionArrays.iconBox) {
                const placeIconFeature = iconBox => {
                    updateBoxData(iconBox);
                    const shiftPoint = hasIconTextFit && shift ? offsetShift(shift.x, shift.y, rotateWithMap, pitchWithMap, this.transform.angle) : new ref_properties.pointGeometry(0, 0);
                    const iconScale = bucket.getSymbolInstanceIconSize(partiallyEvaluatedIconSize, this.transform.zoom, symbolIndex);
                    return this.collisionIndex.placeCollisionBox(iconScale, iconBox, shiftPoint, iconAllowOverlap, textPixelRatio, posMatrix, collisionGroup.predicate);
                };
                if (placedVerticalText && placedVerticalText.box && placedVerticalText.box.length && collisionArrays.verticalIconBox) {
                    placedIconBoxes = placeIconFeature(collisionArrays.verticalIconBox);
                    placeIcon = placedIconBoxes.box.length > 0;
                } else {
                    placedIconBoxes = placeIconFeature(collisionArrays.iconBox);
                    placeIcon = placedIconBoxes.box.length > 0;
                }
                offscreen = offscreen && placedIconBoxes.offscreen;
            }
            const iconWithoutText = textOptional || symbolInstance.numHorizontalGlyphVertices === 0 && symbolInstance.numVerticalGlyphVertices === 0;
            const textWithoutIcon = iconOptional || symbolInstance.numIconVertices === 0;
            if (!iconWithoutText && !textWithoutIcon) {
                placeIcon = placeText = placeIcon && placeText;
            } else if (!textWithoutIcon) {
                placeText = placeIcon && placeText;
            } else if (!iconWithoutText) {
                placeIcon = placeIcon && placeText;
            }
            if (placeText && placedGlyphBoxes && placedGlyphBoxes.box) {
                if (placedVerticalText && placedVerticalText.box && verticalTextFeatureIndex) {
                    this.collisionIndex.insertCollisionBox(placedGlyphBoxes.box, layout.get('text-ignore-placement'), bucket.bucketInstanceId, verticalTextFeatureIndex, collisionGroup.ID);
                } else {
                    this.collisionIndex.insertCollisionBox(placedGlyphBoxes.box, layout.get('text-ignore-placement'), bucket.bucketInstanceId, textFeatureIndex, collisionGroup.ID);
                }
            }
            if (placeIcon && placedIconBoxes) {
                this.collisionIndex.insertCollisionBox(placedIconBoxes.box, layout.get('icon-ignore-placement'), bucket.bucketInstanceId, iconFeatureIndex, collisionGroup.ID);
            }
            if (placedGlyphCircles) {
                if (placeText) {
                    this.collisionIndex.insertCollisionCircles(placedGlyphCircles.circles, layout.get('text-ignore-placement'), bucket.bucketInstanceId, textFeatureIndex, collisionGroup.ID);
                }
                if (showCollisionBoxes) {
                    const id = bucket.bucketInstanceId;
                    let circleArray = this.collisionCircleArrays[id];
                    if (circleArray === undefined)
                        circleArray = this.collisionCircleArrays[id] = new CollisionCircleArray();
                    for (let i = 0; i < placedGlyphCircles.circles.length; i += 4) {
                        circleArray.circles.push(placedGlyphCircles.circles[i + 0]);
                        circleArray.circles.push(placedGlyphCircles.circles[i + 1]);
                        circleArray.circles.push(placedGlyphCircles.circles[i + 2]);
                        circleArray.circles.push(placedGlyphCircles.collisionDetected ? 1 : 0);
                    }
                }
            }
            this.placements[symbolInstance.crossTileID] = new JointPlacement(placeText || alwaysShowText, placeIcon || alwaysShowIcon, offscreen || bucket.justReloaded);
            seenCrossTileIDs[symbolInstance.crossTileID] = true;
        };
        if (zOrderByViewportY) {
            const symbolIndexes = bucket.getSortedSymbolIndexes(this.transform.angle);
            for (let i = symbolIndexes.length - 1; i >= 0; --i) {
                const symbolIndex = symbolIndexes[i];
                placeSymbol(bucket.symbolInstances.get(symbolIndex), symbolIndex, bucket.collisionArrays[symbolIndex]);
            }
        } else {
            for (let i = bucketPart.symbolInstanceStart; i < bucketPart.symbolInstanceEnd; i++) {
                placeSymbol(bucket.symbolInstances.get(i), i, bucket.collisionArrays[i]);
            }
        }
        if (showCollisionBoxes && bucket.bucketInstanceId in this.collisionCircleArrays) {
            const circleArray = this.collisionCircleArrays[bucket.bucketInstanceId];
            ref_properties.invert(circleArray.invProjMatrix, posMatrix);
            circleArray.viewportMatrix = this.collisionIndex.getViewportMatrix();
        }
        bucket.justReloaded = false;
    }
    markUsedJustification(bucket, placedAnchor, symbolInstance, orientation) {
        const justifications = {
            'left': symbolInstance.leftJustifiedTextSymbolIndex,
            'center': symbolInstance.centerJustifiedTextSymbolIndex,
            'right': symbolInstance.rightJustifiedTextSymbolIndex
        };
        let autoIndex;
        if (orientation === ref_properties.WritingMode.vertical) {
            autoIndex = symbolInstance.verticalPlacedTextSymbolIndex;
        } else {
            autoIndex = justifications[ref_properties.getAnchorJustification(placedAnchor)];
        }
        const indexes = [
            symbolInstance.leftJustifiedTextSymbolIndex,
            symbolInstance.centerJustifiedTextSymbolIndex,
            symbolInstance.rightJustifiedTextSymbolIndex,
            symbolInstance.verticalPlacedTextSymbolIndex
        ];
        for (const index of indexes) {
            if (index >= 0) {
                if (autoIndex >= 0 && index !== autoIndex) {
                    bucket.text.placedSymbolArray.get(index).crossTileID = 0;
                } else {
                    bucket.text.placedSymbolArray.get(index).crossTileID = symbolInstance.crossTileID;
                }
            }
        }
    }
    markUsedOrientation(bucket, orientation, symbolInstance) {
        const horizontal = orientation === ref_properties.WritingMode.horizontal || orientation === ref_properties.WritingMode.horizontalOnly ? orientation : 0;
        const vertical = orientation === ref_properties.WritingMode.vertical ? orientation : 0;
        const horizontalIndexes = [
            symbolInstance.leftJustifiedTextSymbolIndex,
            symbolInstance.centerJustifiedTextSymbolIndex,
            symbolInstance.rightJustifiedTextSymbolIndex
        ];
        for (const index of horizontalIndexes) {
            bucket.text.placedSymbolArray.get(index).placedOrientation = horizontal;
        }
        if (symbolInstance.verticalPlacedTextSymbolIndex) {
            bucket.text.placedSymbolArray.get(symbolInstance.verticalPlacedTextSymbolIndex).placedOrientation = vertical;
        }
    }
    commit(now) {
        this.commitTime = now;
        this.zoomAtLastRecencyCheck = this.transform.zoom;
        const prevPlacement = this.prevPlacement;
        let placementChanged = false;
        this.prevZoomAdjustment = prevPlacement ? prevPlacement.zoomAdjustment(this.transform.zoom) : 0;
        const increment = prevPlacement ? prevPlacement.symbolFadeChange(now) : 1;
        const prevOpacities = prevPlacement ? prevPlacement.opacities : {};
        const prevOffsets = prevPlacement ? prevPlacement.variableOffsets : {};
        const prevOrientations = prevPlacement ? prevPlacement.placedOrientations : {};
        for (const crossTileID in this.placements) {
            const jointPlacement = this.placements[crossTileID];
            const prevOpacity = prevOpacities[crossTileID];
            if (prevOpacity) {
                this.opacities[crossTileID] = new JointOpacityState(prevOpacity, increment, jointPlacement.text, jointPlacement.icon);
                placementChanged = placementChanged || jointPlacement.text !== prevOpacity.text.placed || jointPlacement.icon !== prevOpacity.icon.placed;
            } else {
                this.opacities[crossTileID] = new JointOpacityState(null, increment, jointPlacement.text, jointPlacement.icon, jointPlacement.skipFade);
                placementChanged = placementChanged || jointPlacement.text || jointPlacement.icon;
            }
        }
        for (const crossTileID in prevOpacities) {
            const prevOpacity = prevOpacities[crossTileID];
            if (!this.opacities[crossTileID]) {
                const jointOpacity = new JointOpacityState(prevOpacity, increment, false, false);
                if (!jointOpacity.isHidden()) {
                    this.opacities[crossTileID] = jointOpacity;
                    placementChanged = placementChanged || prevOpacity.text.placed || prevOpacity.icon.placed;
                }
            }
        }
        for (const crossTileID in prevOffsets) {
            if (!this.variableOffsets[crossTileID] && this.opacities[crossTileID] && !this.opacities[crossTileID].isHidden()) {
                this.variableOffsets[crossTileID] = prevOffsets[crossTileID];
            }
        }
        for (const crossTileID in prevOrientations) {
            if (!this.placedOrientations[crossTileID] && this.opacities[crossTileID] && !this.opacities[crossTileID].isHidden()) {
                this.placedOrientations[crossTileID] = prevOrientations[crossTileID];
            }
        }
        if (placementChanged) {
            this.lastPlacementChangeTime = now;
        } else if (typeof this.lastPlacementChangeTime !== 'number') {
            this.lastPlacementChangeTime = prevPlacement ? prevPlacement.lastPlacementChangeTime : now;
        }
    }
    updateLayerOpacities(styleLayer, tiles) {
        const seenCrossTileIDs = {};
        for (const tile of tiles) {
            const symbolBucket = tile.getBucket(styleLayer);
            if (symbolBucket && tile.latestFeatureIndex && styleLayer.id === symbolBucket.layerIds[0]) {
                this.updateBucketOpacities(symbolBucket, seenCrossTileIDs, tile.collisionBoxArray);
            }
        }
    }
    updateBucketOpacities(bucket, seenCrossTileIDs, collisionBoxArray) {
        if (bucket.hasTextData())
            bucket.text.opacityVertexArray.clear();
        if (bucket.hasIconData())
            bucket.icon.opacityVertexArray.clear();
        if (bucket.hasIconCollisionBoxData())
            bucket.iconCollisionBox.collisionVertexArray.clear();
        if (bucket.hasTextCollisionBoxData())
            bucket.textCollisionBox.collisionVertexArray.clear();
        const layout = bucket.layers[0].layout;
        const duplicateOpacityState = new JointOpacityState(null, 0, false, false, true);
        const textAllowOverlap = layout.get('text-allow-overlap');
        const iconAllowOverlap = layout.get('icon-allow-overlap');
        const variablePlacement = layout.get('text-variable-anchor');
        const rotateWithMap = layout.get('text-rotation-alignment') === 'map';
        const pitchWithMap = layout.get('text-pitch-alignment') === 'map';
        const hasIconTextFit = layout.get('icon-text-fit') !== 'none';
        const defaultOpacityState = new JointOpacityState(null, 0, textAllowOverlap && (iconAllowOverlap || !bucket.hasIconData() || layout.get('icon-optional')), iconAllowOverlap && (textAllowOverlap || !bucket.hasTextData() || layout.get('text-optional')), true);
        if (!bucket.collisionArrays && collisionBoxArray && (bucket.hasIconCollisionBoxData() || bucket.hasTextCollisionBoxData())) {
            bucket.deserializeCollisionBoxes(collisionBoxArray);
        }
        const addOpacities = (iconOrText, numVertices, opacity) => {
            for (let i = 0; i < numVertices / 4; i++) {
                iconOrText.opacityVertexArray.emplaceBack(opacity);
            }
        };
        for (let s = 0; s < bucket.symbolInstances.length; s++) {
            const symbolInstance = bucket.symbolInstances.get(s);
            const {numHorizontalGlyphVertices, numVerticalGlyphVertices, crossTileID} = symbolInstance;
            const isDuplicate = seenCrossTileIDs[crossTileID];
            let opacityState = this.opacities[crossTileID];
            if (isDuplicate) {
                opacityState = duplicateOpacityState;
            } else if (!opacityState) {
                opacityState = defaultOpacityState;
                this.opacities[crossTileID] = opacityState;
            }
            seenCrossTileIDs[crossTileID] = true;
            const hasText = numHorizontalGlyphVertices > 0 || numVerticalGlyphVertices > 0;
            const hasIcon = symbolInstance.numIconVertices > 0;
            const placedOrientation = this.placedOrientations[symbolInstance.crossTileID];
            const horizontalHidden = placedOrientation === ref_properties.WritingMode.vertical;
            const verticalHidden = placedOrientation === ref_properties.WritingMode.horizontal || placedOrientation === ref_properties.WritingMode.horizontalOnly;
            if (hasText) {
                const packedOpacity = packOpacity(opacityState.text);
                const horizontalOpacity = horizontalHidden ? PACKED_HIDDEN_OPACITY : packedOpacity;
                addOpacities(bucket.text, numHorizontalGlyphVertices, horizontalOpacity);
                const verticalOpacity = verticalHidden ? PACKED_HIDDEN_OPACITY : packedOpacity;
                addOpacities(bucket.text, numVerticalGlyphVertices, verticalOpacity);
                const symbolHidden = opacityState.text.isHidden();
                [
                    symbolInstance.rightJustifiedTextSymbolIndex,
                    symbolInstance.centerJustifiedTextSymbolIndex,
                    symbolInstance.leftJustifiedTextSymbolIndex
                ].forEach(index => {
                    if (index >= 0) {
                        bucket.text.placedSymbolArray.get(index).hidden = symbolHidden || horizontalHidden ? 1 : 0;
                    }
                });
                if (symbolInstance.verticalPlacedTextSymbolIndex >= 0) {
                    bucket.text.placedSymbolArray.get(symbolInstance.verticalPlacedTextSymbolIndex).hidden = symbolHidden || verticalHidden ? 1 : 0;
                }
                const prevOffset = this.variableOffsets[symbolInstance.crossTileID];
                if (prevOffset) {
                    this.markUsedJustification(bucket, prevOffset.anchor, symbolInstance, placedOrientation);
                }
                const prevOrientation = this.placedOrientations[symbolInstance.crossTileID];
                if (prevOrientation) {
                    this.markUsedJustification(bucket, 'left', symbolInstance, prevOrientation);
                    this.markUsedOrientation(bucket, prevOrientation, symbolInstance);
                }
            }
            if (hasIcon) {
                const packedOpacity = packOpacity(opacityState.icon);
                const useHorizontal = !(hasIconTextFit && symbolInstance.verticalPlacedIconSymbolIndex && horizontalHidden);
                if (symbolInstance.placedIconSymbolIndex >= 0) {
                    const horizontalOpacity = useHorizontal ? packedOpacity : PACKED_HIDDEN_OPACITY;
                    addOpacities(bucket.icon, symbolInstance.numIconVertices, horizontalOpacity);
                    bucket.icon.placedSymbolArray.get(symbolInstance.placedIconSymbolIndex).hidden = opacityState.icon.isHidden();
                }
                if (symbolInstance.verticalPlacedIconSymbolIndex >= 0) {
                    const verticalOpacity = !useHorizontal ? packedOpacity : PACKED_HIDDEN_OPACITY;
                    addOpacities(bucket.icon, symbolInstance.numVerticalIconVertices, verticalOpacity);
                    bucket.icon.placedSymbolArray.get(symbolInstance.verticalPlacedIconSymbolIndex).hidden = opacityState.icon.isHidden();
                }
            }
            if (bucket.hasIconCollisionBoxData() || bucket.hasTextCollisionBoxData()) {
                const collisionArrays = bucket.collisionArrays[s];
                if (collisionArrays) {
                    let shift = new ref_properties.pointGeometry(0, 0);
                    if (collisionArrays.textBox || collisionArrays.verticalTextBox) {
                        let used = true;
                        if (variablePlacement) {
                            const variableOffset = this.variableOffsets[crossTileID];
                            if (variableOffset) {
                                shift = calculateVariableLayoutShift(variableOffset.anchor, variableOffset.width, variableOffset.height, variableOffset.textOffset, variableOffset.textBoxScale);
                                if (rotateWithMap) {
                                    shift._rotate(pitchWithMap ? this.transform.angle : -this.transform.angle);
                                }
                            } else {
                                used = false;
                            }
                        }
                        if (collisionArrays.textBox) {
                            updateCollisionVertices(bucket.textCollisionBox.collisionVertexArray, opacityState.text.placed, !used || horizontalHidden, shift.x, shift.y);
                        }
                        if (collisionArrays.verticalTextBox) {
                            updateCollisionVertices(bucket.textCollisionBox.collisionVertexArray, opacityState.text.placed, !used || verticalHidden, shift.x, shift.y);
                        }
                    }
                    const verticalIconUsed = Boolean(!verticalHidden && collisionArrays.verticalIconBox);
                    if (collisionArrays.iconBox) {
                        updateCollisionVertices(bucket.iconCollisionBox.collisionVertexArray, opacityState.icon.placed, verticalIconUsed, hasIconTextFit ? shift.x : 0, hasIconTextFit ? shift.y : 0);
                    }
                    if (collisionArrays.verticalIconBox) {
                        updateCollisionVertices(bucket.iconCollisionBox.collisionVertexArray, opacityState.icon.placed, !verticalIconUsed, hasIconTextFit ? shift.x : 0, hasIconTextFit ? shift.y : 0);
                    }
                }
            }
        }
        bucket.sortFeatures(this.transform.angle);
        if (this.retainedQueryData[bucket.bucketInstanceId]) {
            this.retainedQueryData[bucket.bucketInstanceId].featureSortOrder = bucket.featureSortOrder;
        }
        if (bucket.hasTextData() && bucket.text.opacityVertexBuffer) {
            bucket.text.opacityVertexBuffer.updateData(bucket.text.opacityVertexArray);
        }
        if (bucket.hasIconData() && bucket.icon.opacityVertexBuffer) {
            bucket.icon.opacityVertexBuffer.updateData(bucket.icon.opacityVertexArray);
        }
        if (bucket.hasIconCollisionBoxData() && bucket.iconCollisionBox.collisionVertexBuffer) {
            bucket.iconCollisionBox.collisionVertexBuffer.updateData(bucket.iconCollisionBox.collisionVertexArray);
        }
        if (bucket.hasTextCollisionBoxData() && bucket.textCollisionBox.collisionVertexBuffer) {
            bucket.textCollisionBox.collisionVertexBuffer.updateData(bucket.textCollisionBox.collisionVertexArray);
        }
        if (bucket.bucketInstanceId in this.collisionCircleArrays) {
            const instance = this.collisionCircleArrays[bucket.bucketInstanceId];
            bucket.placementInvProjMatrix = instance.invProjMatrix;
            bucket.placementViewportMatrix = instance.viewportMatrix;
            bucket.collisionCircleArray = instance.circles;
            delete this.collisionCircleArrays[bucket.bucketInstanceId];
        }
    }
    symbolFadeChange(now) {
        return this.fadeDuration === 0 ? 1 : (now - this.commitTime) / this.fadeDuration + this.prevZoomAdjustment;
    }
    zoomAdjustment(zoom) {
        return Math.max(0, (this.transform.zoom - zoom) / 1.5);
    }
    hasTransitions(now) {
        return this.stale || now - this.lastPlacementChangeTime < this.fadeDuration;
    }
    stillRecent(now, zoom) {
        const durationAdjustment = this.zoomAtLastRecencyCheck === zoom ? 1 - this.zoomAdjustment(zoom) : 1;
        this.zoomAtLastRecencyCheck = zoom;
        return this.commitTime + this.fadeDuration * durationAdjustment > now;
    }
    setStale() {
        this.stale = true;
    }
}
function updateCollisionVertices(collisionVertexArray, placed, notUsed, shiftX, shiftY) {
    collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0);
    collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0);
    collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0);
    collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0);
}
const shift25 = Math.pow(2, 25);
const shift24 = Math.pow(2, 24);
const shift17 = Math.pow(2, 17);
const shift16 = Math.pow(2, 16);
const shift9 = Math.pow(2, 9);
const shift8 = Math.pow(2, 8);
const shift1 = Math.pow(2, 1);
function packOpacity(opacityState) {
    if (opacityState.opacity === 0 && !opacityState.placed) {
        return 0;
    } else if (opacityState.opacity === 1 && opacityState.placed) {
        return 4294967295;
    }
    const targetBit = opacityState.placed ? 1 : 0;
    const opacityBits = Math.floor(opacityState.opacity * 127);
    return opacityBits * shift25 + targetBit * shift24 + opacityBits * shift17 + targetBit * shift16 + opacityBits * shift9 + targetBit * shift8 + opacityBits * shift1 + targetBit;
}
const PACKED_HIDDEN_OPACITY = 0;

class LayerPlacement {
    constructor(styleLayer) {
        this._sortAcrossTiles = styleLayer.layout.get('symbol-z-order') !== 'viewport-y' && styleLayer.layout.get('symbol-sort-key').constantOr(1) !== undefined;
        this._currentTileIndex = 0;
        this._currentPartIndex = 0;
        this._seenCrossTileIDs = {};
        this._bucketParts = [];
    }
    continuePlacement(tiles, placement, showCollisionBoxes, styleLayer, shouldPausePlacement) {
        const bucketParts = this._bucketParts;
        while (this._currentTileIndex < tiles.length) {
            const tile = tiles[this._currentTileIndex];
            placement.getBucketParts(bucketParts, styleLayer, tile, this._sortAcrossTiles);
            this._currentTileIndex++;
            if (shouldPausePlacement()) {
                return true;
            }
        }
        if (this._sortAcrossTiles) {
            this._sortAcrossTiles = false;
            bucketParts.sort((a, b) => a.sortKey - b.sortKey);
        }
        while (this._currentPartIndex < bucketParts.length) {
            const bucketPart = bucketParts[this._currentPartIndex];
            placement.placeLayerBucketPart(bucketPart, this._seenCrossTileIDs, showCollisionBoxes);
            this._currentPartIndex++;
            if (shouldPausePlacement()) {
                return true;
            }
        }
        return false;
    }
}
class PauseablePlacement {
    constructor(transform, order, forceFullPlacement, showCollisionBoxes, fadeDuration, crossSourceCollisions, prevPlacement, fogState) {
        this.placement = new Placement(transform, fadeDuration, crossSourceCollisions, prevPlacement, fogState);
        this._currentPlacementIndex = order.length - 1;
        this._forceFullPlacement = forceFullPlacement;
        this._showCollisionBoxes = showCollisionBoxes;
        this._done = false;
    }
    isDone() {
        return this._done;
    }
    continuePlacement(order, layers, layerTiles) {
        const startTime = ref_properties.exported.now();
        const shouldPausePlacement = () => {
            const elapsedTime = ref_properties.exported.now() - startTime;
            return this._forceFullPlacement ? false : elapsedTime > 2;
        };
        while (this._currentPlacementIndex >= 0) {
            const layerId = order[this._currentPlacementIndex];
            const layer = layers[layerId];
            const placementZoom = this.placement.collisionIndex.transform.zoom;
            if (layer.type === 'symbol' && (!layer.minzoom || layer.minzoom <= placementZoom) && (!layer.maxzoom || layer.maxzoom > placementZoom)) {
                if (!this._inProgressLayer) {
                    this._inProgressLayer = new LayerPlacement(layer);
                }
                const pausePlacement = this._inProgressLayer.continuePlacement(layerTiles[layer.source], this.placement, this._showCollisionBoxes, layer, shouldPausePlacement);
                if (pausePlacement) {
                    return;
                }
                delete this._inProgressLayer;
            }
            this._currentPlacementIndex--;
        }
        this._done = true;
    }
    commit(now) {
        this.placement.commit(now);
        return this.placement;
    }
}

const roundingFactor = 512 / ref_properties.EXTENT / 2;
class TileLayerIndex {
    constructor(tileID, symbolInstances, bucketInstanceId) {
        this.tileID = tileID;
        this.indexedSymbolInstances = {};
        this.bucketInstanceId = bucketInstanceId;
        for (let i = 0; i < symbolInstances.length; i++) {
            const symbolInstance = symbolInstances.get(i);
            const key = symbolInstance.key;
            if (!this.indexedSymbolInstances[key]) {
                this.indexedSymbolInstances[key] = [];
            }
            this.indexedSymbolInstances[key].push({
                crossTileID: symbolInstance.crossTileID,
                coord: this.getScaledCoordinates(symbolInstance, tileID)
            });
        }
    }
    getScaledCoordinates(symbolInstance, childTileID) {
        const zDifference = childTileID.canonical.z - this.tileID.canonical.z;
        const scale = roundingFactor / Math.pow(2, zDifference);
        return {
            x: Math.floor((childTileID.canonical.x * ref_properties.EXTENT + symbolInstance.anchorX) * scale),
            y: Math.floor((childTileID.canonical.y * ref_properties.EXTENT + symbolInstance.anchorY) * scale)
        };
    }
    findMatches(symbolInstances, newTileID, zoomCrossTileIDs) {
        const tolerance = this.tileID.canonical.z < newTileID.canonical.z ? 1 : Math.pow(2, this.tileID.canonical.z - newTileID.canonical.z);
        for (let i = 0; i < symbolInstances.length; i++) {
            const symbolInstance = symbolInstances.get(i);
            if (symbolInstance.crossTileID) {
                continue;
            }
            const indexedInstances = this.indexedSymbolInstances[symbolInstance.key];
            if (!indexedInstances) {
                continue;
            }
            const scaledSymbolCoord = this.getScaledCoordinates(symbolInstance, newTileID);
            for (const thisTileSymbol of indexedInstances) {
                if (Math.abs(thisTileSymbol.coord.x - scaledSymbolCoord.x) <= tolerance && Math.abs(thisTileSymbol.coord.y - scaledSymbolCoord.y) <= tolerance && !zoomCrossTileIDs[thisTileSymbol.crossTileID]) {
                    zoomCrossTileIDs[thisTileSymbol.crossTileID] = true;
                    symbolInstance.crossTileID = thisTileSymbol.crossTileID;
                    break;
                }
            }
        }
    }
}
class CrossTileIDs {
    constructor() {
        this.maxCrossTileID = 0;
    }
    generate() {
        return ++this.maxCrossTileID;
    }
}
class CrossTileSymbolLayerIndex {
    constructor() {
        this.indexes = {};
        this.usedCrossTileIDs = {};
        this.lng = 0;
    }
    handleWrapJump(lng) {
        const wrapDelta = Math.round((lng - this.lng) / 360);
        if (wrapDelta !== 0) {
            for (const zoom in this.indexes) {
                const zoomIndexes = this.indexes[zoom];
                const newZoomIndex = {};
                for (const key in zoomIndexes) {
                    const index = zoomIndexes[key];
                    index.tileID = index.tileID.unwrapTo(index.tileID.wrap + wrapDelta);
                    newZoomIndex[index.tileID.key] = index;
                }
                this.indexes[zoom] = newZoomIndex;
            }
        }
        this.lng = lng;
    }
    addBucket(tileID, bucket, crossTileIDs) {
        if (this.indexes[tileID.overscaledZ] && this.indexes[tileID.overscaledZ][tileID.key]) {
            if (this.indexes[tileID.overscaledZ][tileID.key].bucketInstanceId === bucket.bucketInstanceId) {
                return false;
            } else {
                this.removeBucketCrossTileIDs(tileID.overscaledZ, this.indexes[tileID.overscaledZ][tileID.key]);
            }
        }
        for (let i = 0; i < bucket.symbolInstances.length; i++) {
            const symbolInstance = bucket.symbolInstances.get(i);
            symbolInstance.crossTileID = 0;
        }
        if (!this.usedCrossTileIDs[tileID.overscaledZ]) {
            this.usedCrossTileIDs[tileID.overscaledZ] = {};
        }
        const zoomCrossTileIDs = this.usedCrossTileIDs[tileID.overscaledZ];
        for (const zoom in this.indexes) {
            const zoomIndexes = this.indexes[zoom];
            if (Number(zoom) > tileID.overscaledZ) {
                for (const id in zoomIndexes) {
                    const childIndex = zoomIndexes[id];
                    if (childIndex.tileID.isChildOf(tileID)) {
                        childIndex.findMatches(bucket.symbolInstances, tileID, zoomCrossTileIDs);
                    }
                }
            } else {
                const parentCoord = tileID.scaledTo(Number(zoom));
                const parentIndex = zoomIndexes[parentCoord.key];
                if (parentIndex) {
                    parentIndex.findMatches(bucket.symbolInstances, tileID, zoomCrossTileIDs);
                }
            }
        }
        for (let i = 0; i < bucket.symbolInstances.length; i++) {
            const symbolInstance = bucket.symbolInstances.get(i);
            if (!symbolInstance.crossTileID) {
                symbolInstance.crossTileID = crossTileIDs.generate();
                zoomCrossTileIDs[symbolInstance.crossTileID] = true;
            }
        }
        if (this.indexes[tileID.overscaledZ] === undefined) {
            this.indexes[tileID.overscaledZ] = {};
        }
        this.indexes[tileID.overscaledZ][tileID.key] = new TileLayerIndex(tileID, bucket.symbolInstances, bucket.bucketInstanceId);
        return true;
    }
    removeBucketCrossTileIDs(zoom, removedBucket) {
        for (const key in removedBucket.indexedSymbolInstances) {
            for (const symbolInstance of removedBucket.indexedSymbolInstances[key]) {
                delete this.usedCrossTileIDs[zoom][symbolInstance.crossTileID];
            }
        }
    }
    removeStaleBuckets(currentIDs) {
        let tilesChanged = false;
        for (const z in this.indexes) {
            const zoomIndexes = this.indexes[z];
            for (const tileKey in zoomIndexes) {
                if (!currentIDs[zoomIndexes[tileKey].bucketInstanceId]) {
                    this.removeBucketCrossTileIDs(z, zoomIndexes[tileKey]);
                    delete zoomIndexes[tileKey];
                    tilesChanged = true;
                }
            }
        }
        return tilesChanged;
    }
}
class CrossTileSymbolIndex {
    constructor() {
        this.layerIndexes = {};
        this.crossTileIDs = new CrossTileIDs();
        this.maxBucketInstanceId = 0;
        this.bucketsInCurrentPlacement = {};
    }
    addLayer(styleLayer, tiles, lng) {
        let layerIndex = this.layerIndexes[styleLayer.id];
        if (layerIndex === undefined) {
            layerIndex = this.layerIndexes[styleLayer.id] = new CrossTileSymbolLayerIndex();
        }
        let symbolBucketsChanged = false;
        const currentBucketIDs = {};
        layerIndex.handleWrapJump(lng);
        for (const tile of tiles) {
            const symbolBucket = tile.getBucket(styleLayer);
            if (!symbolBucket || styleLayer.id !== symbolBucket.layerIds[0])
                continue;
            if (!symbolBucket.bucketInstanceId) {
                symbolBucket.bucketInstanceId = ++this.maxBucketInstanceId;
            }
            if (layerIndex.addBucket(tile.tileID, symbolBucket, this.crossTileIDs)) {
                symbolBucketsChanged = true;
            }
            currentBucketIDs[symbolBucket.bucketInstanceId] = true;
        }
        if (layerIndex.removeStaleBuckets(currentBucketIDs)) {
            symbolBucketsChanged = true;
        }
        return symbolBucketsChanged;
    }
    pruneUnusedLayers(usedLayers) {
        const usedLayerMap = {};
        usedLayers.forEach(usedLayer => {
            usedLayerMap[usedLayer] = true;
        });
        for (const layerId in this.layerIndexes) {
            if (!usedLayerMap[layerId]) {
                delete this.layerIndexes[layerId];
            }
        }
    }
}

const emitValidationErrors = (evented, errors) => ref_properties.emitValidationErrors(evented, errors && errors.filter(error => error.identifier !== 'source.canvas'));
const supportedDiffOperations = ref_properties.pick(operations, [
    'addLayer',
    'removeLayer',
    'setPaintProperty',
    'setLayoutProperty',
    'setFilter',
    'addSource',
    'removeSource',
    'setLayerZoomRange',
    'setLight',
    'setTransition',
    'setGeoJSONSourceData',
    'setTerrain',
    'setFog'
]);
const ignoredDiffOperations = ref_properties.pick(operations, [
    'setCenter',
    'setZoom',
    'setBearing',
    'setPitch'
]);
const empty = emptyStyle();
const drapedLayers = {
    'fill': true,
    'line': true,
    'background': true,
    'hillshade': true,
    'raster': true
};
class Style extends ref_properties.Evented {
    constructor(map, options = {}) {
        super();
        this.map = map;
        this.dispatcher = new Dispatcher(getGlobalWorkerPool(), this);
        this.imageManager = new ImageManager();
        this.imageManager.setEventedParent(this);
        this.glyphManager = new ref_properties.GlyphManager(map._requestManager, options.localFontFamily ? ref_properties.LocalGlyphMode.all : options.localIdeographFontFamily ? ref_properties.LocalGlyphMode.ideographs : ref_properties.LocalGlyphMode.none, options.localFontFamily || options.localIdeographFontFamily);
        this.lineAtlas = new ref_properties.LineAtlas(256, 512);
        this.crossTileSymbolIndex = new CrossTileSymbolIndex();
        this._layers = {};
        this._num3DLayers = 0;
        this._numSymbolLayers = 0;
        this._numCircleLayers = 0;
        this._serializedLayers = {};
        this._sourceCaches = {};
        this._otherSourceCaches = {};
        this._symbolSourceCaches = {};
        this.zoomHistory = new ref_properties.ZoomHistory();
        this._loaded = false;
        this._availableImages = [];
        this._order = [];
        this._drapedFirstOrder = [];
        this._markersNeedUpdate = false;
        this._resetUpdates();
        this.dispatcher.broadcast('setReferrer', ref_properties.getReferrer());
        const self = this;
        this._rtlTextPluginCallback = Style.registerForPluginStateChange(event => {
            const state = {
                pluginStatus: event.pluginStatus,
                pluginURL: event.pluginURL
            };
            self.dispatcher.broadcast('syncRTLPluginState', state, (err, results) => {
                ref_properties.triggerPluginCompletionEvent(err);
                if (results) {
                    const allComplete = results.every(elem => elem);
                    if (allComplete) {
                        for (const id in self._sourceCaches) {
                            const sourceCache = self._sourceCaches[id];
                            const sourceCacheType = sourceCache.getSource().type;
                            if (sourceCacheType === 'vector' || sourceCacheType === 'geojson') {
                                sourceCache.reload();
                            }
                        }
                    }
                }
            });
        });
        this.on('data', event => {
            if (event.dataType !== 'source' || event.sourceDataType !== 'metadata') {
                return;
            }
            const source = this.getSource(event.sourceId);
            if (!source || !source.vectorLayerIds) {
                return;
            }
            for (const layerId in this._layers) {
                const layer = this._layers[layerId];
                if (layer.source === source.id) {
                    this._validateLayer(layer);
                }
            }
        });
    }
    loadURL(url, options = {}) {
        this.fire(new ref_properties.Event('dataloading', { dataType: 'style' }));
        const validate = typeof options.validate === 'boolean' ? options.validate : !ref_properties.isMapboxURL(url);
        url = this.map._requestManager.normalizeStyleURL(url, options.accessToken);
        const request = this.map._requestManager.transformRequest(url, ref_properties.ResourceType.Style);
        this._request = ref_properties.getJSON(request, (error, json) => {
            this._request = null;
            if (error) {
                this.fire(new ref_properties.ErrorEvent(error));
            } else if (json) {
                this._load(json, validate);
            }
        });
    }
    loadJSON(json, options = {}) {
        this.fire(new ref_properties.Event('dataloading', { dataType: 'style' }));
        this._request = ref_properties.exported.frame(() => {
            this._request = null;
            this._load(json, options.validate !== false);
        });
    }
    loadEmpty() {
        this.fire(new ref_properties.Event('dataloading', { dataType: 'style' }));
        this._load(empty, false);
    }
    _updateLayerCount(layer, add) {
        const count = add ? 1 : -1;
        if (layer.is3D()) {
            this._num3DLayers += count;
        }
        if (layer.type === 'circle') {
            this._numCircleLayers += count;
        }
        if (layer.type === 'symbol') {
            this._numSymbolLayers += count;
        }
    }
    _load(json, validate) {
        if (validate && emitValidationErrors(this, ref_properties.validateStyle(json))) {
            return;
        }
        this._loaded = true;
        this.stylesheet = json;
        for (const id in json.sources) {
            this.addSource(id, json.sources[id], { validate: false });
        }
        this._changed = false;
        if (json.sprite) {
            this._loadSprite(json.sprite);
        } else {
            this.imageManager.setLoaded(true);
            this.dispatcher.broadcast('spriteLoaded', true);
        }
        this.glyphManager.setURL(json.glyphs);
        const layers = derefLayers(this.stylesheet.layers);
        this._order = layers.map(layer => layer.id);
        this._layers = {};
        this._serializedLayers = {};
        for (let layer of layers) {
            layer = ref_properties.createStyleLayer(layer);
            layer.setEventedParent(this, { layer: { id: layer.id } });
            this._layers[layer.id] = layer;
            this._serializedLayers[layer.id] = layer.serialize();
            this._updateLayerCount(layer, true);
        }
        this.dispatcher.broadcast('setLayers', this._serializeLayers(this._order));
        this.light = new Light(this.stylesheet.light);
        if (this.stylesheet.terrain) {
            this._createTerrain(this.stylesheet.terrain);
        }
        if (this.stylesheet.fog) {
            this._createFog(this.stylesheet.fog);
        }
        this._updateDrapeFirstLayers();
        this.fire(new ref_properties.Event('data', { dataType: 'style' }));
        this.fire(new ref_properties.Event('style.load'));
    }
    _loadSprite(url) {
        this._spriteRequest = loadSprite(url, this.map._requestManager, (err, images) => {
            this._spriteRequest = null;
            if (err) {
                this.fire(new ref_properties.ErrorEvent(err));
            } else if (images) {
                for (const id in images) {
                    this.imageManager.addImage(id, images[id]);
                }
            }
            this.imageManager.setLoaded(true);
            this._availableImages = this.imageManager.listImages();
            this.dispatcher.broadcast('setImages', this._availableImages);
            this.dispatcher.broadcast('spriteLoaded', true);
            this.fire(new ref_properties.Event('data', { dataType: 'style' }));
        });
    }
    _validateLayer(layer) {
        const source = this.getSource(layer.source);
        if (!source) {
            return;
        }
        const sourceLayer = layer.sourceLayer;
        if (!sourceLayer) {
            return;
        }
        if (source.type === 'geojson' || source.vectorLayerIds && source.vectorLayerIds.indexOf(sourceLayer) === -1) {
            this.fire(new ref_properties.ErrorEvent(new Error(`Source layer "${ sourceLayer }" ` + `does not exist on source "${ source.id }" ` + `as specified by style layer "${ layer.id }"`)));
        }
    }
    loaded() {
        if (!this._loaded)
            return false;
        if (Object.keys(this._updatedSources).length)
            return false;
        for (const id in this._sourceCaches)
            if (!this._sourceCaches[id].loaded())
                return false;
        if (!this.imageManager.isLoaded())
            return false;
        return true;
    }
    _serializeLayers(ids) {
        const serializedLayers = [];
        for (const id of ids) {
            const layer = this._layers[id];
            if (layer.type !== 'custom') {
                serializedLayers.push(layer.serialize());
            }
        }
        return serializedLayers;
    }
    hasTransitions() {
        if (this.light && this.light.hasTransition()) {
            return true;
        }
        if (this.fog && this.fog.hasTransition()) {
            return true;
        }
        for (const id in this._sourceCaches) {
            if (this._sourceCaches[id].hasTransition()) {
                return true;
            }
        }
        for (const id in this._layers) {
            if (this._layers[id].hasTransition()) {
                return true;
            }
        }
        return false;
    }
    get order() {
        if (this.map._optimizeForTerrain && this.terrain) {
            return this._drapedFirstOrder;
        }
        return this._order;
    }
    isLayerDraped(layer) {
        if (!this.terrain)
            return false;
        return drapedLayers[layer.type];
    }
    _checkLoaded() {
        if (!this._loaded) {
            throw new Error('Style is not done loading');
        }
    }
    update(parameters) {
        if (!this._loaded) {
            return;
        }
        const changed = this._changed;
        if (this._changed) {
            const updatedIds = Object.keys(this._updatedLayers);
            const removedIds = Object.keys(this._removedLayers);
            if (updatedIds.length || removedIds.length) {
                this._updateWorkerLayers(updatedIds, removedIds);
            }
            for (const id in this._updatedSources) {
                const action = this._updatedSources[id];
                if (action === 'reload') {
                    this._reloadSource(id);
                } else if (action === 'clear') {
                    this._clearSource(id);
                }
            }
            this._updateTilesForChangedImages();
            for (const id in this._updatedPaintProps) {
                this._layers[id].updateTransitions(parameters);
            }
            this.light.updateTransitions(parameters);
            if (this.fog) {
                this.fog.updateTransitions(parameters);
            }
            this._resetUpdates();
        }
        const sourcesUsedBefore = {};
        for (const sourceId in this._sourceCaches) {
            const sourceCache = this._sourceCaches[sourceId];
            sourcesUsedBefore[sourceId] = sourceCache.used;
            sourceCache.used = false;
        }
        for (const layerId of this._order) {
            const layer = this._layers[layerId];
            layer.recalculate(parameters, this._availableImages);
            if (!layer.isHidden(parameters.zoom)) {
                const sourceCache = this._getLayerSourceCache(layer);
                if (sourceCache)
                    sourceCache.used = true;
            }
            const painter = this.map.painter;
            if (painter) {
                const programIds = layer.getProgramIds();
                if (!programIds)
                    continue;
                const programConfiguration = layer.getProgramConfiguration(parameters.zoom);
                for (const programId of programIds) {
                    painter.useProgram(programId, programConfiguration);
                }
            }
        }
        for (const sourceId in sourcesUsedBefore) {
            const sourceCache = this._sourceCaches[sourceId];
            if (sourcesUsedBefore[sourceId] !== sourceCache.used) {
                sourceCache.getSource().fire(new ref_properties.Event('data', {
                    sourceDataType: 'visibility',
                    dataType: 'source',
                    sourceId: sourceCache.getSource().id
                }));
            }
        }
        this.light.recalculate(parameters);
        if (this.terrain) {
            this.terrain.recalculate(parameters);
        }
        if (this.fog) {
            this.fog.recalculate(parameters);
        }
        this.z = parameters.zoom;
        if (this._markersNeedUpdate) {
            this._updateMarkersOpacity();
            this._markersNeedUpdate = false;
        }
        if (changed) {
            this.fire(new ref_properties.Event('data', { dataType: 'style' }));
        }
    }
    _updateTilesForChangedImages() {
        const changedImages = Object.keys(this._changedImages);
        if (changedImages.length) {
            for (const name in this._sourceCaches) {
                this._sourceCaches[name].reloadTilesForDependencies([
                    'icons',
                    'patterns'
                ], changedImages);
            }
            this._changedImages = {};
        }
    }
    _updateWorkerLayers(updatedIds, removedIds) {
        this.dispatcher.broadcast('updateLayers', {
            layers: this._serializeLayers(updatedIds),
            removedIds
        });
    }
    _resetUpdates() {
        this._changed = false;
        this._updatedLayers = {};
        this._removedLayers = {};
        this._updatedSources = {};
        this._updatedPaintProps = {};
        this._changedImages = {};
    }
    setState(nextState) {
        this._checkLoaded();
        if (emitValidationErrors(this, ref_properties.validateStyle(nextState)))
            return false;
        nextState = ref_properties.clone$2(nextState);
        nextState.layers = derefLayers(nextState.layers);
        const changes = diffStyles(this.serialize(), nextState).filter(op => !(op.command in ignoredDiffOperations));
        if (changes.length === 0) {
            return false;
        }
        const unimplementedOps = changes.filter(op => !(op.command in supportedDiffOperations));
        if (unimplementedOps.length > 0) {
            throw new Error(`Unimplemented: ${ unimplementedOps.map(op => op.command).join(', ') }.`);
        }
        changes.forEach(op => {
            if (op.command === 'setTransition') {
                return;
            }
            this[op.command].apply(this, op.args);
        });
        this.stylesheet = nextState;
        return true;
    }
    addImage(id, image) {
        if (this.getImage(id)) {
            return this.fire(new ref_properties.ErrorEvent(new Error('An image with this name already exists.')));
        }
        this.imageManager.addImage(id, image);
        this._afterImageUpdated(id);
    }
    updateImage(id, image) {
        this.imageManager.updateImage(id, image);
    }
    getImage(id) {
        return this.imageManager.getImage(id);
    }
    removeImage(id) {
        if (!this.getImage(id)) {
            return this.fire(new ref_properties.ErrorEvent(new Error('No image with this name exists.')));
        }
        this.imageManager.removeImage(id);
        this._afterImageUpdated(id);
    }
    _afterImageUpdated(id) {
        this._availableImages = this.imageManager.listImages();
        this._changedImages[id] = true;
        this._changed = true;
        this.dispatcher.broadcast('setImages', this._availableImages);
        this.fire(new ref_properties.Event('data', { dataType: 'style' }));
    }
    listImages() {
        this._checkLoaded();
        return this.imageManager.listImages();
    }
    addSource(id, source, options = {}) {
        this._checkLoaded();
        if (this.getSource(id) !== undefined) {
            throw new Error('There is already a source with this ID');
        }
        if (!source.type) {
            throw new Error(`The type property must be defined, but only the following properties were given: ${ Object.keys(source).join(', ') }.`);
        }
        const builtIns = [
            'vector',
            'raster',
            'geojson',
            'video',
            'image'
        ];
        const shouldValidate = builtIns.indexOf(source.type) >= 0;
        if (shouldValidate && this._validate(ref_properties.validateStyle.source, `sources.${ id }`, source, null, options))
            return;
        if (this.map && this.map._collectResourceTiming)
            source.collectResourceTiming = true;
        const sourceInstance = create(id, source, this.dispatcher, this);
        sourceInstance.setEventedParent(this, () => ({
            isSourceLoaded: this.loaded(),
            source: sourceInstance.serialize(),
            sourceId: id
        }));
        const addSourceCache = onlySymbols => {
            const sourceCacheId = (onlySymbols ? 'symbol:' : 'other:') + id;
            const sourceCache = this._sourceCaches[sourceCacheId] = new ref_properties.SourceCache(sourceCacheId, sourceInstance, onlySymbols);
            (onlySymbols ? this._symbolSourceCaches : this._otherSourceCaches)[id] = sourceCache;
            sourceCache.style = this;
            sourceCache.onAdd(this.map);
        };
        addSourceCache(false);
        if (source.type === 'vector' || source.type === 'geojson') {
            addSourceCache(true);
        }
        if (sourceInstance.onAdd)
            sourceInstance.onAdd(this.map);
        this._changed = true;
    }
    removeSource(id) {
        this._checkLoaded();
        const source = this.getSource(id);
        if (source === undefined) {
            throw new Error('There is no source with this ID');
        }
        for (const layerId in this._layers) {
            if (this._layers[layerId].source === id) {
                return this.fire(new ref_properties.ErrorEvent(new Error(`Source "${ id }" cannot be removed while layer "${ layerId }" is using it.`)));
            }
        }
        if (this.terrain && this.terrain.get().source === id) {
            return this.fire(new ref_properties.ErrorEvent(new Error(`Source "${ id }" cannot be removed while terrain is using it.`)));
        }
        const sourceCaches = this._getSourceCaches(id);
        for (const sourceCache of sourceCaches) {
            delete this._sourceCaches[sourceCache.id];
            delete this._updatedSources[sourceCache.id];
            sourceCache.fire(new ref_properties.Event('data', {
                sourceDataType: 'metadata',
                dataType: 'source',
                sourceId: sourceCache.getSource().id
            }));
            sourceCache.setEventedParent(null);
            sourceCache.clearTiles();
        }
        delete this._otherSourceCaches[id];
        delete this._symbolSourceCaches[id];
        source.setEventedParent(null);
        if (source.onRemove) {
            source.onRemove(this.map);
        }
        this._changed = true;
    }
    setGeoJSONSourceData(id, data) {
        this._checkLoaded();
        const geojsonSource = this.getSource(id);
        geojsonSource.setData(data);
        this._changed = true;
    }
    getSource(id) {
        const sourceCache = this._getSourceCache(id);
        return sourceCache && sourceCache.getSource();
    }
    addLayer(layerObject, before, options = {}) {
        this._checkLoaded();
        const id = layerObject.id;
        if (this.getLayer(id)) {
            this.fire(new ref_properties.ErrorEvent(new Error(`Layer with id "${ id }" already exists on this map`)));
            return;
        }
        let layer;
        if (layerObject.type === 'custom') {
            if (emitValidationErrors(this, ref_properties.validateCustomStyleLayer(layerObject)))
                return;
            layer = ref_properties.createStyleLayer(layerObject);
        } else {
            if (typeof layerObject.source === 'object') {
                this.addSource(id, layerObject.source);
                layerObject = ref_properties.clone$2(layerObject);
                layerObject = ref_properties.extend(layerObject, { source: id });
            }
            if (this._validate(ref_properties.validateStyle.layer, `layers.${ id }`, layerObject, { arrayIndex: -1 }, options))
                return;
            layer = ref_properties.createStyleLayer(layerObject);
            this._validateLayer(layer);
            layer.setEventedParent(this, { layer: { id } });
            this._serializedLayers[layer.id] = layer.serialize();
            this._updateLayerCount(layer, true);
        }
        const index = before ? this._order.indexOf(before) : this._order.length;
        if (before && index === -1) {
            this.fire(new ref_properties.ErrorEvent(new Error(`Layer with id "${ before }" does not exist on this map.`)));
            return;
        }
        this._order.splice(index, 0, id);
        this._layerOrderChanged = true;
        this._layers[id] = layer;
        const sourceCache = this._getLayerSourceCache(layer);
        if (this._removedLayers[id] && layer.source && sourceCache && layer.type !== 'custom') {
            const removed = this._removedLayers[id];
            delete this._removedLayers[id];
            if (removed.type !== layer.type) {
                this._updatedSources[layer.source] = 'clear';
            } else {
                this._updatedSources[layer.source] = 'reload';
                sourceCache.pause();
            }
        }
        this._updateLayer(layer);
        if (layer.onAdd) {
            layer.onAdd(this.map);
        }
        this._updateDrapeFirstLayers();
    }
    moveLayer(id, before) {
        this._checkLoaded();
        this._changed = true;
        const layer = this._layers[id];
        if (!layer) {
            this.fire(new ref_properties.ErrorEvent(new Error(`The layer '${ id }' does not exist in the map's style and cannot be moved.`)));
            return;
        }
        if (id === before) {
            return;
        }
        const index = this._order.indexOf(id);
        this._order.splice(index, 1);
        const newIndex = before ? this._order.indexOf(before) : this._order.length;
        if (before && newIndex === -1) {
            this.fire(new ref_properties.ErrorEvent(new Error(`Layer with id "${ before }" does not exist on this map.`)));
            return;
        }
        this._order.splice(newIndex, 0, id);
        this._layerOrderChanged = true;
        this._updateDrapeFirstLayers();
    }
    removeLayer(id) {
        this._checkLoaded();
        const layer = this._layers[id];
        if (!layer) {
            this.fire(new ref_properties.ErrorEvent(new Error(`The layer '${ id }' does not exist in the map's style and cannot be removed.`)));
            return;
        }
        layer.setEventedParent(null);
        this._updateLayerCount(layer, false);
        const index = this._order.indexOf(id);
        this._order.splice(index, 1);
        this._layerOrderChanged = true;
        this._changed = true;
        this._removedLayers[id] = layer;
        delete this._layers[id];
        delete this._serializedLayers[id];
        delete this._updatedLayers[id];
        delete this._updatedPaintProps[id];
        if (layer.onRemove) {
            layer.onRemove(this.map);
        }
        this._updateDrapeFirstLayers();
    }
    getLayer(id) {
        return this._layers[id];
    }
    hasLayer(id) {
        return id in this._layers;
    }
    hasLayerType(type) {
        for (const layerId in this._layers) {
            const layer = this._layers[layerId];
            if (layer.type === type) {
                return true;
            }
        }
        return false;
    }
    setLayerZoomRange(layerId, minzoom, maxzoom) {
        this._checkLoaded();
        const layer = this.getLayer(layerId);
        if (!layer) {
            this.fire(new ref_properties.ErrorEvent(new Error(`The layer '${ layerId }' does not exist in the map's style and cannot have zoom extent.`)));
            return;
        }
        if (layer.minzoom === minzoom && layer.maxzoom === maxzoom)
            return;
        if (minzoom != null) {
            layer.minzoom = minzoom;
        }
        if (maxzoom != null) {
            layer.maxzoom = maxzoom;
        }
        this._updateLayer(layer);
    }
    setFilter(layerId, filter, options = {}) {
        this._checkLoaded();
        const layer = this.getLayer(layerId);
        if (!layer) {
            this.fire(new ref_properties.ErrorEvent(new Error(`The layer '${ layerId }' does not exist in the map's style and cannot be filtered.`)));
            return;
        }
        if (deepEqual(layer.filter, filter)) {
            return;
        }
        if (filter === null || filter === undefined) {
            layer.filter = undefined;
            this._updateLayer(layer);
            return;
        }
        if (this._validate(ref_properties.validateStyle.filter, `layers.${ layer.id }.filter`, filter, null, options)) {
            return;
        }
        layer.filter = ref_properties.clone$2(filter);
        this._updateLayer(layer);
    }
    getFilter(layer) {
        return ref_properties.clone$2(this.getLayer(layer).filter);
    }
    setLayoutProperty(layerId, name, value, options = {}) {
        this._checkLoaded();
        const layer = this.getLayer(layerId);
        if (!layer) {
            this.fire(new ref_properties.ErrorEvent(new Error(`The layer '${ layerId }' does not exist in the map's style and cannot be styled.`)));
            return;
        }
        if (deepEqual(layer.getLayoutProperty(name), value))
            return;
        layer.setLayoutProperty(name, value, options);
        this._updateLayer(layer);
    }
    getLayoutProperty(layerId, name) {
        const layer = this.getLayer(layerId);
        if (!layer) {
            this.fire(new ref_properties.ErrorEvent(new Error(`The layer '${ layerId }' does not exist in the map's style.`)));
            return;
        }
        return layer.getLayoutProperty(name);
    }
    setPaintProperty(layerId, name, value, options = {}) {
        this._checkLoaded();
        const layer = this.getLayer(layerId);
        if (!layer) {
            this.fire(new ref_properties.ErrorEvent(new Error(`The layer '${ layerId }' does not exist in the map's style and cannot be styled.`)));
            return;
        }
        if (deepEqual(layer.getPaintProperty(name), value))
            return;
        const requiresRelayout = layer.setPaintProperty(name, value, options);
        if (requiresRelayout) {
            this._updateLayer(layer);
        }
        this._changed = true;
        this._updatedPaintProps[layerId] = true;
    }
    getPaintProperty(layer, name) {
        return this.getLayer(layer).getPaintProperty(name);
    }
    setFeatureState(target, state) {
        this._checkLoaded();
        const sourceId = target.source;
        const sourceLayer = target.sourceLayer;
        const source = this.getSource(sourceId);
        if (source === undefined) {
            this.fire(new ref_properties.ErrorEvent(new Error(`The source '${ sourceId }' does not exist in the map's style.`)));
            return;
        }
        const sourceType = source.type;
        if (sourceType === 'geojson' && sourceLayer) {
            this.fire(new ref_properties.ErrorEvent(new Error(`GeoJSON sources cannot have a sourceLayer parameter.`)));
            return;
        }
        if (sourceType === 'vector' && !sourceLayer) {
            this.fire(new ref_properties.ErrorEvent(new Error(`The sourceLayer parameter must be provided for vector source types.`)));
            return;
        }
        if (target.id === undefined) {
            this.fire(new ref_properties.ErrorEvent(new Error(`The feature id parameter must be provided.`)));
        }
        const sourceCaches = this._getSourceCaches(sourceId);
        for (const sourceCache of sourceCaches) {
            sourceCache.setFeatureState(sourceLayer, target.id, state);
        }
    }
    removeFeatureState(target, key) {
        this._checkLoaded();
        const sourceId = target.source;
        const source = this.getSource(sourceId);
        if (source === undefined) {
            this.fire(new ref_properties.ErrorEvent(new Error(`The source '${ sourceId }' does not exist in the map's style.`)));
            return;
        }
        const sourceType = source.type;
        const sourceLayer = sourceType === 'vector' ? target.sourceLayer : undefined;
        if (sourceType === 'vector' && !sourceLayer) {
            this.fire(new ref_properties.ErrorEvent(new Error(`The sourceLayer parameter must be provided for vector source types.`)));
            return;
        }
        if (key && (typeof target.id !== 'string' && typeof target.id !== 'number')) {
            this.fire(new ref_properties.ErrorEvent(new Error(`A feature id is required to remove its specific state property.`)));
            return;
        }
        const sourceCaches = this._getSourceCaches(sourceId);
        for (const sourceCache of sourceCaches) {
            sourceCache.removeFeatureState(sourceLayer, target.id, key);
        }
    }
    getFeatureState(target) {
        this._checkLoaded();
        const sourceId = target.source;
        const sourceLayer = target.sourceLayer;
        const source = this.getSource(sourceId);
        if (source === undefined) {
            this.fire(new ref_properties.ErrorEvent(new Error(`The source '${ sourceId }' does not exist in the map's style.`)));
            return;
        }
        const sourceType = source.type;
        if (sourceType === 'vector' && !sourceLayer) {
            this.fire(new ref_properties.ErrorEvent(new Error(`The sourceLayer parameter must be provided for vector source types.`)));
            return;
        }
        if (target.id === undefined) {
            this.fire(new ref_properties.ErrorEvent(new Error(`The feature id parameter must be provided.`)));
        }
        const sourceCaches = this._getSourceCaches(sourceId);
        return sourceCaches[0].getFeatureState(sourceLayer, target.id);
    }
    getTransition() {
        return ref_properties.extend({
            duration: 300,
            delay: 0
        }, this.stylesheet && this.stylesheet.transition);
    }
    serialize() {
        const sources = {};
        for (const cacheId in this._sourceCaches) {
            const source = this._sourceCaches[cacheId].getSource();
            if (!sources[source.id]) {
                sources[source.id] = source.serialize();
            }
        }
        return ref_properties.filterObject({
            version: this.stylesheet.version,
            name: this.stylesheet.name,
            metadata: this.stylesheet.metadata,
            light: this.stylesheet.light,
            terrain: this.stylesheet.terrain,
            fog: this.stylesheet.fog,
            center: this.stylesheet.center,
            zoom: this.stylesheet.zoom,
            bearing: this.stylesheet.bearing,
            pitch: this.stylesheet.pitch,
            sprite: this.stylesheet.sprite,
            glyphs: this.stylesheet.glyphs,
            transition: this.stylesheet.transition,
            sources,
            layers: this._serializeLayers(this._order)
        }, value => {
            return value !== undefined;
        });
    }
    _updateLayer(layer) {
        this._updatedLayers[layer.id] = true;
        const sourceCache = this._getLayerSourceCache(layer);
        if (layer.source && !this._updatedSources[layer.source] && sourceCache && sourceCache.getSource().type !== 'raster') {
            this._updatedSources[layer.source] = 'reload';
            sourceCache.pause();
        }
        this._changed = true;
    }
    _flattenAndSortRenderedFeatures(sourceResults) {
        const isLayer3D = layerId => this._layers[layerId].type === 'fill-extrusion';
        const layerIndex = {};
        const features3D = [];
        for (let l = this._order.length - 1; l >= 0; l--) {
            const layerId = this._order[l];
            if (isLayer3D(layerId)) {
                layerIndex[layerId] = l;
                for (const sourceResult of sourceResults) {
                    const layerFeatures = sourceResult[layerId];
                    if (layerFeatures) {
                        for (const featureWrapper of layerFeatures) {
                            features3D.push(featureWrapper);
                        }
                    }
                }
            }
        }
        features3D.sort((a, b) => {
            return b.intersectionZ - a.intersectionZ;
        });
        const features = [];
        for (let l = this._order.length - 1; l >= 0; l--) {
            const layerId = this._order[l];
            if (isLayer3D(layerId)) {
                for (let i = features3D.length - 1; i >= 0; i--) {
                    const topmost3D = features3D[i].feature;
                    if (layerIndex[topmost3D.layer.id] < l)
                        break;
                    features.push(topmost3D);
                    features3D.pop();
                }
            } else {
                for (const sourceResult of sourceResults) {
                    const layerFeatures = sourceResult[layerId];
                    if (layerFeatures) {
                        for (const featureWrapper of layerFeatures) {
                            features.push(featureWrapper.feature);
                        }
                    }
                }
            }
        }
        return features;
    }
    queryRenderedFeatures(queryGeometry, params, transform) {
        if (params && params.filter) {
            this._validate(ref_properties.validateStyle.filter, 'queryRenderedFeatures.filter', params.filter, null, params);
        }
        const includedSources = {};
        if (params && params.layers) {
            if (!Array.isArray(params.layers)) {
                this.fire(new ref_properties.ErrorEvent(new Error('parameters.layers must be an Array.')));
                return [];
            }
            for (const layerId of params.layers) {
                const layer = this._layers[layerId];
                if (!layer) {
                    this.fire(new ref_properties.ErrorEvent(new Error(`The layer '${ layerId }' does not exist in the map's style and cannot be queried for features.`)));
                    return [];
                }
                includedSources[layer.source] = true;
            }
        }
        const sourceResults = [];
        params.availableImages = this._availableImages;
        const has3DLayer = params && params.layers ? params.layers.some(layerId => {
            const layer = this.getLayer(layerId);
            return layer && layer.is3D();
        }) : this.has3DLayers();
        const queryGeometryStruct = QueryGeometry.createFromScreenPoints(queryGeometry, transform);
        for (const id in this._sourceCaches) {
            const sourceId = this._sourceCaches[id].getSource().id;
            if (params.layers && !includedSources[sourceId])
                continue;
            sourceResults.push(queryRenderedFeatures(this._sourceCaches[id], this._layers, this._serializedLayers, queryGeometryStruct, params, transform, has3DLayer, !!this.map._showQueryGeometry));
        }
        if (this.placement) {
            sourceResults.push(queryRenderedSymbols(this._layers, this._serializedLayers, this._getLayerSourceCache.bind(this), queryGeometryStruct.screenGeometry, params, this.placement.collisionIndex, this.placement.retainedQueryData));
        }
        return this._flattenAndSortRenderedFeatures(sourceResults);
    }
    querySourceFeatures(sourceID, params) {
        if (params && params.filter) {
            this._validate(ref_properties.validateStyle.filter, 'querySourceFeatures.filter', params.filter, null, params);
        }
        const sourceCaches = this._getSourceCaches(sourceID);
        let results = [];
        for (const sourceCache of sourceCaches) {
            results = results.concat(querySourceFeatures(sourceCache, params));
        }
        return results;
    }
    addSourceType(name, SourceType, callback) {
        if (Style.getSourceType(name)) {
            return callback(new Error(`A source type called "${ name }" already exists.`));
        }
        Style.setSourceType(name, SourceType);
        if (!SourceType.workerSourceURL) {
            return callback(null, null);
        }
        this.dispatcher.broadcast('loadWorkerSource', {
            name,
            url: SourceType.workerSourceURL
        }, callback);
    }
    getLight() {
        return this.light.getLight();
    }
    setLight(lightOptions, options = {}) {
        this._checkLoaded();
        const light = this.light.getLight();
        let _update = false;
        for (const key in lightOptions) {
            if (!deepEqual(lightOptions[key], light[key])) {
                _update = true;
                break;
            }
        }
        if (!_update)
            return;
        const parameters = {
            now: ref_properties.exported.now(),
            transition: ref_properties.extend({
                duration: 300,
                delay: 0
            }, this.stylesheet.transition)
        };
        this.light.setLight(lightOptions, options);
        this.light.updateTransitions(parameters);
    }
    getTerrain() {
        return this.terrain ? this.terrain.get() : null;
    }
    setTerrain(terrainOptions) {
        this._checkLoaded();
        if (!terrainOptions) {
            delete this.terrain;
            delete this.stylesheet.terrain;
            this.dispatcher.broadcast('enableTerrain', false);
            this._force3DLayerUpdate();
            this._markersNeedUpdate = true;
            return;
        }
        if (typeof terrainOptions.source === 'object') {
            const id = 'terrain-dem-src';
            this.addSource(id, terrainOptions.source);
            terrainOptions = ref_properties.clone$2(terrainOptions);
            terrainOptions = ref_properties.extend(terrainOptions, { source: id });
        }
        if (this._validate(ref_properties.validateStyle.terrain, 'terrain', terrainOptions))
            return;
        if (!this.terrain) {
            this._createTerrain(terrainOptions);
        } else {
            const terrain = this.terrain;
            const currSpec = terrain.get();
            for (const key in terrainOptions) {
                if (!deepEqual(terrainOptions[key], currSpec[key])) {
                    terrain.set(terrainOptions);
                    this.stylesheet.terrain = terrainOptions;
                    const parameters = {
                        now: ref_properties.exported.now(),
                        transition: ref_properties.extend({ duration: 0 }, this.stylesheet.transition)
                    };
                    terrain.updateTransitions(parameters);
                    break;
                }
            }
        }
        this._updateDrapeFirstLayers();
        this._markersNeedUpdate = true;
    }
    _createFog(fogOptions) {
        const fog = this.fog = new Fog(fogOptions);
        this.stylesheet.fog = fogOptions;
        const parameters = {
            now: ref_properties.exported.now(),
            transition: ref_properties.extend({ duration: 0 }, this.stylesheet.transition)
        };
        fog.updateTransitions(parameters);
    }
    _updateMarkersOpacity() {
        if (this.map._markers.length === 0) {
            return;
        }
        this.map._requestDomTask(() => {
            for (const marker of this.map._markers) {
                marker._evaluateOpacity();
            }
        });
    }
    getFog() {
        return this.fog ? this.fog.get() : null;
    }
    setFog(fogOptions) {
        this._checkLoaded();
        if (!fogOptions) {
            delete this.fog;
            delete this.stylesheet.fog;
            this._markersNeedUpdate = true;
            return;
        }
        if (!this.fog) {
            this._createFog(fogOptions);
        } else {
            const fog = this.fog;
            const currSpec = fog.get();
            for (const key in fogOptions) {
                if (!deepEqual(fogOptions[key], currSpec[key])) {
                    fog.set(fogOptions);
                    this.stylesheet.fog = fogOptions;
                    const parameters = {
                        now: ref_properties.exported.now(),
                        transition: ref_properties.extend({ duration: 0 }, this.stylesheet.transition)
                    };
                    fog.updateTransitions(parameters);
                    break;
                }
            }
        }
        this._markersNeedUpdate = true;
    }
    _updateDrapeFirstLayers() {
        if (!this.map._optimizeForTerrain || !this.terrain) {
            return;
        }
        const draped = this._order.filter(id => {
            return this.isLayerDraped(this._layers[id]);
        });
        const nonDraped = this._order.filter(id => {
            return !this.isLayerDraped(this._layers[id]);
        });
        this._drapedFirstOrder = [];
        this._drapedFirstOrder.push(...draped);
        this._drapedFirstOrder.push(...nonDraped);
    }
    _createTerrain(terrainOptions) {
        const terrain = this.terrain = new Terrain$1(terrainOptions);
        this.stylesheet.terrain = terrainOptions;
        this.dispatcher.broadcast('enableTerrain', true);
        this._force3DLayerUpdate();
        const parameters = {
            now: ref_properties.exported.now(),
            transition: ref_properties.extend({ duration: 0 }, this.stylesheet.transition)
        };
        terrain.updateTransitions(parameters);
    }
    _force3DLayerUpdate() {
        for (const layerId in this._layers) {
            const layer = this._layers[layerId];
            if (layer.type === 'fill-extrusion') {
                this._updateLayer(layer);
            }
        }
    }
    _validate(validate, key, value, props, options = {}) {
        if (options && options.validate === false) {
            return false;
        }
        return emitValidationErrors(this, validate.call(ref_properties.validateStyle, ref_properties.extend({
            key,
            style: this.serialize(),
            value,
            styleSpec: ref_properties.spec
        }, props)));
    }
    _remove() {
        if (this._request) {
            this._request.cancel();
            this._request = null;
        }
        if (this._spriteRequest) {
            this._spriteRequest.cancel();
            this._spriteRequest = null;
        }
        ref_properties.evented.off('pluginStateChange', this._rtlTextPluginCallback);
        for (const layerId in this._layers) {
            const layer = this._layers[layerId];
            layer.setEventedParent(null);
        }
        for (const id in this._sourceCaches) {
            this._sourceCaches[id].clearTiles();
            this._sourceCaches[id].setEventedParent(null);
        }
        this.imageManager.setEventedParent(null);
        this.setEventedParent(null);
        this.dispatcher.remove();
    }
    _clearSource(id) {
        const sourceCaches = this._getSourceCaches(id);
        for (const sourceCache of sourceCaches) {
            sourceCache.clearTiles();
        }
    }
    _reloadSource(id) {
        const sourceCaches = this._getSourceCaches(id);
        for (const sourceCache of sourceCaches) {
            sourceCache.resume();
            sourceCache.reload();
        }
    }
    _updateSources(transform) {
        for (const id in this._sourceCaches) {
            this._sourceCaches[id].update(transform);
        }
    }
    _generateCollisionBoxes() {
        for (const id in this._sourceCaches) {
            const sourceCache = this._sourceCaches[id];
            sourceCache.resume();
            sourceCache.reload();
        }
    }
    _updatePlacement(transform, showCollisionBoxes, fadeDuration, crossSourceCollisions, forceFullPlacement = false) {
        let symbolBucketsChanged = false;
        let placementCommitted = false;
        const layerTiles = {};
        for (const layerID of this._order) {
            const styleLayer = this._layers[layerID];
            if (styleLayer.type !== 'symbol')
                continue;
            if (!layerTiles[styleLayer.source]) {
                const sourceCache = this._getLayerSourceCache(styleLayer);
                if (!sourceCache)
                    continue;
                layerTiles[styleLayer.source] = sourceCache.getRenderableIds(true).map(id => sourceCache.getTileByID(id)).sort((a, b) => b.tileID.overscaledZ - a.tileID.overscaledZ || (a.tileID.isLessThan(b.tileID) ? -1 : 1));
            }
            const layerBucketsChanged = this.crossTileSymbolIndex.addLayer(styleLayer, layerTiles[styleLayer.source], transform.center.lng);
            symbolBucketsChanged = symbolBucketsChanged || layerBucketsChanged;
        }
        this.crossTileSymbolIndex.pruneUnusedLayers(this._order);
        forceFullPlacement = forceFullPlacement || this._layerOrderChanged || fadeDuration === 0;
        if (this._layerOrderChanged) {
            this.fire(new ref_properties.Event('neworder'));
        }
        if (forceFullPlacement || !this.pauseablePlacement || this.pauseablePlacement.isDone() && !this.placement.stillRecent(ref_properties.exported.now(), transform.zoom)) {
            this.pauseablePlacement = new PauseablePlacement(transform, this._order, forceFullPlacement, showCollisionBoxes, fadeDuration, crossSourceCollisions, this.placement, this.fog ? this.fog.state : null);
            this._layerOrderChanged = false;
        }
        if (this.pauseablePlacement.isDone()) {
            this.placement.setStale();
        } else {
            this.pauseablePlacement.continuePlacement(this._order, this._layers, layerTiles);
            if (this.pauseablePlacement.isDone()) {
                this.placement = this.pauseablePlacement.commit(ref_properties.exported.now());
                placementCommitted = true;
            }
            if (symbolBucketsChanged) {
                this.pauseablePlacement.placement.setStale();
            }
        }
        if (placementCommitted || symbolBucketsChanged) {
            for (const layerID of this._order) {
                const styleLayer = this._layers[layerID];
                if (styleLayer.type !== 'symbol')
                    continue;
                this.placement.updateLayerOpacities(styleLayer, layerTiles[styleLayer.source]);
            }
        }
        const needsRerender = !this.pauseablePlacement.isDone() || this.placement.hasTransitions(ref_properties.exported.now());
        return needsRerender;
    }
    _releaseSymbolFadeTiles() {
        for (const id in this._sourceCaches) {
            this._sourceCaches[id].releaseSymbolFadeTiles();
        }
    }
    getImages(mapId, params, callback) {
        this.imageManager.getImages(params.icons, callback);
        this._updateTilesForChangedImages();
        const setDependencies = sourceCache => {
            if (sourceCache) {
                sourceCache.setDependencies(params.tileID.key, params.type, params.icons);
            }
        };
        setDependencies(this._otherSourceCaches[params.source]);
        setDependencies(this._symbolSourceCaches[params.source]);
    }
    getGlyphs(mapId, params, callback) {
        this.glyphManager.getGlyphs(params.stacks, callback);
    }
    getResource(mapId, params, callback) {
        return ref_properties.makeRequest(params, callback);
    }
    _getSourceCache(source) {
        return this._otherSourceCaches[source];
    }
    _getLayerSourceCache(layer) {
        return layer.type === 'symbol' ? this._symbolSourceCaches[layer.source] : this._otherSourceCaches[layer.source];
    }
    _getSourceCaches(source) {
        const sourceCaches = [];
        if (this._otherSourceCaches[source]) {
            sourceCaches.push(this._otherSourceCaches[source]);
        }
        if (this._symbolSourceCaches[source]) {
            sourceCaches.push(this._symbolSourceCaches[source]);
        }
        return sourceCaches;
    }
    has3DLayers() {
        return this._num3DLayers > 0;
    }
    hasSymbolLayers() {
        return this._numSymbolLayers > 0;
    }
    hasCircleLayers() {
        return this._numCircleLayers > 0;
    }
}
Style.getSourceType = getType;
Style.setSourceType = setType;
Style.registerForPluginStateChange = ref_properties.registerForPluginStateChange;

var preludeCommon = "\n#define EPSILON 0.0000001\n#define PI 3.141592653589793\n#ifdef FOG\nuniform mediump vec4 u_fog_color;uniform mediump vec2 u_fog_range;uniform mediump float u_fog_horizon_blend;varying vec3 v_fog_pos;float fog_range(float depth) {return (depth-u_fog_range[0])/(u_fog_range[1]-u_fog_range[0]);}float fog_horizon_blending(vec3 camera_dir) {float t=max(0.0,camera_dir.z/u_fog_horizon_blend);return u_fog_color.a*exp(-3.0*t*t);}float fog_opacity(float t) {const float decay=6.0;float falloff=1.0-min(1.0,exp(-decay*t));falloff*=falloff*falloff;return u_fog_color.a*min(1.0,1.00747*falloff);}\n#endif";

var preludeFrag = "#ifdef GL_ES\nprecision mediump float;\n#else\n#if !defined(lowp)\n#define lowp\n#endif\n#if !defined(mediump)\n#define mediump\n#endif\n#if !defined(highp)\n#define highp\n#endif\n#endif\nhighp vec3 hash(highp vec2 p) {highp vec3 p3=fract(p.xyx*vec3(443.8975,397.2973,491.1871));p3+=dot(p3,p3.yxz+19.19);return fract((p3.xxy+p3.yzz)*p3.zyx);}vec3 dither(vec3 color,highp vec2 seed) {vec3 rnd=hash(seed)+hash(seed+0.59374)-0.5;return color+rnd/255.0;}";

var preludeVert = "#ifdef GL_ES\nprecision highp float;\n#else\n#if !defined(lowp)\n#define lowp\n#endif\n#if !defined(mediump)\n#define mediump\n#endif\n#if !defined(highp)\n#define highp\n#endif\n#endif\nvec2 unpack_float(const float packedValue) {int packedIntValue=int(packedValue);int v0=packedIntValue/256;return vec2(v0,packedIntValue-v0*256);}vec2 unpack_opacity(const float packedOpacity) {int intOpacity=int(packedOpacity)/2;return vec2(float(intOpacity)/127.0,mod(packedOpacity,2.0));}vec4 decode_color(const vec2 encodedColor) {return vec4(\nunpack_float(encodedColor[0])/255.0,unpack_float(encodedColor[1])/255.0\n);}float unpack_mix_vec2(const vec2 packedValue,const float t) {return mix(packedValue[0],packedValue[1],t);}vec4 unpack_mix_color(const vec4 packedColors,const float t) {vec4 minColor=decode_color(vec2(packedColors[0],packedColors[1]));vec4 maxColor=decode_color(vec2(packedColors[2],packedColors[3]));return mix(minColor,maxColor,t);}vec2 get_pattern_pos(const vec2 pixel_coord_upper,const vec2 pixel_coord_lower,const vec2 pattern_size,const float tile_units_to_pixels,const vec2 pos) {vec2 offset=mod(mod(mod(pixel_coord_upper,pattern_size)*256.0,pattern_size)*256.0+pixel_coord_lower,pattern_size);return (tile_units_to_pixels*pos+offset)/pattern_size;}const vec4 AWAY=vec4(-1000.0,-1000.0,-1000.0,1);//Normalized device coordinate that is not rendered.";

var backgroundFrag = "uniform vec4 u_color;uniform float u_opacity;void main() {vec4 out_color=u_color;\n#ifdef FOG\nout_color=fog_dither(fog_apply_premultiplied(out_color,v_fog_pos));\n#endif\ngl_FragColor=out_color*u_opacity;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var backgroundVert = "attribute vec2 a_pos;uniform mat4 u_matrix;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);\n#ifdef FOG\nv_fog_pos=fog_position(a_pos);\n#endif\n}";

var backgroundPatternFrag = "uniform vec2 u_pattern_tl_a;uniform vec2 u_pattern_br_a;uniform vec2 u_pattern_tl_b;uniform vec2 u_pattern_br_b;uniform vec2 u_texsize;uniform float u_mix;uniform float u_opacity;uniform sampler2D u_image;varying vec2 v_pos_a;varying vec2 v_pos_b;void main() {vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(u_pattern_tl_a/u_texsize,u_pattern_br_a/u_texsize,imagecoord);vec4 color1=texture2D(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(u_pattern_tl_b/u_texsize,u_pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture2D(u_image,pos2);vec4 out_color=mix(color1,color2,u_mix);\n#ifdef FOG\nout_color=fog_dither(fog_apply_premultiplied(out_color,v_fog_pos));\n#endif\ngl_FragColor=out_color*u_opacity;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var backgroundPatternVert = "uniform mat4 u_matrix;uniform vec2 u_pattern_size_a;uniform vec2 u_pattern_size_b;uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform float u_scale_a;uniform float u_scale_b;uniform float u_tile_units_to_pixels;attribute vec2 a_pos;varying vec2 v_pos_a;varying vec2 v_pos_b;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,u_scale_a*u_pattern_size_a,u_tile_units_to_pixels,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,u_scale_b*u_pattern_size_b,u_tile_units_to_pixels,a_pos);\n#ifdef FOG\nv_fog_pos=fog_position(a_pos);\n#endif\n}";

var circleFrag = "varying vec3 v_data;varying float v_visibility;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define mediump float radius\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define highp vec4 stroke_color\n#pragma mapbox: define mediump float stroke_width\n#pragma mapbox: define lowp float stroke_opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize mediump float radius\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize highp vec4 stroke_color\n#pragma mapbox: initialize mediump float stroke_width\n#pragma mapbox: initialize lowp float stroke_opacity\nvec2 extrude=v_data.xy;float extrude_length=length(extrude);lowp float antialiasblur=v_data.z;float antialiased_blur=-max(blur,antialiasblur);float opacity_t=smoothstep(0.0,antialiased_blur,extrude_length-1.0);float color_t=stroke_width < 0.01 ? 0.0 : smoothstep(\nantialiased_blur,0.0,extrude_length-radius/(radius+stroke_width)\n);vec4 out_color=mix(color*opacity,stroke_color*stroke_opacity,color_t);\n#ifdef FOG\nout_color=fog_apply_premultiplied(out_color,v_fog_pos);\n#endif\ngl_FragColor=out_color*(v_visibility*opacity_t);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var circleVert = "#define NUM_VISIBILITY_RINGS 2\n#define INV_SQRT2 0.70710678\n#define ELEVATION_BIAS 0.0001\n#define NUM_SAMPLES_PER_RING 16\nuniform mat4 u_matrix;uniform vec2 u_extrude_scale;uniform lowp float u_device_pixel_ratio;uniform highp float u_camera_to_center_distance;attribute vec2 a_pos;varying vec3 v_data;varying float v_visibility;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define mediump float radius\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define highp vec4 stroke_color\n#pragma mapbox: define mediump float stroke_width\n#pragma mapbox: define lowp float stroke_opacity\nvec2 calc_offset(vec2 extrusion,float radius,float stroke_width, float view_scale) {return extrusion*(radius+stroke_width)*u_extrude_scale*view_scale;}float cantilevered_elevation(vec2 pos,float radius,float stroke_width,float view_scale) {vec2 c1=pos+calc_offset(vec2(-1,-1),radius,stroke_width,view_scale);vec2 c2=pos+calc_offset(vec2(1,-1),radius,stroke_width,view_scale);vec2 c3=pos+calc_offset(vec2(1,1),radius,stroke_width,view_scale);vec2 c4=pos+calc_offset(vec2(-1,1),radius,stroke_width,view_scale);float h1=elevation(c1)+ELEVATION_BIAS;float h2=elevation(c2)+ELEVATION_BIAS;float h3=elevation(c3)+ELEVATION_BIAS;float h4=elevation(c4)+ELEVATION_BIAS;return max(h4,max(h3,max(h1,h2)));}float circle_elevation(vec2 pos) {\n#if defined(TERRAIN)\nreturn elevation(pos)+ELEVATION_BIAS;\n#else\nreturn 0.0;\n#endif\n}vec4 project_vertex(vec2 extrusion,vec4 world_center,vec4 projected_center,float radius,float stroke_width, float view_scale) {vec2 sample_offset=calc_offset(extrusion,radius,stroke_width,view_scale);\n#ifdef PITCH_WITH_MAP\nreturn u_matrix*( world_center+vec4(sample_offset,0,0) );\n#else\nreturn projected_center+vec4(sample_offset,0,0);\n#endif\n}float get_sample_step() {\n#ifdef PITCH_WITH_MAP\nreturn 2.0*PI/float(NUM_SAMPLES_PER_RING);\n#else\nreturn PI/float(NUM_SAMPLES_PER_RING);\n#endif\n}void main(void) {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize mediump float radius\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize highp vec4 stroke_color\n#pragma mapbox: initialize mediump float stroke_width\n#pragma mapbox: initialize lowp float stroke_opacity\nvec2 extrude=vec2(mod(a_pos,2.0)*2.0-1.0);vec2 circle_center=floor(a_pos*0.5);float height=circle_elevation(circle_center);vec4 world_center=vec4(circle_center,height,1);vec4 projected_center=u_matrix*world_center;float view_scale=0.0;\n#ifdef PITCH_WITH_MAP\n#ifdef SCALE_WITH_MAP\nview_scale=1.0;\n#else\nview_scale=projected_center.w/u_camera_to_center_distance;\n#endif\n#else\n#ifdef SCALE_WITH_MAP\nview_scale=u_camera_to_center_distance;\n#else\nview_scale=projected_center.w;\n#endif\n#endif\ngl_Position=project_vertex(extrude,world_center,projected_center,radius,stroke_width,view_scale);float visibility=0.0;\n#ifdef TERRAIN\nfloat step=get_sample_step();\n#ifdef PITCH_WITH_MAP\nfloat cantilevered_height=cantilevered_elevation(circle_center,radius,stroke_width,view_scale);vec4 occlusion_world_center=vec4(circle_center,cantilevered_height,1);vec4 occlusion_projected_center=u_matrix*occlusion_world_center;\n#else\nvec4 occlusion_world_center=world_center;vec4 occlusion_projected_center=projected_center;\n#endif\nfor(int ring=0; ring < NUM_VISIBILITY_RINGS; ring++) {float scale=(float(ring)+1.0)/float(NUM_VISIBILITY_RINGS);for(int i=0; i < NUM_SAMPLES_PER_RING; i++) {vec2 extrusion=vec2(cos(step*float(i)),-sin(step*float(i)))*scale;vec4 frag_pos=project_vertex(extrusion,occlusion_world_center,occlusion_projected_center,radius,stroke_width,view_scale);visibility+=float(!isOccluded(frag_pos));}}visibility/=float(NUM_VISIBILITY_RINGS)*float(NUM_SAMPLES_PER_RING);\n#else\nvisibility=1.0;\n#endif\nv_visibility=visibility;lowp float antialiasblur=1.0/u_device_pixel_ratio/(radius+stroke_width);v_data=vec3(extrude.x,extrude.y,antialiasblur);\n#ifdef FOG\nv_fog_pos=fog_position(world_center.xyz);\n#endif\n}";

var clippingMaskFrag = "void main() {gl_FragColor=vec4(1.0);}";

var clippingMaskVert = "attribute vec2 a_pos;uniform mat4 u_matrix;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);}";

var heatmapFrag = "uniform highp float u_intensity;varying vec2 v_extrude;\n#pragma mapbox: define highp float weight\n#define GAUSS_COEF 0.3989422804014327\nvoid main() {\n#pragma mapbox: initialize highp float weight\nfloat d=-0.5*3.0*3.0*dot(v_extrude,v_extrude);float val=weight*u_intensity*GAUSS_COEF*exp(d);gl_FragColor=vec4(val,1.0,1.0,1.0);\n#ifdef FOG\ngl_FragColor.r*=pow(1.0-fog_opacity(v_fog_pos),2.0);\n#endif\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var heatmapVert = "uniform mat4 u_matrix;uniform float u_extrude_scale;uniform float u_opacity;uniform float u_intensity;attribute vec2 a_pos;varying vec2 v_extrude;\n#pragma mapbox: define highp float weight\n#pragma mapbox: define mediump float radius\nconst highp float ZERO=1.0/255.0/16.0;\n#define GAUSS_COEF 0.3989422804014327\nvoid main(void) {\n#pragma mapbox: initialize highp float weight\n#pragma mapbox: initialize mediump float radius\nvec2 unscaled_extrude=vec2(mod(a_pos,2.0)*2.0-1.0);float S=sqrt(-2.0*log(ZERO/weight/u_intensity/GAUSS_COEF))/3.0;v_extrude=S*unscaled_extrude;vec2 extrude=v_extrude*radius*u_extrude_scale;vec3 pos=vec3(floor(a_pos*0.5)+extrude,elevation(floor(a_pos*0.5)));gl_Position=u_matrix*vec4(pos,1);\n#ifdef FOG\nv_fog_pos=fog_position(pos);\n#endif\n}";

var heatmapTextureFrag = "uniform sampler2D u_image;uniform sampler2D u_color_ramp;uniform float u_opacity;varying vec2 v_pos;void main() {float t=texture2D(u_image,v_pos).r;vec4 color=texture2D(u_color_ramp,vec2(t,0.5));gl_FragColor=color*u_opacity;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(0.0);\n#endif\n}";

var heatmapTextureVert = "uniform mat4 u_matrix;uniform vec2 u_world;attribute vec2 a_pos;varying vec2 v_pos;void main() {gl_Position=u_matrix*vec4(a_pos*u_world,0,1);v_pos.x=a_pos.x;v_pos.y=1.0-a_pos.y;}";

var collisionBoxFrag = "varying float v_placed;varying float v_notUsed;void main() {vec4 red =vec4(1.0,0.0,0.0,1.0);vec4 blue=vec4(0.0,0.0,1.0,0.5);gl_FragColor =mix(red,blue,step(0.5,v_placed))*0.5;gl_FragColor*=mix(1.0,0.1,step(0.5,v_notUsed));}";

var collisionBoxVert = "attribute vec2 a_pos;attribute vec2 a_anchor_pos;attribute vec2 a_extrude;attribute vec2 a_placed;attribute vec2 a_shift;attribute float a_size_scale;attribute vec2 a_padding;uniform mat4 u_matrix;uniform vec2 u_extrude_scale;uniform float u_camera_to_center_distance;varying float v_placed;varying float v_notUsed;void main() {vec4 projectedPoint=u_matrix*vec4(a_anchor_pos,elevation(a_anchor_pos),1);highp float camera_to_anchor_distance=projectedPoint.w;highp float collision_perspective_ratio=clamp(\n0.5+0.5*(u_camera_to_center_distance/camera_to_anchor_distance),0.0,1.5);gl_Position=u_matrix*vec4(a_pos,elevation(a_pos),1.0);gl_Position.xy+=(a_extrude*a_size_scale+a_shift+a_padding)*u_extrude_scale*gl_Position.w*collision_perspective_ratio;v_placed=a_placed.x;v_notUsed=a_placed.y;}";

var collisionCircleFrag = "varying float v_radius;varying vec2 v_extrude;varying float v_perspective_ratio;varying float v_collision;void main() {float alpha=0.5*min(v_perspective_ratio,1.0);float stroke_radius=0.9*max(v_perspective_ratio,1.0);float distance_to_center=length(v_extrude);float distance_to_edge=abs(distance_to_center-v_radius);float opacity_t=smoothstep(-stroke_radius,0.0,-distance_to_edge);vec4 color=mix(vec4(0.0,0.0,1.0,0.5),vec4(1.0,0.0,0.0,1.0),v_collision);gl_FragColor=color*alpha*opacity_t;}";

var collisionCircleVert = "attribute vec2 a_pos_2f;attribute float a_radius;attribute vec2 a_flags;uniform mat4 u_matrix;uniform mat4 u_inv_matrix;uniform vec2 u_viewport_size;uniform float u_camera_to_center_distance;varying float v_radius;varying vec2 v_extrude;varying float v_perspective_ratio;varying float v_collision;vec3 toTilePosition(vec2 screenPos) {vec4 rayStart=u_inv_matrix*vec4(screenPos,-1.0,1.0);vec4 rayEnd  =u_inv_matrix*vec4(screenPos, 1.0,1.0);rayStart.xyz/=rayStart.w;rayEnd.xyz  /=rayEnd.w;highp float t=(0.0-rayStart.z)/(rayEnd.z-rayStart.z);return mix(rayStart.xyz,rayEnd.xyz,t);}void main() {vec2 quadCenterPos=a_pos_2f;float radius=a_radius;float collision=a_flags.x;float vertexIdx=a_flags.y;vec2 quadVertexOffset=vec2(\nmix(-1.0,1.0,float(vertexIdx >=2.0)),mix(-1.0,1.0,float(vertexIdx >=1.0 && vertexIdx <=2.0)));vec2 quadVertexExtent=quadVertexOffset*radius;vec3 tilePos=toTilePosition(quadCenterPos);vec4 clipPos=u_matrix*vec4(tilePos,1.0);highp float camera_to_anchor_distance=clipPos.w;highp float collision_perspective_ratio=clamp(\n0.5+0.5*(u_camera_to_center_distance/camera_to_anchor_distance),0.0,4.0);float padding_factor=1.2;v_radius=radius;v_extrude=quadVertexExtent*padding_factor;v_perspective_ratio=collision_perspective_ratio;v_collision=collision;gl_Position=vec4(clipPos.xyz/clipPos.w,1.0)+vec4(quadVertexExtent*padding_factor/u_viewport_size*2.0,0.0,0.0);}";

var debugFrag = "uniform highp vec4 u_color;uniform sampler2D u_overlay;varying vec2 v_uv;void main() {vec4 overlay_color=texture2D(u_overlay,v_uv);gl_FragColor=mix(u_color,overlay_color,overlay_color.a);}";

var debugVert = "attribute vec2 a_pos;varying vec2 v_uv;uniform mat4 u_matrix;uniform float u_overlay_scale;void main() {float h=elevation(a_pos);v_uv=a_pos/8192.0;gl_Position=u_matrix*vec4(a_pos*u_overlay_scale,h,1);}";

var fillFrag = "#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float opacity\nvec4 out_color=color;\n#ifdef FOG\nout_color=fog_dither(fog_apply_premultiplied(out_color,v_fog_pos));\n#endif\ngl_FragColor=out_color*opacity;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var fillVert = "attribute vec2 a_pos;uniform mat4 u_matrix;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float opacity\ngl_Position=u_matrix*vec4(a_pos,0,1);\n#ifdef FOG\nv_fog_pos=fog_position(a_pos);\n#endif\n}";

var fillOutlineFrag = "varying vec2 v_pos;\n#pragma mapbox: define highp vec4 outline_color\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 outline_color\n#pragma mapbox: initialize lowp float opacity\nfloat dist=length(v_pos-gl_FragCoord.xy);float alpha=1.0-smoothstep(0.0,1.0,dist);vec4 out_color=outline_color;\n#ifdef FOG\nout_color=fog_dither(fog_apply_premultiplied(out_color,v_fog_pos));\n#endif\ngl_FragColor=out_color*(alpha*opacity);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var fillOutlineVert = "attribute vec2 a_pos;uniform mat4 u_matrix;uniform vec2 u_world;varying vec2 v_pos;\n#pragma mapbox: define highp vec4 outline_color\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 outline_color\n#pragma mapbox: initialize lowp float opacity\ngl_Position=u_matrix*vec4(a_pos,0,1);v_pos=(gl_Position.xy/gl_Position.w+1.0)/2.0*u_world;\n#ifdef FOG\nv_fog_pos=fog_position(a_pos);\n#endif\n}";

var fillOutlinePatternFrag = "uniform vec2 u_texsize;uniform sampler2D u_image;uniform float u_fade;varying vec2 v_pos_a;varying vec2 v_pos_b;varying vec2 v_pos;\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture2D(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture2D(u_image,pos2);float dist=length(v_pos-gl_FragCoord.xy);float alpha=1.0-smoothstep(0.0,1.0,dist);vec4 out_color=mix(color1,color2,u_fade);\n#ifdef FOG\nout_color=fog_dither(fog_apply_premultiplied(out_color,v_fog_pos));\n#endif\ngl_FragColor=out_color*(alpha*opacity);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var fillOutlinePatternVert = "uniform mat4 u_matrix;uniform vec2 u_world;uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform vec3 u_scale;attribute vec2 a_pos;varying vec2 v_pos_a;varying vec2 v_pos_b;varying vec2 v_pos;\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;gl_Position=u_matrix*vec4(a_pos,0,1);vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileRatio,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileRatio,a_pos);v_pos=(gl_Position.xy/gl_Position.w+1.0)/2.0*u_world;\n#ifdef FOG\nv_fog_pos=fog_position(a_pos);\n#endif\n}";

var fillPatternFrag = "uniform vec2 u_texsize;uniform float u_fade;uniform sampler2D u_image;varying vec2 v_pos_a;varying vec2 v_pos_b;\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture2D(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture2D(u_image,pos2);vec4 out_color=mix(color1,color2,u_fade);\n#ifdef FOG\nout_color=fog_dither(fog_apply_premultiplied(out_color,v_fog_pos));\n#endif\ngl_FragColor=out_color*opacity;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var fillPatternVert = "uniform mat4 u_matrix;uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform vec3 u_scale;attribute vec2 a_pos;varying vec2 v_pos_a;varying vec2 v_pos_b;\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileZoomRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;gl_Position=u_matrix*vec4(a_pos,0,1);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileZoomRatio,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileZoomRatio,a_pos);\n#ifdef FOG\nv_fog_pos=fog_position(a_pos);\n#endif\n}";

var fillExtrusionFrag = "varying vec4 v_color;void main() {vec4 color=v_color;\n#ifdef FOG\ncolor=fog_dither(fog_apply_premultiplied(color,v_fog_pos));\n#endif\ngl_FragColor=color;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var fillExtrusionVert = "uniform mat4 u_matrix;uniform vec3 u_lightcolor;uniform lowp vec3 u_lightpos;uniform lowp float u_lightintensity;uniform float u_vertical_gradient;uniform lowp float u_opacity;attribute vec4 a_pos_normal_ed;attribute vec2 a_centroid_pos;varying vec4 v_color;\n#pragma mapbox: define highp float base\n#pragma mapbox: define highp float height\n#pragma mapbox: define highp vec4 color\nvoid main() {\n#pragma mapbox: initialize highp float base\n#pragma mapbox: initialize highp float height\n#pragma mapbox: initialize highp vec4 color\nvec3 pos_nx=floor(a_pos_normal_ed.xyz*0.5);mediump vec3 top_up_ny=a_pos_normal_ed.xyz-2.0*pos_nx;float x_normal=pos_nx.z/8192.0;vec3 normal=top_up_ny.y==1.0 ? vec3(0.0,0.0,1.0) : normalize(vec3(x_normal,(2.0*top_up_ny.z-1.0)*(1.0-abs(x_normal)),0.0));base=max(0.0,base);height=max(0.0,height);float t=top_up_ny.x;\n#ifdef TERRAIN\nvec2 centroid_pos=a_centroid_pos;bool flat_roof=centroid_pos.x !=0.0;float ele=elevation(pos_nx.xy);float hidden=float(centroid_pos.x==0.0 && centroid_pos.y==1.0);float c_ele=flat_roof ? centroid_pos.y==0.0 ? elevationFromUint16(centroid_pos.x) : flatElevation(centroid_pos) : ele;float h=flat_roof ? max(c_ele+height,ele+base+2.0) : ele+(t > 0.0 ? height : base==0.0 ?-5.0 : base);vec3 pos=vec3(pos_nx.xy,h);gl_Position=mix(u_matrix*vec4(pos,1),AWAY,hidden);\n#else\nvec3 pos=vec3(pos_nx.xy,t > 0.0 ? height : base);gl_Position=u_matrix*vec4(pos,1);\n#endif\nfloat colorvalue=color.r*0.2126+color.g*0.7152+color.b*0.0722;v_color=vec4(0.0,0.0,0.0,1.0);vec4 ambientlight=vec4(0.03,0.03,0.03,1.0);color+=ambientlight;float directional=clamp(dot(normal,u_lightpos),0.0,1.0);directional=mix((1.0-u_lightintensity),max((1.0-colorvalue+u_lightintensity),1.0),directional);if (normal.y !=0.0) {directional*=(\n(1.0-u_vertical_gradient)+(u_vertical_gradient*clamp((t+base)*pow(height/150.0,0.5),mix(0.7,0.98,1.0-u_lightintensity),1.0)));}v_color.rgb+=clamp(color.rgb*directional*u_lightcolor,mix(vec3(0.0),vec3(0.3),1.0-u_lightcolor),vec3(1.0));v_color*=u_opacity;\n#ifdef FOG\nv_fog_pos=fog_position(pos);\n#endif\n}";

var fillExtrusionPatternFrag = "uniform vec2 u_texsize;uniform float u_fade;uniform sampler2D u_image;varying vec2 v_pos_a;varying vec2 v_pos_b;varying vec4 v_lighting;\n#pragma mapbox: define lowp float base\n#pragma mapbox: define lowp float height\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\nvoid main() {\n#pragma mapbox: initialize lowp float base\n#pragma mapbox: initialize lowp float height\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture2D(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture2D(u_image,pos2);vec4 out_color=mix(color1,color2,u_fade);out_color=out_color*v_lighting;\n#ifdef FOG\nout_color=fog_dither(fog_apply_premultiplied(out_color,v_fog_pos));\n#endif\ngl_FragColor=out_color;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var fillExtrusionPatternVert = "uniform mat4 u_matrix;uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform float u_height_factor;uniform vec3 u_scale;uniform float u_vertical_gradient;uniform lowp float u_opacity;uniform vec3 u_lightcolor;uniform lowp vec3 u_lightpos;uniform lowp float u_lightintensity;attribute vec4 a_pos_normal_ed;attribute vec2 a_centroid_pos;varying vec2 v_pos_a;varying vec2 v_pos_b;varying vec4 v_lighting;\n#pragma mapbox: define lowp float base\n#pragma mapbox: define lowp float height\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\nvoid main() {\n#pragma mapbox: initialize lowp float base\n#pragma mapbox: initialize lowp float height\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec3 pos_nx=floor(a_pos_normal_ed.xyz*0.5);mediump vec3 top_up_ny=a_pos_normal_ed.xyz-2.0*pos_nx;float x_normal=pos_nx.z/8192.0;vec3 normal=top_up_ny.y==1.0 ? vec3(0.0,0.0,1.0) : normalize(vec3(x_normal,(2.0*top_up_ny.z-1.0)*(1.0-abs(x_normal)),0.0));float edgedistance=a_pos_normal_ed.w;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;base=max(0.0,base);height=max(0.0,height);float t=top_up_ny.x;float z=t > 0.0 ? height : base;\n#ifdef TERRAIN\nvec2 centroid_pos=a_centroid_pos;bool flat_roof=centroid_pos.x !=0.0;float ele=elevation(pos_nx.xy);float hidden=float(centroid_pos.x==0.0 && centroid_pos.y==1.0);float c_ele=flat_roof ? centroid_pos.y==0.0 ? elevationFromUint16(centroid_pos.x) : flatElevation(centroid_pos) : ele;float h=flat_roof ? max(c_ele+height,ele+base+2.0) : ele+(t > 0.0 ? height : base==0.0 ?-5.0 : base);vec3 p=vec3(pos_nx.xy,h);gl_Position=mix(u_matrix*vec4(p,1),AWAY,hidden);\n#else\nvec3 p=vec3(pos_nx.xy,z);gl_Position=u_matrix*vec4(p,1);\n#endif\nvec2 pos=normal.z==1.0\n? pos_nx.xy\n: vec2(edgedistance,z*u_height_factor);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileRatio,pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileRatio,pos);v_lighting=vec4(0.0,0.0,0.0,1.0);float directional=clamp(dot(normal,u_lightpos),0.0,1.0);directional=mix((1.0-u_lightintensity),max((0.5+u_lightintensity),1.0),directional);if (normal.y !=0.0) {directional*=(\n(1.0-u_vertical_gradient)+(u_vertical_gradient*clamp((t+base)*pow(height/150.0,0.5),mix(0.7,0.98,1.0-u_lightintensity),1.0)));}v_lighting.rgb+=clamp(directional*u_lightcolor,mix(vec3(0.0),vec3(0.3),1.0-u_lightcolor),vec3(1.0));v_lighting*=u_opacity;\n#ifdef FOG\nv_fog_pos=fog_position(p);\n#endif\n}";

var hillshadePrepareFrag = "#ifdef GL_ES\nprecision highp float;\n#endif\nuniform sampler2D u_image;varying vec2 v_pos;uniform vec2 u_dimension;uniform float u_zoom;uniform vec4 u_unpack;float getElevation(vec2 coord,float bias) {vec4 data=texture2D(u_image,coord)*255.0;data.a=-1.0;return dot(data,u_unpack)/4.0;}void main() {vec2 epsilon=1.0/u_dimension;float a=getElevation(v_pos+vec2(-epsilon.x,-epsilon.y),0.0);float b=getElevation(v_pos+vec2(0,-epsilon.y),0.0);float c=getElevation(v_pos+vec2(epsilon.x,-epsilon.y),0.0);float d=getElevation(v_pos+vec2(-epsilon.x,0),0.0);float e=getElevation(v_pos,0.0);float f=getElevation(v_pos+vec2(epsilon.x,0),0.0);float g=getElevation(v_pos+vec2(-epsilon.x,epsilon.y),0.0);float h=getElevation(v_pos+vec2(0,epsilon.y),0.0);float i=getElevation(v_pos+vec2(epsilon.x,epsilon.y),0.0);float exaggerationFactor=u_zoom < 2.0 ? 0.4 : u_zoom < 4.5 ? 0.35 : 0.3;float exaggeration=u_zoom < 15.0 ? (u_zoom-15.0)*exaggerationFactor : 0.0;vec2 deriv=vec2(\n(c+f+f+i)-(a+d+d+g),(g+h+h+i)-(a+b+b+c)\n)/pow(2.0,exaggeration+(19.2562-u_zoom));gl_FragColor=clamp(vec4(\nderiv.x/2.0+0.5,deriv.y/2.0+0.5,1.0,1.0),0.0,1.0);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var hillshadePrepareVert = "uniform mat4 u_matrix;uniform vec2 u_dimension;attribute vec2 a_pos;attribute vec2 a_texture_pos;varying vec2 v_pos;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);highp vec2 epsilon=1.0/u_dimension;float scale=(u_dimension.x-2.0)/u_dimension.x;v_pos=(a_texture_pos/8192.0)*scale+epsilon;}";

var hillshadeFrag = "uniform sampler2D u_image;varying vec2 v_pos;uniform vec2 u_latrange;uniform vec2 u_light;uniform vec4 u_shadow;uniform vec4 u_highlight;uniform vec4 u_accent;void main() {vec4 pixel=texture2D(u_image,v_pos);vec2 deriv=((pixel.rg*2.0)-1.0);float scaleFactor=cos(radians((u_latrange[0]-u_latrange[1])*(1.0-v_pos.y)+u_latrange[1]));float slope=atan(1.25*length(deriv)/scaleFactor);float aspect=deriv.x !=0.0 ? atan(deriv.y,-deriv.x) : PI/2.0*(deriv.y > 0.0 ? 1.0 :-1.0);float intensity=u_light.x;float azimuth=u_light.y+PI;float base=1.875-intensity*1.75;float maxValue=0.5*PI;float scaledSlope=intensity !=0.5 ? ((pow(base,slope)-1.0)/(pow(base,maxValue)-1.0))*maxValue : slope;float accent=cos(scaledSlope);vec4 accent_color=(1.0-accent)*u_accent*clamp(intensity*2.0,0.0,1.0);float shade=abs(mod((aspect+azimuth)/PI+0.5,2.0)-1.0);vec4 shade_color=mix(u_shadow,u_highlight,shade)*sin(scaledSlope)*clamp(intensity*2.0,0.0,1.0);gl_FragColor=accent_color*(1.0-shade_color.a)+shade_color;\n#ifdef FOG\ngl_FragColor=fog_dither(fog_apply_premultiplied(gl_FragColor,v_fog_pos));\n#endif\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var hillshadeVert = "uniform mat4 u_matrix;attribute vec2 a_pos;attribute vec2 a_texture_pos;varying vec2 v_pos;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);v_pos=a_texture_pos/8192.0;\n#ifdef FOG\nv_fog_pos=fog_position(a_pos);\n#endif\n}";

var lineFrag = "uniform lowp float u_device_pixel_ratio;varying vec2 v_width2;varying vec2 v_normal;varying float v_gamma_scale;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\nfloat dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);vec4 out_color=color;\n#ifdef FOG\nout_color=fog_dither(fog_apply_premultiplied(out_color,v_fog_pos));\n#endif\ngl_FragColor=out_color*(alpha*opacity);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var lineVert = "\n#define scale 0.015873016\nattribute vec2 a_pos_normal;attribute vec4 a_data;uniform mat4 u_matrix;uniform mediump float u_ratio;uniform vec2 u_units_to_pixels;uniform lowp float u_device_pixel_ratio;varying vec2 v_normal;varying vec2 v_width2;varying float v_gamma_scale;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float width\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float gapwidth\n#pragma mapbox: initialize lowp float offset\n#pragma mapbox: initialize mediump float width\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);vec4 projected_extrude=u_matrix*vec4(dist/u_ratio,0.0,0.0);gl_Position=u_matrix*vec4(pos+offset2/u_ratio,0.0,1.0)+projected_extrude;\n#ifndef RENDER_TO_TEXTURE\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length(projected_extrude.xy/gl_Position.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#else\nv_gamma_scale=1.0;\n#endif\nv_width2=vec2(outset,inset);\n#ifdef FOG\nv_fog_pos=fog_position(pos);\n#endif\n}";

var lineGradientFrag = "uniform lowp float u_device_pixel_ratio;uniform sampler2D u_image;varying vec2 v_width2;varying vec2 v_normal;varying float v_gamma_scale;varying highp vec2 v_uv;\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\nfloat dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);vec4 color=texture2D(u_image,v_uv);\n#ifdef FOG\ncolor=fog_dither(fog_apply_premultiplied(color,v_fog_pos));\n#endif\ngl_FragColor=color*(alpha*opacity);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var lineGradientVert = "\n#define scale 0.015873016\nattribute vec2 a_pos_normal;attribute vec4 a_data;attribute float a_uv_x;attribute float a_split_index;uniform mat4 u_matrix;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;uniform vec2 u_units_to_pixels;uniform float u_image_height;varying vec2 v_normal;varying vec2 v_width2;varying float v_gamma_scale;varying highp vec2 v_uv;\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float width\nvoid main() {\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float gapwidth\n#pragma mapbox: initialize lowp float offset\n#pragma mapbox: initialize mediump float width\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;highp float texel_height=1.0/u_image_height;highp float half_texel_height=0.5*texel_height;v_uv=vec2(a_uv_x,a_split_index*texel_height-half_texel_height);vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);vec4 projected_extrude=u_matrix*vec4(dist/u_ratio,0.0,0.0);gl_Position=u_matrix*vec4(pos+offset2/u_ratio,0.0,1.0)+projected_extrude;\n#ifndef RENDER_TO_TEXTURE\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length(projected_extrude.xy/gl_Position.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#else\nv_gamma_scale=1.0;\n#endif\nv_width2=vec2(outset,inset);\n#ifdef FOG\nv_fog_pos=fog_position(pos);\n#endif\n}";

var linePatternFrag = "uniform lowp float u_device_pixel_ratio;uniform vec2 u_texsize;uniform float u_fade;uniform mediump vec3 u_scale;uniform sampler2D u_image;varying vec2 v_normal;varying vec2 v_width2;varying float v_linesofar;varying float v_gamma_scale;varying float v_width;\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileZoomRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;vec2 pattern_size_a=vec2(display_size_a.x*fromScale/tileZoomRatio,display_size_a.y);vec2 pattern_size_b=vec2(display_size_b.x*toScale/tileZoomRatio,display_size_b.y);float aspect_a=display_size_a.y/v_width;float aspect_b=display_size_b.y/v_width;float dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);float x_a=mod(v_linesofar/pattern_size_a.x*aspect_a,1.0);float x_b=mod(v_linesofar/pattern_size_b.x*aspect_b,1.0);float y=0.5*v_normal.y+0.5;vec2 texel_size=1.0/u_texsize;vec2 pos_a=mix(pattern_tl_a*texel_size-texel_size,pattern_br_a*texel_size+texel_size,vec2(x_a,y));vec2 pos_b=mix(pattern_tl_b*texel_size-texel_size,pattern_br_b*texel_size+texel_size,vec2(x_b,y));vec4 color=mix(texture2D(u_image,pos_a),texture2D(u_image,pos_b),u_fade);\n#ifdef FOG\ncolor=fog_dither(fog_apply_premultiplied(color,v_fog_pos));\n#endif\ngl_FragColor=color*(alpha*opacity);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var linePatternVert = "\n#define scale 0.015873016\nattribute vec2 a_pos_normal;attribute vec4 a_data;attribute float a_linesofar;uniform mat4 u_matrix;uniform vec2 u_units_to_pixels;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;varying vec2 v_normal;varying vec2 v_width2;varying float v_linesofar;varying float v_gamma_scale;varying float v_width;\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\n#pragma mapbox: define lowp vec4 pattern_from\n#pragma mapbox: define lowp vec4 pattern_to\n#pragma mapbox: define lowp float pixel_ratio_from\n#pragma mapbox: define lowp float pixel_ratio_to\nvoid main() {\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize lowp float offset\n#pragma mapbox: initialize mediump float gapwidth\n#pragma mapbox: initialize mediump float width\n#pragma mapbox: initialize lowp float floorwidth\n#pragma mapbox: initialize mediump vec4 pattern_from\n#pragma mapbox: initialize mediump vec4 pattern_to\n#pragma mapbox: initialize lowp float pixel_ratio_from\n#pragma mapbox: initialize lowp float pixel_ratio_to\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);vec4 projected_extrude=u_matrix*vec4(dist/u_ratio,0.0,0.0);gl_Position=u_matrix*vec4(pos+offset2/u_ratio,0.0,1.0)+projected_extrude;\n#ifndef RENDER_TO_TEXTURE\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length(projected_extrude.xy/gl_Position.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#else\nv_gamma_scale=1.0;\n#endif\nv_linesofar=a_linesofar;v_width2=vec2(outset,inset);v_width=floorwidth;\n#ifdef FOG\nv_fog_pos=fog_position(pos);\n#endif\n}";

var lineSDFFrag = "uniform lowp float u_device_pixel_ratio;uniform sampler2D u_image;uniform float u_mix;uniform vec3 u_scale;varying vec2 v_normal;varying vec2 v_width2;varying vec2 v_tex_a;varying vec2 v_tex_b;varying float v_gamma_scale;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\n#pragma mapbox: define lowp vec4 dash_from\n#pragma mapbox: define lowp vec4 dash_to\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float width\n#pragma mapbox: initialize lowp float floorwidth\n#pragma mapbox: initialize mediump vec4 dash_from\n#pragma mapbox: initialize mediump vec4 dash_to\nfloat dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);float sdfdist_a=texture2D(u_image,v_tex_a).a;float sdfdist_b=texture2D(u_image,v_tex_b).a;float sdfdist=mix(sdfdist_a,sdfdist_b,u_mix);float sdfwidth=min(dash_from.z*u_scale.y,dash_to.z*u_scale.z);float sdfgamma=1.0/(2.0*u_device_pixel_ratio)/sdfwidth;alpha*=smoothstep(0.5-sdfgamma/floorwidth,0.5+sdfgamma/floorwidth,sdfdist);vec4 out_color=color;\n#ifdef FOG\nout_color=fog_dither(fog_apply_premultiplied(out_color,v_fog_pos));\n#endif\ngl_FragColor=out_color*(alpha*opacity);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var lineSDFVert = "\n#define EXTRUDE_SCALE 0.015873016\nattribute vec2 a_pos_normal;attribute vec4 a_data;attribute float a_linesofar;uniform mat4 u_matrix;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;uniform vec2 u_units_to_pixels;uniform vec2 u_texsize;uniform mediump vec3 u_scale;varying vec2 v_normal;varying vec2 v_width2;varying vec2 v_tex_a;varying vec2 v_tex_b;varying float v_gamma_scale;\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\n#pragma mapbox: define lowp vec4 dash_from\n#pragma mapbox: define lowp vec4 dash_to\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float gapwidth\n#pragma mapbox: initialize lowp float offset\n#pragma mapbox: initialize mediump float width\n#pragma mapbox: initialize lowp float floorwidth\n#pragma mapbox: initialize mediump vec4 dash_from\n#pragma mapbox: initialize mediump vec4 dash_to\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*EXTRUDE_SCALE;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*EXTRUDE_SCALE*normal.y*mat2(t,-u,u,t);vec4 projected_extrude=u_matrix*vec4(dist/u_ratio,0.0,0.0);gl_Position=u_matrix*vec4(pos+offset2/u_ratio,0.0,1.0)+projected_extrude;\n#ifndef RENDER_TO_TEXTURE\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length(projected_extrude.xy/gl_Position.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#else\nv_gamma_scale=1.0;\n#endif\nfloat tileZoomRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;float widthA=dash_from.z*fromScale;float widthB=dash_to.z*toScale;float heightA=dash_from.y;float heightB=dash_to.y;v_tex_a=vec2(a_linesofar*(tileZoomRatio/widthA)/floorwidth,(-normal.y*heightA+dash_from.x+0.5)/u_texsize.y);v_tex_b=vec2(a_linesofar*(tileZoomRatio/widthB)/floorwidth,(-normal.y*heightB+dash_to.x+0.5)/u_texsize.y);v_width2=vec2(outset,inset);\n#ifdef FOG\nv_fog_pos=fog_position(pos);\n#endif\n}";

var rasterFrag = "uniform float u_fade_t;uniform float u_opacity;uniform sampler2D u_image0;uniform sampler2D u_image1;varying vec2 v_pos0;varying vec2 v_pos1;uniform float u_brightness_low;uniform float u_brightness_high;uniform float u_saturation_factor;uniform float u_contrast_factor;uniform vec3 u_spin_weights;void main() {vec4 color0=texture2D(u_image0,v_pos0);vec4 color1=texture2D(u_image1,v_pos1);if (color0.a > 0.0) {color0.rgb=color0.rgb/color0.a;}if (color1.a > 0.0) {color1.rgb=color1.rgb/color1.a;}vec4 color=mix(color0,color1,u_fade_t);color.a*=u_opacity;vec3 rgb=color.rgb;rgb=vec3(\ndot(rgb,u_spin_weights.xyz),dot(rgb,u_spin_weights.zxy),dot(rgb,u_spin_weights.yzx));float average=(color.r+color.g+color.b)/3.0;rgb+=(average-rgb)*u_saturation_factor;rgb=(rgb-0.5)*u_contrast_factor+0.5;vec3 u_high_vec=vec3(u_brightness_low,u_brightness_low,u_brightness_low);vec3 u_low_vec=vec3(u_brightness_high,u_brightness_high,u_brightness_high);vec3 out_color=mix(u_high_vec,u_low_vec,rgb);\n#ifdef FOG\nout_color=fog_dither(fog_apply(out_color,v_fog_pos));\n#endif\ngl_FragColor=vec4(out_color*color.a,color.a);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var rasterVert = "uniform mat4 u_matrix;uniform vec2 u_tl_parent;uniform float u_scale_parent;uniform float u_buffer_scale;attribute vec2 a_pos;attribute vec2 a_texture_pos;varying vec2 v_pos0;varying vec2 v_pos1;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);v_pos0=(((a_texture_pos/8192.0)-0.5)/u_buffer_scale )+0.5;v_pos1=(v_pos0*u_scale_parent)+u_tl_parent;\n#ifdef FOG\nv_fog_pos=fog_position(a_pos);\n#endif\n}";

var symbolIconFrag = "uniform sampler2D u_texture;varying vec2 v_tex;varying float v_fade_opacity;\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\nlowp float alpha=opacity*v_fade_opacity;gl_FragColor=texture2D(u_texture,v_tex)*alpha;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var symbolIconVert = "attribute vec4 a_pos_offset;attribute vec4 a_data;attribute vec4 a_pixeloffset;attribute vec3 a_projected_pos;attribute float a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform highp float u_camera_to_center_distance;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform float u_fade_change;uniform mat4 u_matrix;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform vec2 u_texsize;varying vec2 v_tex;varying float v_fade_opacity;\n#pragma mapbox: define lowp float opacity\nvoid main() {\n#pragma mapbox: initialize lowp float opacity\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=a_data.xy;vec2 a_size=a_data.zw;float a_size_min=floor(a_size[0]*0.5);vec2 a_pxoffset=a_pixeloffset.xy;vec2 a_minFontScale=a_pixeloffset.zw/256.0;highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}float h=elevation(a_pos);vec4 projectedPoint=u_matrix*vec4(a_pos,h,1);highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(\n0.5+0.5*distance_ratio,0.0,1.5);size*=perspective_ratio;float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=u_matrix*vec4(a_pos+vec2(1,0),h,1);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy,h,1.0);float z=0.0;vec2 offset=rotation_matrix*(a_offset/32.0*max(a_minFontScale,fontScale)+a_pxoffset/16.0);\n#ifdef PITCH_WITH_MAP_TERRAIN\nvec4 tile_pos=u_label_plane_matrix_inv*vec4(a_projected_pos.xy+offset,0.0,1.0);z=elevation(tile_pos.xy);\n#endif\nfloat occlusion_fade=occlusionFade(projectedPoint);gl_Position=mix(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+offset,z,1.0),AWAY,float(projectedPoint.w <=0.0 || occlusion_fade==0.0));v_tex=a_tex/u_texsize;vec2 fade_opacity=unpack_opacity(a_fade_opacity);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;v_fade_opacity=max(0.0,min(occlusion_fade,fade_opacity[0]+fade_change));}";

var symbolSDFFrag = "#define SDF_PX 8.0\nuniform bool u_is_halo;uniform sampler2D u_texture;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;uniform bool u_is_text;varying vec2 v_data0;varying vec3 v_data1;\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\nvoid main() {\n#pragma mapbox: initialize highp vec4 fill_color\n#pragma mapbox: initialize highp vec4 halo_color\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize lowp float halo_width\n#pragma mapbox: initialize lowp float halo_blur\nfloat EDGE_GAMMA=0.105/u_device_pixel_ratio;vec2 tex=v_data0.xy;float gamma_scale=v_data1.x;float size=v_data1.y;float fade_opacity=v_data1[2];float fontScale=u_is_text ? size/24.0 : size;lowp vec4 color=fill_color;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float buff=(256.0-64.0)/256.0;if (u_is_halo) {color=halo_color;gamma=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);buff=(6.0-halo_width/fontScale)/SDF_PX;}lowp float dist=texture2D(u_texture,tex).a;highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(buff-gamma_scaled,buff+gamma_scaled,dist);gl_FragColor=color*(alpha*opacity*fade_opacity);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var symbolSDFVert = "attribute vec4 a_pos_offset;attribute vec4 a_data;attribute vec4 a_pixeloffset;attribute vec3 a_projected_pos;attribute float a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_matrix;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;varying vec2 v_data0;varying vec3 v_data1;\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\nvoid main() {\n#pragma mapbox: initialize highp vec4 fill_color\n#pragma mapbox: initialize highp vec4 halo_color\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize lowp float halo_width\n#pragma mapbox: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=a_data.xy;vec2 a_size=a_data.zw;float a_size_min=floor(a_size[0]*0.5);vec2 a_pxoffset=a_pixeloffset.xy;highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}float h=elevation(a_pos);vec4 projectedPoint=u_matrix*vec4(a_pos,h,1);highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(\n0.5+0.5*distance_ratio,0.0,1.5);size*=perspective_ratio;float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=u_matrix*vec4(a_pos+vec2(1,0),h,1);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy,h,1.0);float z=0.0;vec2 offset=rotation_matrix*(a_offset/32.0*fontScale+a_pxoffset);\n#ifdef PITCH_WITH_MAP_TERRAIN\nvec4 tile_pos=u_label_plane_matrix_inv*vec4(a_projected_pos.xy+offset,0.0,1.0);z=elevation(tile_pos.xy);\n#endif\nfloat occlusion_fade=occlusionFade(projectedPoint);gl_Position=mix(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+offset,z,1.0),AWAY,float(projectedPoint.w <=0.0 || occlusion_fade==0.0));float gamma_scale=gl_Position.w;vec2 fade_opacity=unpack_opacity(a_fade_opacity);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(occlusion_fade,fade_opacity[0]+fade_change));v_data0=a_tex/u_texsize;v_data1=vec3(gamma_scale,size,interpolated_fade_opacity);}";

var symbolTextAndIconFrag = "#define SDF_PX 8.0\n#define SDF 1.0\n#define ICON 0.0\nuniform bool u_is_halo;uniform sampler2D u_texture;uniform sampler2D u_texture_icon;uniform highp float u_gamma_scale;uniform lowp float u_device_pixel_ratio;varying vec4 v_data0;varying vec4 v_data1;\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\nvoid main() {\n#pragma mapbox: initialize highp vec4 fill_color\n#pragma mapbox: initialize highp vec4 halo_color\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize lowp float halo_width\n#pragma mapbox: initialize lowp float halo_blur\nfloat fade_opacity=v_data1[2];if (v_data1.w==ICON) {vec2 tex_icon=v_data0.zw;lowp float alpha=opacity*fade_opacity;gl_FragColor=texture2D(u_texture_icon,tex_icon)*alpha;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\nreturn;}vec2 tex=v_data0.xy;float EDGE_GAMMA=0.105/u_device_pixel_ratio;float gamma_scale=v_data1.x;float size=v_data1.y;float fontScale=size/24.0;lowp vec4 color=fill_color;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float buff=(256.0-64.0)/256.0;if (u_is_halo) {color=halo_color;gamma=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);buff=(6.0-halo_width/fontScale)/SDF_PX;}lowp float dist=texture2D(u_texture,tex).a;highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(buff-gamma_scaled,buff+gamma_scaled,dist);gl_FragColor=color*(alpha*opacity*fade_opacity);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var symbolTextAndIconVert = "attribute vec4 a_pos_offset;attribute vec4 a_data;attribute vec3 a_projected_pos;attribute float a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_matrix;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_texsize_icon;varying vec4 v_data0;varying vec4 v_data1;\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\nvoid main() {\n#pragma mapbox: initialize highp vec4 fill_color\n#pragma mapbox: initialize highp vec4 halo_color\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize lowp float halo_width\n#pragma mapbox: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=a_data.xy;vec2 a_size=a_data.zw;float a_size_min=floor(a_size[0]*0.5);float is_sdf=a_size[0]-2.0*a_size_min;highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}float h=elevation(a_pos);vec4 projectedPoint=u_matrix*vec4(a_pos,h,1);highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(\n0.5+0.5*distance_ratio,0.0,1.5);size*=perspective_ratio;float fontScale=size/24.0;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=u_matrix*vec4(a_pos+vec2(1,0),h,1);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy,h,1.0);float z=0.0;vec2 offset=rotation_matrix*(a_offset/32.0*fontScale);\n#ifdef PITCH_WITH_MAP_TERRAIN\nvec4 tile_pos=u_label_plane_matrix_inv*vec4(a_projected_pos.xy+offset,0.0,1.0);z=elevation(tile_pos.xy);\n#endif\nfloat occlusion_fade=occlusionFade(projectedPoint);gl_Position=mix(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+offset,z,1.0),AWAY,float(projectedPoint.w <=0.0 || occlusion_fade==0.0));float gamma_scale=gl_Position.w;vec2 fade_opacity=unpack_opacity(a_fade_opacity);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(occlusion_fade,fade_opacity[0]+fade_change));v_data0.xy=a_tex/u_texsize;v_data0.zw=a_tex/u_texsize_icon;v_data1=vec4(gamma_scale,size,interpolated_fade_opacity,is_sdf);}";

var skyboxFrag = "\nvarying lowp vec3 v_uv;uniform lowp samplerCube u_cubemap;uniform lowp float u_opacity;uniform highp float u_temporal_offset;uniform highp vec3 u_sun_direction;float sun_disk(highp vec3 ray_direction,highp vec3 sun_direction) {highp float cos_angle=dot(normalize(ray_direction),sun_direction);const highp float cos_sun_angular_diameter=0.99996192306;const highp float smoothstep_delta=1e-5;return smoothstep(\ncos_sun_angular_diameter-smoothstep_delta,cos_sun_angular_diameter+smoothstep_delta,cos_angle);}float map(float value,float start,float end,float new_start,float new_end) {return ((value-start)*(new_end-new_start))/(end-start)+new_start;}void main() {vec3 uv=v_uv;const float y_bias=0.015;uv.y+=y_bias;uv.y=pow(abs(uv.y),1.0/5.0);uv.y=map(uv.y,0.0,1.0,-1.0,1.0);vec3 sky_color=textureCube(u_cubemap,uv).rgb;\n#ifdef FOG\nsky_color=fog_apply_sky_gradient(v_uv.xzy,sky_color);\n#endif\nsky_color.rgb=dither(sky_color.rgb,gl_FragCoord.xy+u_temporal_offset);sky_color+=0.1*sun_disk(v_uv,u_sun_direction);gl_FragColor=vec4(sky_color*u_opacity,u_opacity);\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var skyboxGradientFrag = "varying highp vec3 v_uv;uniform lowp sampler2D u_color_ramp;uniform lowp vec3 u_center_direction;uniform lowp float u_radius;uniform lowp float u_opacity;uniform highp float u_temporal_offset;void main() {float progress=acos(dot(normalize(v_uv),u_center_direction))/u_radius;vec4 color=texture2D(u_color_ramp,vec2(progress,0.5));\n#ifdef FOG\ncolor.rgb=fog_apply_sky_gradient(v_uv.xzy,color.rgb/color.a)*color.a;\n#endif\ncolor*=u_opacity;color.rgb=dither(color.rgb,gl_FragCoord.xy+u_temporal_offset);gl_FragColor=color;\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var skyboxVert = "attribute highp vec3 a_pos_3f;uniform lowp mat4 u_matrix;varying highp vec3 v_uv;void main() {const mat3 half_neg_pi_around_x=mat3(1.0,0.0, 0.0,0.0,0.0,-1.0,0.0,1.0, 0.0);v_uv=half_neg_pi_around_x*a_pos_3f;vec4 pos=u_matrix*vec4(a_pos_3f,1.0);gl_Position=pos.xyww;}";

var terrainRasterFrag = "uniform sampler2D u_image0;varying vec2 v_pos0;\n#ifdef FOG\nvarying float v_fog_opacity;\n#endif\nvoid main() {vec4 color=texture2D(u_image0,v_pos0);\n#ifdef FOG\ncolor.rgb=fog_dither(fog_apply_from_vert(color.rgb,v_fog_opacity));\n#endif\ngl_FragColor=color;\n#ifdef TERRAIN_WIREFRAME\ngl_FragColor=vec4(1.0,0.0,0.0,0.8);\n#endif\n#ifdef OVERDRAW_INSPECTOR\ngl_FragColor=vec4(1.0);\n#endif\n}";

var terrainRasterVert = "uniform mat4 u_matrix;uniform float u_skirt_height;attribute vec2 a_pos;attribute vec2 a_texture_pos;varying vec2 v_pos0;\n#ifdef FOG\nvarying float v_fog_opacity;\n#endif\nconst float skirtOffset=24575.0;const float wireframeOffset=0.00015;void main() {v_pos0=a_texture_pos/8192.0;float skirt=float(a_pos.x >=skirtOffset);float elevation=elevation(a_texture_pos)-skirt*u_skirt_height;\n#ifdef TERRAIN_WIREFRAME\nelevation+=u_skirt_height*u_skirt_height*wireframeOffset;\n#endif\nvec2 decodedPos=a_pos-vec2(skirt*skirtOffset,0.0);gl_Position=u_matrix*vec4(decodedPos,elevation,1.0);\n#ifdef FOG\nv_fog_opacity=fog(fog_position(vec3(decodedPos,elevation)));\n#endif\n}";

var terrainDepthFrag = "#ifdef GL_ES\nprecision highp float;\n#endif\nvec4 pack_depth(float ndc_z) {float depth=ndc_z*0.5+0.5;const vec4 bit_shift=vec4(256.0*256.0*256.0,256.0*256.0,256.0,1.0);const vec4 bit_mask =vec4(0.0,1.0/256.0,1.0/256.0,1.0/256.0);vec4 res=fract(depth*bit_shift);res-=res.xxyz*bit_mask;return res;}varying float v_depth;void main() {gl_FragColor=pack_depth(v_depth);}";

var terrainDepthVert = "uniform mat4 u_matrix;attribute vec2 a_pos;attribute vec2 a_texture_pos;varying float v_depth;void main() {float elevation=elevation(a_texture_pos);gl_Position=u_matrix*vec4(a_pos,elevation,1.0);v_depth=gl_Position.z/gl_Position.w;}";

var preludeTerrainVert = "\n#define ELEVATION_SCALE 7.0\n#define ELEVATION_OFFSET 450.0\n#ifdef TERRAIN\nuniform sampler2D u_dem;uniform sampler2D u_dem_prev;uniform vec4 u_dem_unpack;uniform vec2 u_dem_tl;uniform vec2 u_dem_tl_prev;uniform float u_dem_scale;uniform float u_dem_scale_prev;uniform float u_dem_size;uniform float u_dem_lerp;uniform float u_exaggeration;uniform float u_meter_to_dem;uniform mat4 u_label_plane_matrix_inv;uniform sampler2D u_depth;uniform vec2 u_depth_size_inv;vec4 tileUvToDemSample(vec2 uv,float dem_size,float dem_scale,vec2 dem_tl) {vec2 pos=dem_size*(uv*dem_scale+dem_tl)+1.0;vec2 f=fract(pos);return vec4((pos-f+0.5)/(dem_size+2.0),f);}float decodeElevation(vec4 v) {return dot(vec4(v.xyz*255.0,-1.0),u_dem_unpack);}float currentElevation(vec2 apos) {float dd=1.0/(u_dem_size+2.0);vec4 r=tileUvToDemSample(apos/8192.0,u_dem_size,u_dem_scale,u_dem_tl);vec2 pos=r.xy;vec2 f=r.zw;float tl=decodeElevation(texture2D(u_dem,pos));\n#ifdef TERRAIN_DEM_NEAREST_FILTER\nreturn u_exaggeration*tl;\n#endif\nfloat tr=decodeElevation(texture2D(u_dem,pos+vec2(dd,0.0)));float bl=decodeElevation(texture2D(u_dem,pos+vec2(0.0,dd)));float br=decodeElevation(texture2D(u_dem,pos+vec2(dd,dd)));return u_exaggeration*mix(mix(tl,tr,f.x),mix(bl,br,f.x),f.y);}float prevElevation(vec2 apos) {float dd=1.0/(u_dem_size+2.0);vec4 r=tileUvToDemSample(apos/8192.0,u_dem_size,u_dem_scale_prev,u_dem_tl_prev);vec2 pos=r.xy;vec2 f=r.zw;float tl=decodeElevation(texture2D(u_dem_prev,pos));float tr=decodeElevation(texture2D(u_dem_prev,pos+vec2(dd,0.0)));float bl=decodeElevation(texture2D(u_dem_prev,pos+vec2(0.0,dd)));float br=decodeElevation(texture2D(u_dem_prev,pos+vec2(dd,dd)));return u_exaggeration*mix(mix(tl,tr,f.x),mix(bl,br,f.x),f.y);}\n#ifdef TERRAIN_VERTEX_MORPHING\nfloat elevation(vec2 apos) {float nextElevation=currentElevation(apos);float prevElevation=prevElevation(apos);return mix(prevElevation,nextElevation,u_dem_lerp);}\n#else\nfloat elevation(vec2 apos) {return currentElevation(apos);}\n#endif\nfloat unpack_depth(vec4 rgba_depth)\n{const vec4 bit_shift=vec4(1.0/(256.0*256.0*256.0),1.0/(256.0*256.0),1.0/256.0,1.0);return dot(rgba_depth,bit_shift)*2.0-1.0;}bool isOccluded(vec4 frag) {vec3 coord=frag.xyz/frag.w;float depth=unpack_depth(texture2D(u_depth,(coord.xy+1.0)*0.5));return coord.z > depth+0.0005;}float occlusionFade(vec4 frag) {vec3 coord=frag.xyz/frag.w;vec3 df=vec3(5.0*u_depth_size_inv,0.0);vec2 uv=0.5*coord.xy+0.5;vec4 depth=vec4(\nunpack_depth(texture2D(u_depth,uv-df.xz)),unpack_depth(texture2D(u_depth,uv+df.xz)),unpack_depth(texture2D(u_depth,uv-df.zy)),unpack_depth(texture2D(u_depth,uv+df.zy))\n);return dot(vec4(0.25),vec4(1.0)-clamp(300.0*(vec4(coord.z-0.001)-depth),0.0,1.0));}vec4 fourSample(vec2 pos,vec2 off) {vec4 demtl=vec4(texture2D(u_dem,pos).xyz*255.0,-1.0);float tl=dot(demtl,u_dem_unpack);vec4 demtr=vec4(texture2D(u_dem,pos+vec2(off.x,0.0)).xyz*255.0,-1.0);float tr=dot(demtr,u_dem_unpack);vec4 dembl=vec4(texture2D(u_dem,pos+vec2(0.0,off.y)).xyz*255.0,-1.0);float bl=dot(dembl,u_dem_unpack);vec4 dembr=vec4(texture2D(u_dem,pos+off).xyz*255.0,-1.0);float br=dot(dembr,u_dem_unpack);return vec4(tl,tr,bl,br);}float flatElevation(vec2 pack) {vec2 apos=floor(pack/8.0);vec2 span=10.0*(pack-apos*8.0);vec2 uvTex=(apos-vec2(1.0,1.0))/8190.0;float size=u_dem_size+2.0;float dd=1.0/size;vec2 pos=u_dem_size*(uvTex*u_dem_scale+u_dem_tl)+1.0;vec2 f=fract(pos);pos=(pos-f+0.5)*dd;vec4 h=fourSample(pos,vec2(dd));float z=mix(mix(h.x,h.y,f.x),mix(h.z,h.w,f.x),f.y);vec2 w=floor(0.5*(span*u_meter_to_dem-1.0));vec2 d=dd*w;vec4 bounds=vec4(d,vec2(1.0)-d);h=fourSample(pos-d,2.0*d+vec2(dd));vec4 diff=abs(h.xzxy-h.ywzw);vec2 slope=min(vec2(0.25),u_meter_to_dem*0.5*(diff.xz+diff.yw)/(2.0*w+vec2(1.0)));vec2 fix=slope*span;float base=z+max(fix.x,fix.y);return u_exaggeration*base;}float elevationFromUint16(float word) {return u_exaggeration*(word/ELEVATION_SCALE-ELEVATION_OFFSET);}\n#else\nfloat elevation(vec2 pos) { return 0.0; }bool isOccluded(vec4 frag) { return false; }float occlusionFade(vec4 frag) { return 1.0; }\n#endif";

var preludeFogVert = "#ifdef FOG\nuniform mat4 u_fog_matrix;vec3 fog_position(vec3 pos) {return (u_fog_matrix*vec4(pos,1.0)).xyz;}vec3 fog_position(vec2 pos) {return fog_position(vec3(pos,0.0));}float fog(vec3 pos) {float depth=length(pos);float opacity=fog_opacity(fog_range(depth));return opacity*fog_horizon_blending(pos/depth);}\n#endif";

var preludeFogFrag = "#ifdef FOG\nuniform float u_fog_temporal_offset;float fog_opacity(vec3 pos) {float depth=length(pos);return fog_opacity(fog_range(depth));}vec3 fog_apply(vec3 color,vec3 pos) {float depth=length(pos);float opacity=fog_opacity(fog_range(depth));opacity*=fog_horizon_blending(pos/depth);return mix(color,u_fog_color.rgb,opacity);}vec3 fog_apply_from_vert(vec3 color,float fog_opac) {return mix(color,u_fog_color.rgb,fog_opac);}vec3 fog_apply_sky_gradient(vec3 camera_ray,vec3 sky_color) {float horizon_blend=fog_horizon_blending(normalize(camera_ray));return mix(sky_color,u_fog_color.rgb,horizon_blend);}vec4 fog_apply_premultiplied(vec4 color,vec3 pos) {float alpha=EPSILON+color.a;color.rgb/=alpha;color.rgb=fog_apply(color.rgb,pos);color.rgb*=alpha;return color;}vec3 fog_dither(vec3 color) {vec2 dither_seed=gl_FragCoord.xy+u_fog_temporal_offset;return dither(color,dither_seed);}vec4 fog_dither(vec4 color) {return vec4(fog_dither(color.rgb),color.a);}\n#endif";

var skyboxCaptureFrag = "\nvarying highp vec3 v_position;uniform highp float u_sun_intensity;uniform highp float u_luminance;uniform lowp vec3 u_sun_direction;uniform highp vec4 u_color_tint_r;uniform highp vec4 u_color_tint_m;\n#ifdef GL_ES\nprecision highp float;\n#endif\n#define BETA_R                  vec3(5.5e-6,13.0e-6,22.4e-6)\n#define BETA_M                  vec3(21e-6,21e-6,21e-6)\n#define MIE_G                   0.76\n#define DENSITY_HEIGHT_SCALE_R  8000.0\n#define DENSITY_HEIGHT_SCALE_M  1200.0\n#define PLANET_RADIUS           6360e3\n#define ATMOSPHERE_RADIUS       6420e3\n#define SAMPLE_STEPS            10\n#define DENSITY_STEPS           4\nfloat ray_sphere_exit(vec3 orig,vec3 dir,float radius) {float a=dot(dir,dir);float b=2.0*dot(dir,orig);float c=dot(orig,orig)-radius*radius;float d=sqrt(b*b-4.0*a*c);return (-b+d)/(2.0*a);}vec3 extinction(vec2 density) {return exp(-vec3(BETA_R*u_color_tint_r.a*density.x+BETA_M*u_color_tint_m.a*density.y));}vec2 local_density(vec3 point) {float height=max(length(point)-PLANET_RADIUS,0.0);float exp_r=exp(-height/DENSITY_HEIGHT_SCALE_R);float exp_m=exp(-height/DENSITY_HEIGHT_SCALE_M);return vec2(exp_r,exp_m);}float phase_ray(float cos_angle) {return (3.0/(16.0*PI))*(1.0+cos_angle*cos_angle);}float phase_mie(float cos_angle) {return (3.0/(8.0*PI))*((1.0-MIE_G*MIE_G)*(1.0+cos_angle*cos_angle))/((2.0+MIE_G*MIE_G)*pow(1.0+MIE_G*MIE_G-2.0*MIE_G*cos_angle,1.5));}vec2 density_to_atmosphere(vec3 point,vec3 light_dir) {float ray_len=ray_sphere_exit(point,light_dir,ATMOSPHERE_RADIUS);float step_len=ray_len/float(DENSITY_STEPS);vec2 density_point_to_atmosphere=vec2(0.0);for (int i=0; i < DENSITY_STEPS;++i) {vec3 point_on_ray=point+light_dir*((float(i)+0.5)*step_len);density_point_to_atmosphere+=local_density(point_on_ray)*step_len;;}return density_point_to_atmosphere;}vec3 atmosphere(vec3 ray_dir,vec3 sun_direction,float sun_intensity) {vec2 density_orig_to_point=vec2(0.0);vec3 scatter_r=vec3(0.0);vec3 scatter_m=vec3(0.0);vec3 origin=vec3(0.0,PLANET_RADIUS,0.0);float ray_len=ray_sphere_exit(origin,ray_dir,ATMOSPHERE_RADIUS);float step_len=ray_len/float(SAMPLE_STEPS);for (int i=0; i < SAMPLE_STEPS;++i) {vec3 point_on_ray=origin+ray_dir*((float(i)+0.5)*step_len);vec2 density=local_density(point_on_ray)*step_len;density_orig_to_point+=density;vec2 density_point_to_atmosphere=density_to_atmosphere(point_on_ray,sun_direction);vec2 density_orig_to_atmosphere=density_orig_to_point+density_point_to_atmosphere;vec3 extinction=extinction(density_orig_to_atmosphere);scatter_r+=density.x*extinction;scatter_m+=density.y*extinction;}float cos_angle=dot(ray_dir,sun_direction);float phase_r=phase_ray(cos_angle);float phase_m=phase_mie(cos_angle);vec3 beta_r=BETA_R*u_color_tint_r.rgb*u_color_tint_r.a;vec3 beta_m=BETA_M*u_color_tint_m.rgb*u_color_tint_m.a;return (scatter_r*phase_r*beta_r+scatter_m*phase_m*beta_m)*sun_intensity;}const float A=0.15;const float B=0.50;const float C=0.10;const float D=0.20;const float E=0.02;const float F=0.30;vec3 uncharted2_tonemap(vec3 x) {return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;}void main() {vec3 ray_direction=v_position;ray_direction.y=pow(ray_direction.y,5.0);const float y_bias=0.015;ray_direction.y+=y_bias;vec3 color=atmosphere(normalize(ray_direction),u_sun_direction,u_sun_intensity);float white_scale=1.0748724675633854;color=uncharted2_tonemap((log2(2.0/pow(u_luminance,4.0)))*color)*white_scale;gl_FragColor=vec4(color,1.0);}";

var skyboxCaptureVert = "attribute highp vec3 a_pos_3f;uniform mat3 u_matrix_3f;varying highp vec3 v_position;float map(float value,float start,float end,float new_start,float new_end) {return ((value-start)*(new_end-new_start))/(end-start)+new_start;}void main() {vec4 pos=vec4(u_matrix_3f*a_pos_3f,1.0);v_position=pos.xyz;v_position.y*=-1.0;v_position.y=map(v_position.y,-1.0,1.0,0.0,1.0);gl_Position=vec4(a_pos_3f.xy,0.0,1.0);}";

let preludeTerrain = {};
let preludeFog = {};
preludeTerrain = compile('', preludeTerrainVert, true);
preludeFog = compile(preludeFogFrag, preludeFogVert, true);
const prelude = compile(preludeFrag, preludeVert);
const preludeCommonSource = preludeCommon;
var shaders = {
    background: compile(backgroundFrag, backgroundVert),
    backgroundPattern: compile(backgroundPatternFrag, backgroundPatternVert),
    circle: compile(circleFrag, circleVert),
    clippingMask: compile(clippingMaskFrag, clippingMaskVert),
    heatmap: compile(heatmapFrag, heatmapVert),
    heatmapTexture: compile(heatmapTextureFrag, heatmapTextureVert),
    collisionBox: compile(collisionBoxFrag, collisionBoxVert),
    collisionCircle: compile(collisionCircleFrag, collisionCircleVert),
    debug: compile(debugFrag, debugVert),
    fill: compile(fillFrag, fillVert),
    fillOutline: compile(fillOutlineFrag, fillOutlineVert),
    fillOutlinePattern: compile(fillOutlinePatternFrag, fillOutlinePatternVert),
    fillPattern: compile(fillPatternFrag, fillPatternVert),
    fillExtrusion: compile(fillExtrusionFrag, fillExtrusionVert),
    fillExtrusionPattern: compile(fillExtrusionPatternFrag, fillExtrusionPatternVert),
    hillshadePrepare: compile(hillshadePrepareFrag, hillshadePrepareVert),
    hillshade: compile(hillshadeFrag, hillshadeVert),
    line: compile(lineFrag, lineVert),
    lineGradient: compile(lineGradientFrag, lineGradientVert),
    linePattern: compile(linePatternFrag, linePatternVert),
    lineSDF: compile(lineSDFFrag, lineSDFVert),
    raster: compile(rasterFrag, rasterVert),
    symbolIcon: compile(symbolIconFrag, symbolIconVert),
    symbolSDF: compile(symbolSDFFrag, symbolSDFVert),
    symbolTextAndIcon: compile(symbolTextAndIconFrag, symbolTextAndIconVert),
    terrainRaster: compile(terrainRasterFrag, terrainRasterVert),
    terrainDepth: compile(terrainDepthFrag, terrainDepthVert),
    skybox: compile(skyboxFrag, skyboxVert),
    skyboxGradient: compile(skyboxGradientFrag, skyboxVert),
    skyboxCapture: compile(skyboxCaptureFrag, skyboxCaptureVert)
};
function compile(fragmentSource, vertexSource, isGlobalPrelude) {
    const pragmaRegex = /#pragma mapbox: ([\w]+) ([\w]+) ([\w]+) ([\w]+)/g;
    const uniformRegex = /uniform (highp |mediump |lowp )?([\w]+) ([\w]+)([\s]*)([\w]*)/g;
    const attributeRegex = /attribute (highp |mediump |lowp )?([\w]+) ([\w]+)/g;
    const staticAttributes = vertexSource.match(attributeRegex);
    const fragmentUniforms = fragmentSource.match(uniformRegex);
    const vertexUniforms = vertexSource.match(uniformRegex);
    const commonUniforms = preludeCommon.match(uniformRegex);
    let staticUniforms = vertexUniforms ? vertexUniforms.concat(fragmentUniforms) : fragmentUniforms;
    if (!isGlobalPrelude) {
        if (preludeTerrain.staticUniforms) {
            staticUniforms = preludeTerrain.staticUniforms.concat(staticUniforms);
        }
        if (preludeFog.staticUniforms) {
            staticUniforms = preludeFog.staticUniforms.concat(staticUniforms);
        }
    }
    if (staticUniforms) {
        staticUniforms = staticUniforms.concat(commonUniforms);
    }
    const fragmentPragmas = {};
    fragmentSource = fragmentSource.replace(pragmaRegex, (match, operation, precision, type, name) => {
        fragmentPragmas[name] = true;
        if (operation === 'define') {
            return `
#ifndef HAS_UNIFORM_u_${ name }
varying ${ precision } ${ type } ${ name };
#else
uniform ${ precision } ${ type } u_${ name };
#endif
`;
        } else {
            return `
#ifdef HAS_UNIFORM_u_${ name }
    ${ precision } ${ type } ${ name } = u_${ name };
#endif
`;
        }
    });
    vertexSource = vertexSource.replace(pragmaRegex, (match, operation, precision, type, name) => {
        const attrType = type === 'float' ? 'vec2' : 'vec4';
        const unpackType = name.match(/color/) ? 'color' : attrType;
        if (fragmentPragmas[name]) {
            if (operation === 'define') {
                return `
#ifndef HAS_UNIFORM_u_${ name }
uniform lowp float u_${ name }_t;
attribute ${ precision } ${ attrType } a_${ name };
varying ${ precision } ${ type } ${ name };
#else
uniform ${ precision } ${ type } u_${ name };
#endif
`;
            } else {
                if (unpackType === 'vec4') {
                    return `
#ifndef HAS_UNIFORM_u_${ name }
    ${ name } = a_${ name };
#else
    ${ precision } ${ type } ${ name } = u_${ name };
#endif
`;
                } else {
                    return `
#ifndef HAS_UNIFORM_u_${ name }
    ${ name } = unpack_mix_${ unpackType }(a_${ name }, u_${ name }_t);
#else
    ${ precision } ${ type } ${ name } = u_${ name };
#endif
`;
                }
            }
        } else {
            if (operation === 'define') {
                return `
#ifndef HAS_UNIFORM_u_${ name }
uniform lowp float u_${ name }_t;
attribute ${ precision } ${ attrType } a_${ name };
#else
uniform ${ precision } ${ type } u_${ name };
#endif
`;
            } else {
                if (unpackType === 'vec4') {
                    return `
#ifndef HAS_UNIFORM_u_${ name }
    ${ precision } ${ type } ${ name } = a_${ name };
#else
    ${ precision } ${ type } ${ name } = u_${ name };
#endif
`;
                } else {
                    return `
#ifndef HAS_UNIFORM_u_${ name }
    ${ precision } ${ type } ${ name } = unpack_mix_${ unpackType }(a_${ name }, u_${ name }_t);
#else
    ${ precision } ${ type } ${ name } = u_${ name };
#endif
`;
                }
            }
        }
    });
    return {
        fragmentSource,
        vertexSource,
        staticAttributes,
        staticUniforms
    };
}

class VertexArrayObject {
    constructor() {
        this.boundProgram = null;
        this.boundLayoutVertexBuffer = null;
        this.boundPaintVertexBuffers = [];
        this.boundIndexBuffer = null;
        this.boundVertexOffset = null;
        this.boundDynamicVertexBuffer = null;
        this.vao = null;
    }
    bind(context, program, layoutVertexBuffer, paintVertexBuffers, indexBuffer, vertexOffset, dynamicVertexBuffer, dynamicVertexBuffer2) {
        this.context = context;
        let paintBuffersDiffer = this.boundPaintVertexBuffers.length !== paintVertexBuffers.length;
        for (let i = 0; !paintBuffersDiffer && i < paintVertexBuffers.length; i++) {
            if (this.boundPaintVertexBuffers[i] !== paintVertexBuffers[i]) {
                paintBuffersDiffer = true;
            }
        }
        const isFreshBindRequired = !this.vao || this.boundProgram !== program || this.boundLayoutVertexBuffer !== layoutVertexBuffer || paintBuffersDiffer || this.boundIndexBuffer !== indexBuffer || this.boundVertexOffset !== vertexOffset || this.boundDynamicVertexBuffer !== dynamicVertexBuffer || this.boundDynamicVertexBuffer2 !== dynamicVertexBuffer2;
        if (!context.extVertexArrayObject || isFreshBindRequired) {
            this.freshBind(program, layoutVertexBuffer, paintVertexBuffers, indexBuffer, vertexOffset, dynamicVertexBuffer, dynamicVertexBuffer2);
        } else {
            context.bindVertexArrayOES.set(this.vao);
            if (dynamicVertexBuffer) {
                dynamicVertexBuffer.bind();
            }
            if (indexBuffer && indexBuffer.dynamicDraw) {
                indexBuffer.bind();
            }
            if (dynamicVertexBuffer2) {
                dynamicVertexBuffer2.bind();
            }
        }
    }
    freshBind(program, layoutVertexBuffer, paintVertexBuffers, indexBuffer, vertexOffset, dynamicVertexBuffer, dynamicVertexBuffer2) {
        let numPrevAttributes;
        const numNextAttributes = program.numAttributes;
        const context = this.context;
        const gl = context.gl;
        if (context.extVertexArrayObject) {
            if (this.vao)
                this.destroy();
            this.vao = context.extVertexArrayObject.createVertexArrayOES();
            context.bindVertexArrayOES.set(this.vao);
            numPrevAttributes = 0;
            this.boundProgram = program;
            this.boundLayoutVertexBuffer = layoutVertexBuffer;
            this.boundPaintVertexBuffers = paintVertexBuffers;
            this.boundIndexBuffer = indexBuffer;
            this.boundVertexOffset = vertexOffset;
            this.boundDynamicVertexBuffer = dynamicVertexBuffer;
            this.boundDynamicVertexBuffer2 = dynamicVertexBuffer2;
        } else {
            numPrevAttributes = context.currentNumAttributes || 0;
            for (let i = numNextAttributes; i < numPrevAttributes; i++) {
                gl.disableVertexAttribArray(i);
            }
        }
        layoutVertexBuffer.enableAttributes(gl, program);
        for (const vertexBuffer of paintVertexBuffers) {
            vertexBuffer.enableAttributes(gl, program);
        }
        if (dynamicVertexBuffer) {
            dynamicVertexBuffer.enableAttributes(gl, program);
        }
        if (dynamicVertexBuffer2) {
            dynamicVertexBuffer2.enableAttributes(gl, program);
        }
        layoutVertexBuffer.bind();
        layoutVertexBuffer.setVertexAttribPointers(gl, program, vertexOffset);
        for (const vertexBuffer of paintVertexBuffers) {
            vertexBuffer.bind();
            vertexBuffer.setVertexAttribPointers(gl, program, vertexOffset);
        }
        if (dynamicVertexBuffer) {
            dynamicVertexBuffer.bind();
            dynamicVertexBuffer.setVertexAttribPointers(gl, program, vertexOffset);
        }
        if (indexBuffer) {
            indexBuffer.bind();
        }
        if (dynamicVertexBuffer2) {
            dynamicVertexBuffer2.bind();
            dynamicVertexBuffer2.setVertexAttribPointers(gl, program, vertexOffset);
        }
        context.currentNumAttributes = numNextAttributes;
    }
    destroy() {
        if (this.vao) {
            this.context.extVertexArrayObject.deleteVertexArrayOES(this.vao);
            this.vao = null;
        }
    }
}

const hillshadeUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_image': new ref_properties.Uniform1i(context, locations.u_image),
    'u_latrange': new ref_properties.Uniform2f(context, locations.u_latrange),
    'u_light': new ref_properties.Uniform2f(context, locations.u_light),
    'u_shadow': new ref_properties.UniformColor(context, locations.u_shadow),
    'u_highlight': new ref_properties.UniformColor(context, locations.u_highlight),
    'u_accent': new ref_properties.UniformColor(context, locations.u_accent)
});
const hillshadePrepareUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_image': new ref_properties.Uniform1i(context, locations.u_image),
    'u_dimension': new ref_properties.Uniform2f(context, locations.u_dimension),
    'u_zoom': new ref_properties.Uniform1f(context, locations.u_zoom),
    'u_unpack': new ref_properties.Uniform4f(context, locations.u_unpack)
});
const hillshadeUniformValues = (painter, tile, layer, matrix) => {
    const shadow = layer.paint.get('hillshade-shadow-color');
    const highlight = layer.paint.get('hillshade-highlight-color');
    const accent = layer.paint.get('hillshade-accent-color');
    let azimuthal = layer.paint.get('hillshade-illumination-direction') * (Math.PI / 180);
    if (layer.paint.get('hillshade-illumination-anchor') === 'viewport') {
        azimuthal -= painter.transform.angle;
    }
    const align = !painter.options.moving;
    return {
        'u_matrix': matrix ? matrix : painter.transform.calculateProjMatrix(tile.tileID.toUnwrapped(), align),
        'u_image': 0,
        'u_latrange': getTileLatRange(painter, tile.tileID),
        'u_light': [
            layer.paint.get('hillshade-exaggeration'),
            azimuthal
        ],
        'u_shadow': shadow,
        'u_highlight': highlight,
        'u_accent': accent
    };
};
const hillshadeUniformPrepareValues = (tileID, dem) => {
    const stride = dem.stride;
    const matrix = ref_properties.create$1();
    ref_properties.ortho(matrix, 0, ref_properties.EXTENT, -ref_properties.EXTENT, 0, 0, 1);
    ref_properties.translate(matrix, matrix, [
        0,
        -ref_properties.EXTENT,
        0
    ]);
    return {
        'u_matrix': matrix,
        'u_image': 1,
        'u_dimension': [
            stride,
            stride
        ],
        'u_zoom': tileID.overscaledZ,
        'u_unpack': dem.unpackVector
    };
};
function getTileLatRange(painter, tileID) {
    const tilesAtZoom = Math.pow(2, tileID.canonical.z);
    const y = tileID.canonical.y;
    return [
        new ref_properties.MercatorCoordinate(0, y / tilesAtZoom).toLngLat().lat,
        new ref_properties.MercatorCoordinate(0, (y + 1) / tilesAtZoom).toLngLat().lat
    ];
}

function drawHillshade(painter, sourceCache, layer, tileIDs) {
    if (painter.renderPass !== 'offscreen' && painter.renderPass !== 'translucent')
        return;
    const context = painter.context;
    const depthMode = painter.depthModeForSublayer(0, ref_properties.DepthMode.ReadOnly);
    const colorMode = painter.colorModeForRenderPass();
    const renderingToTexture = painter.terrain && painter.terrain.renderingToTexture;
    const [stencilModes, coords] = painter.renderPass === 'translucent' && !renderingToTexture ? painter.stencilConfigForOverlap(tileIDs) : [
        {},
        tileIDs
    ];
    for (const coord of coords) {
        const tile = sourceCache.getTile(coord);
        if (tile.needsHillshadePrepare && painter.renderPass === 'offscreen') {
            prepareHillshade(painter, tile, layer, depthMode, ref_properties.StencilMode.disabled, colorMode);
        } else if (painter.renderPass === 'translucent') {
            const stencilMode = renderingToTexture && painter.terrain ? painter.terrain.stencilModeForRTTOverlap(coord) : stencilModes[coord.overscaledZ];
            renderHillshade(painter, coord, tile, layer, depthMode, stencilMode, colorMode);
        }
    }
    context.viewport.set([
        0,
        0,
        painter.width,
        painter.height
    ]);
}
function renderHillshade(painter, coord, tile, layer, depthMode, stencilMode, colorMode) {
    const context = painter.context;
    const gl = context.gl;
    const fbo = tile.fbo;
    if (!fbo)
        return;
    painter.prepareDrawTile(coord);
    const program = painter.useProgram('hillshade');
    context.activeTexture.set(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
    const uniformValues = hillshadeUniformValues(painter, tile, layer, painter.terrain ? coord.projMatrix : null);
    painter.prepareDrawProgram(context, program, coord.toUnwrapped());
    program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, ref_properties.CullFaceMode.disabled, uniformValues, layer.id, painter.rasterBoundsBuffer, painter.quadTriangleIndexBuffer, painter.rasterBoundsSegments);
}
function prepareDEMTexture(painter, tile, dem) {
    if (!tile.needsDEMTextureUpload)
        return;
    const context = painter.context;
    const gl = context.gl;
    context.pixelStoreUnpackPremultiplyAlpha.set(false);
    const textureStride = dem.stride;
    tile.demTexture = tile.demTexture || painter.getTileTexture(textureStride);
    const pixelData = dem.getPixels();
    if (tile.demTexture) {
        tile.demTexture.update(pixelData, { premultiply: false });
    } else {
        tile.demTexture = new ref_properties.Texture(context, pixelData, gl.RGBA, { premultiply: false });
    }
    tile.needsDEMTextureUpload = false;
}
function prepareHillshade(painter, tile, layer, depthMode, stencilMode, colorMode) {
    const context = painter.context;
    const gl = context.gl;
    if (!tile.dem)
        return;
    const dem = tile.dem;
    context.activeTexture.set(gl.TEXTURE1);
    prepareDEMTexture(painter, tile, dem);
    if (!tile.demTexture)
        return;
    tile.demTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE);
    const tileSize = dem.dim;
    context.activeTexture.set(gl.TEXTURE0);
    let fbo = tile.fbo;
    if (!fbo) {
        const renderTexture = new ref_properties.Texture(context, {
            width: tileSize,
            height: tileSize,
            data: null
        }, gl.RGBA);
        renderTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
        fbo = tile.fbo = context.createFramebuffer(tileSize, tileSize, true);
        fbo.colorAttachment.set(renderTexture.texture);
    }
    context.bindFramebuffer.set(fbo.framebuffer);
    context.viewport.set([
        0,
        0,
        tileSize,
        tileSize
    ]);
    painter.useProgram('hillshadePrepare').draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, ref_properties.CullFaceMode.disabled, hillshadeUniformPrepareValues(tile.tileID, dem), layer.id, painter.rasterBoundsBuffer, painter.quadTriangleIndexBuffer, painter.rasterBoundsSegments);
    tile.needsHillshadePrepare = false;
}

const terrainRasterUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_image0': new ref_properties.Uniform1i(context, locations.u_image0),
    'u_skirt_height': new ref_properties.Uniform1f(context, locations.u_skirt_height)
});
const terrainRasterUniformValues = (matrix, skirtHeight) => ({
    'u_matrix': matrix,
    'u_image0': 0,
    'u_skirt_height': skirtHeight
});

class VertexMorphing {
    constructor() {
        this.operations = {};
    }
    newMorphing(key, from, to, now, duration) {
        if (key in this.operations) {
            const op = this.operations[key];
            if (op.to.tileID.key !== to.tileID.key)
                op.queued = to;
        } else {
            this.operations[key] = {
                startTime: now,
                phase: 0,
                duration,
                from,
                to,
                queued: null
            };
        }
    }
    getMorphValuesForProxy(key) {
        if (!(key in this.operations))
            return null;
        const op = this.operations[key];
        const from = op.from;
        const to = op.to;
        return {
            from,
            to,
            phase: op.phase
        };
    }
    update(now) {
        for (const key in this.operations) {
            const op = this.operations[key];
            op.phase = (now - op.startTime) / op.duration;
            while (op.phase >= 1 || !this._validOp(op)) {
                if (!this._nextOp(op, now)) {
                    delete this.operations[key];
                    break;
                }
            }
        }
    }
    _nextOp(op, now) {
        if (!op.queued)
            return false;
        op.from = op.to;
        op.to = op.queued;
        op.queued = null;
        op.phase = 0;
        op.startTime = now;
        return true;
    }
    _validOp(op) {
        return op.from.hasData() && op.to.hasData();
    }
}
function demTileChanged(prev, next) {
    if (prev == null || next == null)
        return false;
    if (!prev.hasData() || !next.hasData())
        return false;
    if (prev.demTexture == null || next.demTexture == null)
        return false;
    return prev.tileID.key !== next.tileID.key;
}
const vertexMorphing = new VertexMorphing();
const SHADER_DEFAULT = 0;
const SHADER_MORPHING = 1;
const SHADER_TERRAIN_WIREFRAME = 2;
const defaultDuration = 250;
const shaderDefines = {
    '0': null,
    '1': 'TERRAIN_VERTEX_MORPHING',
    '2': 'TERRAIN_WIREFRAME'
};
function drawTerrainRaster(painter, terrain, sourceCache, tileIDs, now) {
    const context = painter.context;
    const gl = context.gl;
    let program, programMode;
    const showWireframe = painter.options.showTerrainWireframe ? SHADER_TERRAIN_WIREFRAME : SHADER_DEFAULT;
    const setShaderMode = (mode, isWireframe) => {
        if (programMode === mode)
            return;
        const modes = [shaderDefines[mode]];
        if (isWireframe)
            modes.push(shaderDefines[showWireframe]);
        program = painter.useProgram('terrainRaster', null, modes);
        programMode = mode;
    };
    const colorMode = painter.colorModeForRenderPass();
    const depthMode = new ref_properties.DepthMode(gl.LEQUAL, ref_properties.DepthMode.ReadWrite, painter.depthRangeFor3D);
    vertexMorphing.update(now);
    const tr = painter.transform;
    const skirt = skirtHeight(tr.zoom) * terrain.exaggeration();
    const batches = showWireframe ? [
        false,
        true
    ] : [false];
    batches.forEach(isWireframe => {
        programMode = -1;
        const primitive = isWireframe ? gl.LINES : gl.TRIANGLES;
        const [buffer, segments] = isWireframe ? terrain.getWirefameBuffer() : [
            terrain.gridIndexBuffer,
            terrain.gridSegments
        ];
        for (const coord of tileIDs) {
            const tile = sourceCache.getTile(coord);
            const stencilMode = ref_properties.StencilMode.disabled;
            const prevDemTile = terrain.prevTerrainTileForTile[coord.key];
            const nextDemTile = terrain.terrainTileForTile[coord.key];
            if (demTileChanged(prevDemTile, nextDemTile)) {
                vertexMorphing.newMorphing(coord.key, prevDemTile, nextDemTile, now, defaultDuration);
            }
            context.activeTexture.set(gl.TEXTURE0);
            tile.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
            const morph = vertexMorphing.getMorphValuesForProxy(coord.key);
            const shaderMode = morph ? SHADER_MORPHING : SHADER_DEFAULT;
            let elevationOptions;
            if (morph) {
                elevationOptions = {
                    morphing: {
                        srcDemTile: morph.from,
                        dstDemTile: morph.to,
                        phase: ref_properties.easeCubicInOut(morph.phase)
                    }
                };
            }
            const uniformValues = terrainRasterUniformValues(coord.projMatrix, isEdgeTile(coord.canonical, tr.renderWorldCopies) ? skirt / 10 : skirt);
            setShaderMode(shaderMode, isWireframe);
            terrain.setupElevationDraw(tile, program, elevationOptions);
            painter.prepareDrawProgram(context, program, coord.toUnwrapped());
            program.draw(context, primitive, depthMode, stencilMode, colorMode, ref_properties.CullFaceMode.backCCW, uniformValues, 'terrain_raster', terrain.gridBuffer, buffer, segments);
        }
    });
}
function drawTerrainDepth(painter, terrain, sourceCache, tileIDs) {
    const context = painter.context;
    const gl = context.gl;
    context.clear({ depth: 1 });
    const program = painter.useProgram('terrainDepth');
    const depthMode = new ref_properties.DepthMode(gl.LESS, ref_properties.DepthMode.ReadWrite, painter.depthRangeFor3D);
    for (const coord of tileIDs) {
        const tile = sourceCache.getTile(coord);
        const uniformValues = terrainRasterUniformValues(coord.projMatrix, 0);
        terrain.setupElevationDraw(tile, program);
        program.draw(context, gl.TRIANGLES, depthMode, ref_properties.StencilMode.disabled, ref_properties.ColorMode.unblended, ref_properties.CullFaceMode.backCCW, uniformValues, 'terrain_depth', terrain.gridBuffer, terrain.gridIndexBuffer, terrain.gridNoSkirtSegments);
    }
}
function skirtHeight(zoom) {
    return 6 * Math.pow(1.5, 22 - zoom);
}
function isEdgeTile(cid, renderWorldCopies) {
    const numTiles = 1 << cid.z;
    return !renderWorldCopies && (cid.x === 0 || cid.x === numTiles - 1) || cid.y === 0 || cid.y === numTiles - 1;
}

const clippingMaskUniforms = (context, locations) => ({ 'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix) });
const clippingMaskUniformValues = matrix => ({ 'u_matrix': matrix });

function rasterFade(tile, parentTile, sourceCache, transform, fadeDuration) {
    if (fadeDuration > 0) {
        const now = ref_properties.exported.now();
        const sinceTile = (now - tile.timeAdded) / fadeDuration;
        const sinceParent = parentTile ? (now - parentTile.timeAdded) / fadeDuration : -1;
        const source = sourceCache.getSource();
        const idealZ = transform.coveringZoomLevel({
            tileSize: source.tileSize,
            roundZoom: source.roundZoom
        });
        const fadeIn = !parentTile || Math.abs(parentTile.tileID.overscaledZ - idealZ) > Math.abs(tile.tileID.overscaledZ - idealZ);
        const childOpacity = fadeIn && tile.refreshedUponExpiration ? 1 : ref_properties.clamp(fadeIn ? sinceTile : 1 - sinceParent, 0, 1);
        if (tile.refreshedUponExpiration && sinceTile >= 1)
            tile.refreshedUponExpiration = false;
        if (parentTile) {
            return {
                opacity: 1,
                mix: 1 - childOpacity
            };
        } else {
            return {
                opacity: childOpacity,
                mix: 0
            };
        }
    } else {
        return {
            opacity: 1,
            mix: 0
        };
    }
}

const GRID_DIM = 128;
const FBO_POOL_SIZE = 5;
const RENDER_CACHE_MAX_SIZE = 50;
class ProxySourceCache extends ref_properties.SourceCache {
    constructor(map) {
        const source = create('proxy', {
            type: 'geojson',
            maxzoom: map.transform.maxZoom
        }, new Dispatcher(getGlobalWorkerPool(), null), map.style);
        super('proxy', source, false);
        source.setEventedParent(this);
        this.map = this.getSource().map = map;
        this.used = this._sourceLoaded = true;
        this.renderCache = [];
        this.renderCachePool = [];
        this.proxyCachedFBO = {};
    }
    update(transform, tileSize, updateForTerrain) {
        if (transform.freezeTileCoverage) {
            return;
        }
        this.transform = transform;
        const idealTileIDs = transform.coveringTiles({
            tileSize: this._source.tileSize,
            minzoom: this._source.minzoom,
            maxzoom: this._source.maxzoom,
            roundZoom: this._source.roundZoom,
            reparseOverscaled: this._source.reparseOverscaled,
            useElevationData: true
        });
        const incoming = idealTileIDs.reduce((acc, tileID) => {
            acc[tileID.key] = '';
            if (!this._tiles[tileID.key]) {
                const tile = new ref_properties.Tile(tileID, this._source.tileSize * tileID.overscaleFactor(), transform.tileZoom);
                tile.state = 'loaded';
                this._tiles[tileID.key] = tile;
            }
            return acc;
        }, {});
        for (const id in this._tiles) {
            if (!(id in incoming)) {
                this.freeFBO(id);
                this._tiles[id].state = 'unloaded';
                delete this._tiles[id];
            }
        }
    }
    freeFBO(id) {
        const fbos = this.proxyCachedFBO[id];
        if (fbos !== undefined) {
            const fboIds = Object.values(fbos);
            this.renderCachePool.push(...fboIds);
            delete this.proxyCachedFBO[id];
        }
    }
    deallocRenderCache() {
        this.renderCache.forEach(fbo => fbo.fb.destroy());
        this.renderCache = [];
        this.renderCachePool = [];
        this.proxyCachedFBO = {};
    }
}
class ProxiedTileID extends ref_properties.OverscaledTileID {
    constructor(tileID, proxyTileKey, projMatrix) {
        super(tileID.overscaledZ, tileID.wrap, tileID.canonical.z, tileID.canonical.x, tileID.canonical.y);
        this.proxyTileKey = proxyTileKey;
        this.projMatrix = projMatrix;
    }
}
class Terrain extends ref_properties.Elevation {
    constructor(painter, style) {
        super();
        this.painter = painter;
        this.terrainTileForTile = {};
        this.prevTerrainTileForTile = {};
        const [triangleGridArray, triangleGridIndices, skirtIndicesOffset] = createGrid(GRID_DIM + 1);
        const context = painter.context;
        this.gridBuffer = context.createVertexBuffer(triangleGridArray, rasterBoundsAttributes.members);
        this.gridIndexBuffer = context.createIndexBuffer(triangleGridIndices);
        this.gridSegments = ref_properties.SegmentVector.simpleSegment(0, 0, triangleGridArray.length, triangleGridIndices.length);
        this.gridNoSkirtSegments = ref_properties.SegmentVector.simpleSegment(0, 0, triangleGridArray.length, skirtIndicesOffset);
        this.proxyCoords = [];
        this.proxiedCoords = {};
        this._visibleDemTiles = [];
        this._drapedRenderBatches = [];
        this._sourceTilesOverlap = {};
        this.proxySourceCache = new ProxySourceCache(style.map);
        this.orthoMatrix = ref_properties.create$1();
        ref_properties.ortho(this.orthoMatrix, 0, ref_properties.EXTENT, 0, ref_properties.EXTENT, 0, 1);
        const gl = context.gl;
        this._overlapStencilMode = new ref_properties.StencilMode({
            func: gl.GEQUAL,
            mask: 255
        }, 0, 255, gl.KEEP, gl.KEEP, gl.REPLACE);
        this._previousZoom = painter.transform.zoom;
        this.pool = [];
        this._findCoveringTileCache = {};
        this._tilesDirty = {};
        this.style = style;
        this._useVertexMorphing = true;
        this._exaggeration = 1;
    }
    set style(style) {
        style.on('data', this._onStyleDataEvent.bind(this));
        style.on('neworder', this._checkRenderCacheEfficiency.bind(this));
        this._style = style;
        this._checkRenderCacheEfficiency();
    }
    update(style, transform, cameraChanging) {
        if (style && style.terrain) {
            if (this._style !== style) {
                this.style = style;
            }
            this.enabled = true;
            const terrainProps = style.terrain.properties;
            this.sourceCache = style._getSourceCache(terrainProps.get('source'));
            this._exaggeration = terrainProps.get('exaggeration');
            const updateSourceCache = () => {
                if (this.sourceCache.used) {
                    ref_properties.warnOnce(`Raster DEM source '${ this.sourceCache.id }' is used both for terrain and as layer source.\n` + 'This leads to lower resolution of hillshade. For full hillshade resolution but higher memory consumption, define another raster DEM source.');
                }
                const demScale = this.sourceCache.getSource().tileSize / GRID_DIM;
                const proxyTileSize = this.proxySourceCache.getSource().tileSize;
                this.sourceCache.update(transform, demScale * proxyTileSize, true);
                this._findCoveringTileCache[this.sourceCache.id] = {};
            };
            if (!this.sourceCache.usedForTerrain) {
                this._findCoveringTileCache[this.sourceCache.id] = {};
                this.sourceCache.usedForTerrain = true;
                updateSourceCache();
                this._initializing = true;
            }
            updateSourceCache();
            transform.updateElevation(!cameraChanging);
            this._findCoveringTileCache[this.proxySourceCache.id] = {};
            this.proxySourceCache.update(transform);
            this._emptyDEMTextureDirty = true;
        } else {
            this._disable();
        }
    }
    _checkRenderCacheEfficiency() {
        const renderCacheInfo = this.renderCacheEfficiency(this._style);
        if (this._style.map._optimizeForTerrain) ; else if (renderCacheInfo.efficiency !== 100) {
            ref_properties.warnOnce(`Terrain render cache efficiency is not optimal (${ renderCacheInfo.efficiency }%) and performance
                may be affected negatively, consider placing all background, fill and line layers before layer
                with id '${ renderCacheInfo.firstUndrapedLayer }' or create a map using optimizeForTerrain: true option.`);
        }
    }
    _onStyleDataEvent(event) {
        if (event.coord && event.dataType === 'source') {
            this._clearRenderCacheForTile(event.sourceCacheId, event.coord);
        } else if (event.dataType === 'style') {
            this._invalidateRenderCache = true;
        }
    }
    _disable() {
        if (!this.enabled)
            return;
        this.enabled = false;
        this._sharedDepthStencil = undefined;
        this.proxySourceCache.deallocRenderCache();
        if (this._style) {
            for (const id in this._style._sourceCaches) {
                this._style._sourceCaches[id].usedForTerrain = false;
            }
        }
    }
    destroy() {
        this._disable();
        if (this._emptyDEMTexture)
            this._emptyDEMTexture.destroy();
        this.pool.forEach(fbo => fbo.fb.destroy());
        this.pool = [];
        if (this._depthFBO) {
            this._depthFBO.destroy();
            delete this._depthFBO;
            delete this._depthTexture;
        }
    }
    _source() {
        return this.enabled ? this.sourceCache : null;
    }
    exaggeration() {
        return this._exaggeration;
    }
    get visibleDemTiles() {
        return this._visibleDemTiles;
    }
    get drapeBufferSize() {
        const extent = this.proxySourceCache.getSource().tileSize * 2;
        return [
            extent,
            extent
        ];
    }
    set useVertexMorphing(enable) {
        this._useVertexMorphing = enable;
    }
    updateTileBinding(sourcesCoords) {
        if (!this.enabled)
            return;
        this.prevTerrainTileForTile = this.terrainTileForTile;
        const psc = this.proxySourceCache;
        const tr = this.painter.transform;
        if (this._initializing) {
            this._initializing = tr._centerAltitude === 0 && this.getAtPointOrZero(ref_properties.MercatorCoordinate.fromLngLat(tr.center), -1) === -1;
            this._emptyDEMTextureDirty = !this._initializing;
        }
        const options = this.painter.options;
        this.renderCached = (options.zooming || options.moving || options.rotating || !!this.forceRenderCached) && !this._invalidateRenderCache;
        this._invalidateRenderCache = false;
        const coords = this.proxyCoords = psc.getIds().map(id => {
            const tileID = psc.getTileByID(id).tileID;
            tileID.projMatrix = tr.calculateProjMatrix(tileID.toUnwrapped());
            return tileID;
        });
        sortByDistanceToCamera(coords, this.painter);
        this._previousZoom = tr.zoom;
        const previousProxyToSource = this.proxyToSource || {};
        this.proxyToSource = {};
        coords.forEach(tileID => {
            this.proxyToSource[tileID.key] = {};
        });
        this.terrainTileForTile = {};
        const sourceCaches = this._style._sourceCaches;
        for (const id in sourceCaches) {
            const sourceCache = sourceCaches[id];
            if (!sourceCache.used)
                continue;
            if (sourceCache !== this.sourceCache)
                this._findCoveringTileCache[sourceCache.id] = {};
            this._setupProxiedCoordsForOrtho(sourceCache, sourcesCoords[id], previousProxyToSource);
            if (sourceCache.usedForTerrain)
                continue;
            const coordinates = sourcesCoords[id];
            if (sourceCache.getSource().reparseOverscaled) {
                this._assignTerrainTiles(coordinates);
            }
        }
        this.proxiedCoords[psc.id] = coords.map(tileID => new ProxiedTileID(tileID, tileID.key, this.orthoMatrix));
        this._assignTerrainTiles(coords);
        this._prepareDEMTextures();
        this._setupDrapedRenderBatches();
        this._initFBOPool();
        this._setupRenderCache(previousProxyToSource);
        this.renderingToTexture = false;
        this._updateTimestamp = ref_properties.exported.now();
        const visibleKeys = {};
        this._visibleDemTiles = [];
        for (const id of this.proxyCoords) {
            const demTile = this.terrainTileForTile[id.key];
            if (!demTile)
                continue;
            const key = demTile.tileID.key;
            if (key in visibleKeys)
                continue;
            this._visibleDemTiles.push(demTile);
            visibleKeys[key] = key;
        }
    }
    _assignTerrainTiles(coords) {
        if (this._initializing)
            return;
        coords.forEach(tileID => {
            if (this.terrainTileForTile[tileID.key])
                return;
            const demTile = this._findTileCoveringTileID(tileID, this.sourceCache);
            if (demTile)
                this.terrainTileForTile[tileID.key] = demTile;
        });
    }
    _prepareDEMTextures() {
        const context = this.painter.context;
        const gl = context.gl;
        for (const key in this.terrainTileForTile) {
            const tile = this.terrainTileForTile[key];
            const dem = tile.dem;
            if (dem && (!tile.demTexture || tile.needsDEMTextureUpload)) {
                context.activeTexture.set(gl.TEXTURE1);
                prepareDEMTexture(this.painter, tile, dem);
            }
        }
    }
    _prepareDemTileUniforms(proxyTile, demTile, uniforms, uniformSuffix) {
        if (!demTile || demTile.demTexture == null)
            return false;
        const proxyId = proxyTile.tileID.canonical;
        const demId = demTile.tileID.canonical;
        const demScaleBy = Math.pow(2, demId.z - proxyId.z);
        const suffix = uniformSuffix || '';
        uniforms[`u_dem_tl${ suffix }`] = [
            proxyId.x * demScaleBy % 1,
            proxyId.y * demScaleBy % 1
        ];
        uniforms[`u_dem_scale${ suffix }`] = demScaleBy;
        return true;
    }
    get emptyDEMTexture() {
        return !this._emptyDEMTextureDirty && this._emptyDEMTexture ? this._emptyDEMTexture : this._updateEmptyDEMTexture();
    }
    _getLoadedAreaMinimum() {
        let nonzero = 0;
        const min = this._visibleDemTiles.reduce((acc, tile) => {
            if (!tile.dem)
                return acc;
            const m = tile.dem.tree.minimums[0];
            acc += m;
            if (m > 0)
                nonzero++;
            return acc;
        }, 0);
        return nonzero ? min / nonzero : 0;
    }
    _updateEmptyDEMTexture() {
        const context = this.painter.context;
        const gl = context.gl;
        context.activeTexture.set(gl.TEXTURE2);
        const min = this._getLoadedAreaMinimum();
        const image = {
            width: 1,
            height: 1,
            data: new Uint8Array(ref_properties.DEMData.pack(min, this.sourceCache.getSource().encoding))
        };
        this._emptyDEMTextureDirty = false;
        let texture = this._emptyDEMTexture;
        if (!texture) {
            texture = this._emptyDEMTexture = new ref_properties.Texture(context, image, gl.RGBA, { premultiply: false });
        } else {
            texture.update(image, { premultiply: false });
        }
        return texture;
    }
    setupElevationDraw(tile, program, options) {
        const context = this.painter.context;
        const gl = context.gl;
        const uniforms = defaultTerrainUniforms(this.sourceCache.getSource().encoding);
        uniforms['u_dem_size'] = this.sourceCache.getSource().tileSize;
        uniforms['u_exaggeration'] = this.exaggeration();
        let demTile = null;
        let prevDemTile = null;
        let morphingPhase = 1;
        if (options && options.morphing && this._useVertexMorphing) {
            const srcTile = options.morphing.srcDemTile;
            const dstTile = options.morphing.dstDemTile;
            morphingPhase = options.morphing.phase;
            if (srcTile && dstTile) {
                if (this._prepareDemTileUniforms(tile, srcTile, uniforms, '_prev'))
                    prevDemTile = srcTile;
                if (this._prepareDemTileUniforms(tile, dstTile, uniforms))
                    demTile = dstTile;
            }
        }
        if (prevDemTile && demTile) {
            context.activeTexture.set(gl.TEXTURE2);
            demTile.demTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE, gl.NEAREST);
            context.activeTexture.set(gl.TEXTURE4);
            prevDemTile.demTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE, gl.NEAREST);
            uniforms['u_dem_lerp'] = morphingPhase;
        } else {
            demTile = this.terrainTileForTile[tile.tileID.key];
            context.activeTexture.set(gl.TEXTURE2);
            const demTexture = this._prepareDemTileUniforms(tile, demTile, uniforms) ? demTile.demTexture : this.emptyDEMTexture;
            demTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE, gl.NEAREST);
        }
        if (options && options.useDepthForOcclusion) {
            context.activeTexture.set(gl.TEXTURE3);
            this._depthTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE, gl.NEAREST);
            uniforms['u_depth_size_inv'] = [
                1 / this._depthFBO.width,
                1 / this._depthFBO.height
            ];
        }
        if (options && options.useMeterToDem && demTile) {
            const meterToDEM = (1 << demTile.tileID.canonical.z) * ref_properties.mercatorZfromAltitude(1, this.painter.transform.center.lat) * this.sourceCache.getSource().tileSize;
            uniforms['u_meter_to_dem'] = meterToDEM;
        }
        if (options && options.labelPlaneMatrixInv) {
            uniforms['u_label_plane_matrix_inv'] = options.labelPlaneMatrixInv;
        }
        program.setTerrainUniformValues(context, uniforms);
    }
    renderBatch(startLayerIndex) {
        if (this._drapedRenderBatches.length === 0) {
            return startLayerIndex + 1;
        }
        this.renderingToTexture = true;
        const painter = this.painter;
        const context = this.painter.context;
        const psc = this.proxySourceCache;
        const proxies = this.proxiedCoords[psc.id];
        const setupRenderToScreen = () => {
            context.bindFramebuffer.set(null);
            context.viewport.set([
                0,
                0,
                painter.width,
                painter.height
            ]);
            this.renderingToTexture = false;
        };
        const drapedLayerBatch = this._drapedRenderBatches.shift();
        let drawAsRasterCoords = [];
        const layerIds = painter.style.order;
        let poolIndex = 0;
        for (let i = 0; i < proxies.length; i++) {
            const proxy = proxies[i];
            const tile = psc.getTileByID(proxy.proxyTileKey);
            const renderCacheIndex = psc.proxyCachedFBO[proxy.key] ? psc.proxyCachedFBO[proxy.key][startLayerIndex] : undefined;
            let fbo;
            if (renderCacheIndex !== undefined) {
                fbo = this.currentFBO = psc.renderCache[renderCacheIndex];
            } else {
                fbo = this.currentFBO = this.pool[poolIndex++];
            }
            tile.texture = fbo.tex;
            if (renderCacheIndex !== undefined && !fbo.dirty) {
                drawAsRasterCoords.push(tile.tileID);
                continue;
            }
            context.bindFramebuffer.set(fbo.fb.framebuffer);
            this.renderedToTile = false;
            if (fbo.dirty) {
                context.clear({ color: ref_properties.Color.transparent });
                fbo.dirty = false;
            }
            let currentStencilSource;
            for (let j = drapedLayerBatch.start; j <= drapedLayerBatch.end; ++j) {
                const layer = painter.style._layers[layerIds[j]];
                const hidden = layer.isHidden(painter.transform.zoom);
                if (hidden)
                    continue;
                const sourceCache = painter.style._getLayerSourceCache(layer);
                const proxiedCoords = sourceCache ? this.proxyToSource[proxy.key][sourceCache.id] : [proxy];
                if (!proxiedCoords)
                    continue;
                const coords = proxiedCoords;
                context.viewport.set([
                    0,
                    0,
                    fbo.fb.width,
                    fbo.fb.height
                ]);
                if (currentStencilSource !== (sourceCache ? sourceCache.id : null)) {
                    this._setupStencil(proxiedCoords, layer, sourceCache);
                    currentStencilSource = sourceCache ? sourceCache.id : null;
                }
                painter.renderLayer(painter, sourceCache, layer, coords);
            }
            fbo.dirty = this.renderedToTile;
            if (this.renderedToTile)
                drawAsRasterCoords.push(tile.tileID);
            if (poolIndex === FBO_POOL_SIZE) {
                poolIndex = 0;
                if (drawAsRasterCoords.length > 0) {
                    setupRenderToScreen();
                    drawTerrainRaster(painter, this, psc, drawAsRasterCoords, this._updateTimestamp);
                    this.renderingToTexture = true;
                    drawAsRasterCoords = [];
                }
            }
        }
        setupRenderToScreen();
        if (drawAsRasterCoords.length > 0) {
            drawTerrainRaster(painter, this, psc, drawAsRasterCoords, this._updateTimestamp);
        }
        return drapedLayerBatch.end + 1;
    }
    postRender() {
    }
    renderCacheEfficiency(style) {
        const layerCount = style.order.length;
        if (layerCount === 0) {
            return { efficiency: 100 };
        }
        let uncacheableLayerCount = 0;
        let drapedLayerCount = 0;
        let reachedUndrapedLayer = false;
        let firstUndrapedLayer;
        for (let i = 0; i < layerCount; ++i) {
            const layer = style._layers[style.order[i]];
            if (!this._style.isLayerDraped(layer)) {
                if (!reachedUndrapedLayer) {
                    reachedUndrapedLayer = true;
                    firstUndrapedLayer = layer.id;
                }
            } else {
                if (reachedUndrapedLayer) {
                    ++uncacheableLayerCount;
                }
                ++drapedLayerCount;
            }
        }
        if (drapedLayerCount === 0) {
            return { efficiency: 100 };
        }
        return {
            efficiency: (1 - uncacheableLayerCount / drapedLayerCount) * 100,
            firstUndrapedLayer
        };
    }
    getMinElevationBelowMSL() {
        let min = 0;
        const maxDEMError = 30;
        this._visibleDemTiles.filter(tile => tile.dem).forEach(tile => {
            const minMaxTree = tile.dem.tree;
            min = Math.min(min, minMaxTree.minimums[0]);
        });
        return min === 0 ? min : (min - maxDEMError) * this._exaggeration;
    }
    raycast(pos, dir, exaggeration) {
        if (!this._visibleDemTiles)
            return null;
        const preparedTiles = this._visibleDemTiles.filter(tile => tile.dem).map(tile => {
            const id = tile.tileID;
            const tiles = Math.pow(2, id.overscaledZ);
            const {x, y} = id.canonical;
            const minx = x / tiles;
            const maxx = (x + 1) / tiles;
            const miny = y / tiles;
            const maxy = (y + 1) / tiles;
            const tree = tile.dem.tree;
            return {
                minx,
                miny,
                maxx,
                maxy,
                t: tree.raycastRoot(minx, miny, maxx, maxy, pos, dir, exaggeration),
                tile
            };
        });
        preparedTiles.sort((a, b) => {
            const at = a.t !== null ? a.t : Number.MAX_VALUE;
            const bt = b.t !== null ? b.t : Number.MAX_VALUE;
            return at - bt;
        });
        for (const obj of preparedTiles) {
            if (obj.t == null)
                return null;
            const tree = obj.tile.dem.tree;
            const t = tree.raycast(obj.minx, obj.miny, obj.maxx, obj.maxy, pos, dir, exaggeration);
            if (t != null)
                return t;
        }
        return null;
    }
    _createFBO() {
        const painter = this.painter;
        const context = painter.context;
        const gl = context.gl;
        const bufferSize = this.drapeBufferSize;
        context.activeTexture.set(gl.TEXTURE0);
        const tex = new ref_properties.Texture(context, {
            width: bufferSize[0],
            height: bufferSize[1],
            data: null
        }, gl.RGBA);
        tex.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
        const fb = context.createFramebuffer(bufferSize[0], bufferSize[1], false);
        fb.colorAttachment.set(tex.texture);
        fb.depthAttachment = new DepthStencilAttachment(context, fb.framebuffer);
        if (this._sharedDepthStencil === undefined) {
            this._sharedDepthStencil = context.createRenderbuffer(context.gl.DEPTH_STENCIL, bufferSize[0], bufferSize[1]);
            this._stencilRef = 0;
            fb.depthAttachment.set(this._sharedDepthStencil);
            context.clear({ stencil: 0 });
        } else {
            fb.depthAttachment.set(this._sharedDepthStencil);
        }
        if (context.extTextureFilterAnisotropic && !context.extTextureFilterAnisotropicForceOff) {
            gl.texParameterf(gl.TEXTURE_2D, context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, context.extTextureFilterAnisotropicMax);
        }
        return {
            fb,
            tex,
            dirty: false
        };
    }
    _initFBOPool() {
        while (this.pool.length < Math.min(FBO_POOL_SIZE, this.proxyCoords.length)) {
            this.pool.push(this._createFBO());
        }
    }
    _shouldDisableRenderCache() {
        if (!this.renderCached) {
            return true;
        }
        if (this._style.light && this._style.light.hasTransition()) {
            return true;
        }
        for (const id in this._style._sourceCaches) {
            if (this._style._sourceCaches[id].hasTransition()) {
                return true;
            }
        }
        const fadingOrTransitioning = id => {
            const layer = this._style._layers[id];
            const isHidden = layer.isHidden(this.painter.transform.zoom);
            const crossFade = layer.getCrossfadeParameters();
            const isFading = !!crossFade && crossFade.t !== 1;
            const isTransitioning = layer.hasTransition();
            return layer.type !== 'custom' && !isHidden && (isFading || isTransitioning);
        };
        return this._style.order.some(fadingOrTransitioning);
    }
    _clearRasterFadeFromRenderCache() {
        let hasRasterSource = false;
        for (const id in this._style._sourceCaches) {
            if (this._style._sourceCaches[id]._source instanceof RasterTileSource) {
                hasRasterSource = true;
                break;
            }
        }
        if (!hasRasterSource) {
            return;
        }
        for (let i = 0; i < this._style.order.length; ++i) {
            const layer = this._style._layers[this._style.order[i]];
            const isHidden = layer.isHidden(this.painter.transform.zoom);
            const sourceCache = this._style._getLayerSourceCache(layer);
            if (layer.type !== 'raster' || isHidden || !sourceCache) {
                continue;
            }
            const rasterLayer = layer;
            const fadeDuration = rasterLayer.paint.get('raster-fade-duration');
            for (const proxy of this.proxyCoords) {
                const proxiedCoords = this.proxyToSource[proxy.key][sourceCache.id];
                const coords = proxiedCoords;
                if (!coords) {
                    continue;
                }
                for (const coord of coords) {
                    const tile = sourceCache.getTile(coord);
                    const parent = sourceCache.findLoadedParent(coord, 0);
                    const fade = rasterFade(tile, parent, sourceCache, this.painter.transform, fadeDuration);
                    const isFading = fade.opacity !== 1 || fade.mix !== 0;
                    if (isFading) {
                        this._clearRenderCacheForTile(sourceCache.id, coord);
                    }
                }
            }
        }
    }
    _setupDrapedRenderBatches() {
        const layerIds = this._style.order;
        const layerCount = layerIds.length;
        if (layerCount === 0) {
            return;
        }
        const batches = [];
        let currentLayer = 0;
        let layer = this._style._layers[layerIds[currentLayer]];
        while (!this._style.isLayerDraped(layer) && layer.isHidden(this.painter.transform.zoom) && ++currentLayer < layerCount) {
            layer = this._style._layers[layerIds[currentLayer]];
        }
        let batchStart;
        for (; currentLayer < layerCount; ++currentLayer) {
            const layer = this._style._layers[layerIds[currentLayer]];
            if (layer.isHidden(this.painter.transform.zoom)) {
                continue;
            }
            if (!this._style.isLayerDraped(layer)) {
                if (batchStart !== undefined) {
                    batches.push({
                        start: batchStart,
                        end: currentLayer - 1
                    });
                    batchStart = undefined;
                }
                continue;
            }
            if (batchStart === undefined) {
                batchStart = currentLayer;
            }
        }
        if (batchStart !== undefined) {
            batches.push({
                start: batchStart,
                end: currentLayer - 1
            });
        }
        if (this._style.map._optimizeForTerrain) ;
        this._drapedRenderBatches = batches;
    }
    _setupRenderCache(previousProxyToSource) {
        const psc = this.proxySourceCache;
        if (this._shouldDisableRenderCache()) {
            if (psc.renderCache.length > psc.renderCachePool.length) {
                const used = Object.values(psc.proxyCachedFBO);
                psc.proxyCachedFBO = {};
                for (let i = 0; i < used.length; ++i) {
                    const fbos = Object.values(used[i]);
                    psc.renderCachePool.push(...fbos);
                }
            }
            return;
        }
        this._clearRasterFadeFromRenderCache();
        const coords = this.proxyCoords;
        const dirty = this._tilesDirty;
        for (let i = coords.length - 1; i >= 0; i--) {
            const proxy = coords[i];
            psc.getTileByID(proxy.key);
            if (psc.proxyCachedFBO[proxy.key] !== undefined) {
                const prev = previousProxyToSource[proxy.key];
                const current = this.proxyToSource[proxy.key];
                let equal = 0;
                for (const source in current) {
                    const tiles = current[source];
                    const prevTiles = prev[source];
                    if (!prevTiles || prevTiles.length !== tiles.length || tiles.some((t, index) => t !== prevTiles[index] || dirty[source] && dirty[source].hasOwnProperty(t.key))) {
                        equal = -1;
                        break;
                    }
                    ++equal;
                }
                for (const proxyFBO in psc.proxyCachedFBO[proxy.key]) {
                    psc.renderCache[psc.proxyCachedFBO[proxy.key][proxyFBO]].dirty = equal < 0 || equal !== Object.values(prev).length;
                }
            } else {
                for (let j = 0; j < this._drapedRenderBatches.length; ++j) {
                    const batch = this._drapedRenderBatches[j];
                    let index = psc.renderCachePool.pop();
                    if (index === undefined && psc.renderCache.length < RENDER_CACHE_MAX_SIZE) {
                        index = psc.renderCache.length;
                        psc.renderCache.push(this._createFBO());
                    }
                    if (index !== undefined) {
                        if (psc.proxyCachedFBO[proxy.key] === undefined)
                            psc.proxyCachedFBO[proxy.key] = {};
                        psc.proxyCachedFBO[proxy.key][batch.start] = index;
                        psc.renderCache[index].dirty = true;
                    }
                }
            }
        }
        this._tilesDirty = {};
    }
    _setupStencil(proxiedCoords, layer, sourceCache) {
        if (!sourceCache || !this._sourceTilesOverlap[sourceCache.id]) {
            if (this._overlapStencilType)
                this._overlapStencilType = false;
            return;
        }
        const context = this.painter.context;
        const gl = context.gl;
        if (proxiedCoords.length <= 1) {
            this._overlapStencilType = false;
            return;
        }
        const fbo = this.currentFBO;
        fbo.fb;
        let stencilRange;
        if (layer.isTileClipped()) {
            stencilRange = proxiedCoords.length;
            this._overlapStencilMode.test = {
                func: gl.EQUAL,
                mask: 255
            };
            this._overlapStencilType = 'Clip';
        } else if (proxiedCoords[0].overscaledZ > proxiedCoords[proxiedCoords.length - 1].overscaledZ) {
            stencilRange = 1;
            this._overlapStencilMode.test = {
                func: gl.GREATER,
                mask: 255
            };
            this._overlapStencilType = 'Mask';
        } else {
            this._overlapStencilType = false;
            return;
        }
        if (this._stencilRef + stencilRange > 255) {
            context.clear({ stencil: 0 });
            this._stencilRef = 0;
        }
        this._stencilRef += stencilRange;
        this._overlapStencilMode.ref = this._stencilRef;
        if (layer.isTileClipped()) {
            this._renderTileClippingMasks(proxiedCoords, this._overlapStencilMode.ref);
        }
    }
    stencilModeForRTTOverlap(id) {
        if (!this.renderingToTexture || !this._overlapStencilType) {
            return ref_properties.StencilMode.disabled;
        }
        if (this._overlapStencilType === 'Clip') {
            this._overlapStencilMode.ref = this.painter._tileClippingMaskIDs[id.key];
        }
        return this._overlapStencilMode;
    }
    _renderTileClippingMasks(proxiedCoords, ref) {
        const painter = this.painter;
        const context = this.painter.context;
        const gl = context.gl;
        painter._tileClippingMaskIDs = {};
        context.setColorMode(ref_properties.ColorMode.disabled);
        context.setDepthMode(ref_properties.DepthMode.disabled);
        const program = painter.useProgram('clippingMask');
        for (const tileID of proxiedCoords) {
            const id = painter._tileClippingMaskIDs[tileID.key] = --ref;
            program.draw(context, gl.TRIANGLES, ref_properties.DepthMode.disabled, new ref_properties.StencilMode({
                func: gl.ALWAYS,
                mask: 0
            }, id, 255, gl.KEEP, gl.KEEP, gl.REPLACE), ref_properties.ColorMode.disabled, ref_properties.CullFaceMode.disabled, clippingMaskUniformValues(tileID.projMatrix), '$clipping', painter.tileExtentBuffer, painter.quadTriangleIndexBuffer, painter.tileExtentSegments);
        }
    }
    pointCoordinate(screenPoint) {
        const transform = this.painter.transform;
        if (screenPoint.x < 0 || screenPoint.x > transform.width || screenPoint.y < 0 || screenPoint.y > transform.height) {
            return null;
        }
        const far = [
            screenPoint.x,
            screenPoint.y,
            1,
            1
        ];
        ref_properties.transformMat4(far, far, transform.pixelMatrixInverse);
        ref_properties.scale$2(far, far, 1 / far[3]);
        far[0] /= transform.worldSize;
        far[1] /= transform.worldSize;
        const camera = transform._camera.position;
        const mercatorZScale = ref_properties.mercatorZfromAltitude(1, transform.center.lat);
        const p = [
            camera[0],
            camera[1],
            camera[2] / mercatorZScale,
            0
        ];
        const dir = ref_properties.subtract([], far.slice(0, 3), p);
        ref_properties.normalize(dir, dir);
        const exaggeration = this._exaggeration;
        const distanceAlongRay = this.raycast(p, dir, exaggeration);
        if (distanceAlongRay === null || !distanceAlongRay)
            return null;
        ref_properties.scaleAndAdd(p, p, dir, distanceAlongRay);
        p[3] = p[2];
        p[2] *= mercatorZScale;
        return p;
    }
    drawDepth() {
        const painter = this.painter;
        const context = painter.context;
        const psc = this.proxySourceCache;
        const width = Math.ceil(painter.width), height = Math.ceil(painter.height);
        if (this._depthFBO && (this._depthFBO.width !== width || this._depthFBO.height !== height)) {
            this._depthFBO.destroy();
            delete this._depthFBO;
            delete this._depthTexture;
        }
        if (!this._depthFBO) {
            const gl = context.gl;
            const fbo = context.createFramebuffer(width, height, true);
            context.activeTexture.set(gl.TEXTURE0);
            const texture = new ref_properties.Texture(context, {
                width,
                height,
                data: null
            }, gl.RGBA);
            texture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE);
            fbo.colorAttachment.set(texture.texture);
            const renderbuffer = context.createRenderbuffer(context.gl.DEPTH_COMPONENT16, width, height);
            fbo.depthAttachment.set(renderbuffer);
            this._depthFBO = fbo;
            this._depthTexture = texture;
        }
        context.bindFramebuffer.set(this._depthFBO.framebuffer);
        context.viewport.set([
            0,
            0,
            width,
            height
        ]);
        drawTerrainDepth(painter, this, psc, this.proxyCoords);
    }
    _setupProxiedCoordsForOrtho(sourceCache, sourceCoords, previousProxyToSource) {
        if (sourceCache.getSource() instanceof ImageSource) {
            return this._setupProxiedCoordsForImageSource(sourceCache, sourceCoords, previousProxyToSource);
        }
        this._findCoveringTileCache[sourceCache.id] = this._findCoveringTileCache[sourceCache.id] || {};
        const coords = this.proxiedCoords[sourceCache.id] = [];
        const proxys = this.proxyCoords;
        for (let i = 0; i < proxys.length; i++) {
            const proxyTileID = proxys[i];
            const proxied = this._findTileCoveringTileID(proxyTileID, sourceCache);
            if (proxied) {
                const id = this._createProxiedId(proxyTileID, proxied, previousProxyToSource[proxyTileID.key] && previousProxyToSource[proxyTileID.key][sourceCache.id]);
                coords.push(id);
                this.proxyToSource[proxyTileID.key][sourceCache.id] = [id];
            }
        }
        let hasOverlap = false;
        for (let i = 0; i < sourceCoords.length; i++) {
            const tile = sourceCache.getTile(sourceCoords[i]);
            if (!tile || !tile.hasData())
                continue;
            const proxy = this._findTileCoveringTileID(tile.tileID, this.proxySourceCache);
            if (proxy && proxy.tileID.canonical.z !== tile.tileID.canonical.z) {
                const array = this.proxyToSource[proxy.tileID.key][sourceCache.id];
                const id = this._createProxiedId(proxy.tileID, tile, previousProxyToSource[proxy.tileID.key] && previousProxyToSource[proxy.tileID.key][sourceCache.id]);
                if (!array) {
                    this.proxyToSource[proxy.tileID.key][sourceCache.id] = [id];
                } else {
                    array.splice(array.length - 1, 0, id);
                }
                coords.push(id);
                hasOverlap = true;
            }
        }
        this._sourceTilesOverlap[sourceCache.id] = hasOverlap;
    }
    _setupProxiedCoordsForImageSource(sourceCache, sourceCoords, previousProxyToSource) {
        if (!sourceCache.getSource().loaded())
            return;
        const coords = this.proxiedCoords[sourceCache.id] = [];
        const proxys = this.proxyCoords;
        const imageSource = sourceCache.getSource();
        const anchor = new ref_properties.pointGeometry(imageSource.tileID.x, imageSource.tileID.y)._div(1 << imageSource.tileID.z);
        const aabb = imageSource.coordinates.map(ref_properties.MercatorCoordinate.fromLngLat).reduce((acc, coord) => {
            acc.min.x = Math.min(acc.min.x, coord.x - anchor.x);
            acc.min.y = Math.min(acc.min.y, coord.y - anchor.y);
            acc.max.x = Math.max(acc.max.x, coord.x - anchor.x);
            acc.max.y = Math.max(acc.max.y, coord.y - anchor.y);
            return acc;
        }, {
            min: new ref_properties.pointGeometry(Number.MAX_VALUE, Number.MAX_VALUE),
            max: new ref_properties.pointGeometry(-Number.MAX_VALUE, -Number.MAX_VALUE)
        });
        const tileOutsideImage = (tileID, imageTileID) => {
            const x = tileID.wrap + tileID.canonical.x / (1 << tileID.canonical.z);
            const y = tileID.canonical.y / (1 << tileID.canonical.z);
            const d = ref_properties.EXTENT / (1 << tileID.canonical.z);
            const ix = imageTileID.wrap + imageTileID.canonical.x / (1 << imageTileID.canonical.z);
            const iy = imageTileID.canonical.y / (1 << imageTileID.canonical.z);
            return x + d < ix + aabb.min.x || x > ix + aabb.max.x || y + d < iy + aabb.min.y || y > iy + aabb.max.y;
        };
        for (let i = 0; i < proxys.length; i++) {
            const proxyTileID = proxys[i];
            for (let j = 0; j < sourceCoords.length; j++) {
                const tile = sourceCache.getTile(sourceCoords[j]);
                if (!tile || !tile.hasData())
                    continue;
                if (tileOutsideImage(proxyTileID, tile.tileID))
                    continue;
                const id = this._createProxiedId(proxyTileID, tile, previousProxyToSource[proxyTileID.key] && previousProxyToSource[proxyTileID.key][sourceCache.id]);
                const array = this.proxyToSource[proxyTileID.key][sourceCache.id];
                if (!array) {
                    this.proxyToSource[proxyTileID.key][sourceCache.id] = [id];
                } else {
                    array.push(id);
                }
                coords.push(id);
            }
        }
    }
    _createProxiedId(proxyTileID, tile, recycle) {
        let matrix = this.orthoMatrix;
        if (recycle) {
            const recycled = recycle.find(proxied => proxied.key === tile.tileID.key);
            if (recycled)
                return recycled;
        }
        if (tile.tileID.key !== proxyTileID.key) {
            const scale = proxyTileID.canonical.z - tile.tileID.canonical.z;
            matrix = ref_properties.create$1();
            let size, xOffset, yOffset;
            const wrap = tile.tileID.wrap - proxyTileID.wrap << proxyTileID.overscaledZ;
            if (scale > 0) {
                size = ref_properties.EXTENT >> scale;
                xOffset = size * ((tile.tileID.canonical.x << scale) - proxyTileID.canonical.x + wrap);
                yOffset = size * ((tile.tileID.canonical.y << scale) - proxyTileID.canonical.y);
            } else {
                size = ref_properties.EXTENT << -scale;
                xOffset = ref_properties.EXTENT * (tile.tileID.canonical.x - (proxyTileID.canonical.x + wrap << -scale));
                yOffset = ref_properties.EXTENT * (tile.tileID.canonical.y - (proxyTileID.canonical.y << -scale));
            }
            ref_properties.ortho(matrix, 0, size, 0, size, 0, 1);
            ref_properties.translate(matrix, matrix, [
                xOffset,
                yOffset,
                0
            ]);
        }
        return new ProxiedTileID(tile.tileID, proxyTileID.key, matrix);
    }
    _findTileCoveringTileID(tileID, sourceCache) {
        let tile = sourceCache.getTile(tileID);
        if (tile && tile.hasData())
            return tile;
        const lookup = this._findCoveringTileCache[sourceCache.id];
        const key = lookup[tileID.key];
        tile = key ? sourceCache.getTileByID(key) : null;
        if (tile && tile.hasData() || key === null)
            return tile;
        let sourceTileID = tile ? tile.tileID : tileID;
        let z = sourceTileID.overscaledZ;
        const minzoom = sourceCache.getSource().minzoom;
        const path = [];
        if (!key) {
            const maxzoom = sourceCache.getSource().maxzoom;
            if (tileID.canonical.z >= maxzoom) {
                const downscale = tileID.canonical.z - maxzoom;
                if (sourceCache.getSource().reparseOverscaled) {
                    z = Math.max(tileID.canonical.z + 2, sourceCache.transform.tileZoom);
                    sourceTileID = new ref_properties.OverscaledTileID(z, tileID.wrap, maxzoom, tileID.canonical.x >> downscale, tileID.canonical.y >> downscale);
                } else if (downscale !== 0) {
                    z = maxzoom;
                    sourceTileID = new ref_properties.OverscaledTileID(z, tileID.wrap, maxzoom, tileID.canonical.x >> downscale, tileID.canonical.y >> downscale);
                }
            }
            if (sourceTileID.key !== tileID.key) {
                path.push(sourceTileID.key);
                tile = sourceCache.getTile(sourceTileID);
            }
        }
        const pathToLookup = key => {
            path.forEach(id => {
                lookup[id] = key;
            });
            path.length = 0;
        };
        for (z = z - 1; z >= minzoom && !(tile && tile.hasData()); z--) {
            if (tile) {
                pathToLookup(tile.tileID.key);
            }
            const id = sourceTileID.calculateScaledKey(z);
            tile = sourceCache.getTileByID(id);
            if (tile && tile.hasData())
                break;
            const key = lookup[id];
            if (key === null) {
                break;
            } else if (key !== undefined) {
                tile = sourceCache.getTileByID(key);
                continue;
            }
            path.push(id);
        }
        pathToLookup(tile ? tile.tileID.key : null);
        return tile && tile.hasData() ? tile : null;
    }
    findDEMTileFor(tileID) {
        return this.enabled ? this._findTileCoveringTileID(tileID, this.sourceCache) : null;
    }
    prepareDrawTile(coord) {
        if (!this.renderedToTile) {
            this.renderedToTile = true;
        }
    }
    _clearRenderCacheForTile(source, coord) {
        let sourceTiles = this._tilesDirty[source];
        if (!sourceTiles)
            sourceTiles = this._tilesDirty[source] = {};
        sourceTiles[coord.key] = true;
    }
    getWirefameBuffer() {
        if (!this.wireframeSegments) {
            const wireframeGridIndices = createWireframeGrid(GRID_DIM + 1);
            this.wireframeIndexBuffer = this.painter.context.createIndexBuffer(wireframeGridIndices);
            this.wireframeSegments = ref_properties.SegmentVector.simpleSegment(0, 0, this.gridBuffer.length, wireframeGridIndices.length);
        }
        return [
            this.wireframeIndexBuffer,
            this.wireframeSegments
        ];
    }
}
function sortByDistanceToCamera(tileIDs, painter) {
    const cameraCoordinate = painter.transform.pointCoordinate(painter.transform.getCameraPoint());
    const cameraPoint = new ref_properties.pointGeometry(cameraCoordinate.x, cameraCoordinate.y);
    tileIDs.sort((a, b) => {
        if (b.overscaledZ - a.overscaledZ)
            return b.overscaledZ - a.overscaledZ;
        const aPoint = new ref_properties.pointGeometry(a.canonical.x + (1 << a.canonical.z) * a.wrap, a.canonical.y);
        const bPoint = new ref_properties.pointGeometry(b.canonical.x + (1 << b.canonical.z) * b.wrap, b.canonical.y);
        const cameraScaled = cameraPoint.mult(1 << a.canonical.z);
        cameraScaled.x -= 0.5;
        cameraScaled.y -= 0.5;
        return cameraScaled.distSqr(aPoint) - cameraScaled.distSqr(bPoint);
    });
}
function createGrid(count) {
    const boundsArray = new ref_properties.StructArrayLayout4i8();
    const indexArray = new ref_properties.StructArrayLayout3ui6();
    const size = count + 2;
    boundsArray.reserve(size * size);
    indexArray.reserve((size - 1) * (size - 1) * 2);
    const step = ref_properties.EXTENT / (count - 1);
    const gridBound = ref_properties.EXTENT + step / 2;
    const bound = gridBound + step;
    const skirtOffset = 24575;
    for (let y = -step; y < bound; y += step) {
        for (let x = -step; x < bound; x += step) {
            const offset = x < 0 || x > gridBound || y < 0 || y > gridBound ? skirtOffset : 0;
            const xi = ref_properties.clamp(Math.round(x), 0, ref_properties.EXTENT);
            const yi = ref_properties.clamp(Math.round(y), 0, ref_properties.EXTENT);
            boundsArray.emplaceBack(xi + offset, yi, xi, yi);
        }
    }
    const skirtIndicesOffset = (size - 3) * (size - 3) * 2;
    const quad = (i, j) => {
        const index = j * size + i;
        indexArray.emplaceBack(index + 1, index, index + size);
        indexArray.emplaceBack(index + size, index + size + 1, index + 1);
    };
    for (let j = 1; j < size - 2; j++) {
        for (let i = 1; i < size - 2; i++) {
            quad(i, j);
        }
    }
    [
        0,
        size - 2
    ].forEach(j => {
        for (let i = 0; i < size - 1; i++) {
            quad(i, j);
            quad(j, i);
        }
    });
    return [
        boundsArray,
        indexArray,
        skirtIndicesOffset
    ];
}
function createWireframeGrid(count) {
    let i, j, index;
    const indexArray = new ref_properties.StructArrayLayout2ui4();
    const size = count + 2;
    for (j = 1; j < count; j++) {
        for (i = 1; i < count; i++) {
            index = j * size + i;
            indexArray.emplaceBack(index, index + 1);
            indexArray.emplaceBack(index, index + size);
            indexArray.emplaceBack(index + 1, index + size);
            if (j === count - 1)
                indexArray.emplaceBack(index + size, index + size + 1);
        }
        indexArray.emplaceBack(index + 1, index + 1 + size);
    }
    return indexArray;
}
const terrainUniforms = (context, locations) => ({
    'u_dem': new ref_properties.Uniform1i(context, locations.u_dem),
    'u_dem_prev': new ref_properties.Uniform1i(context, locations.u_dem_prev),
    'u_dem_unpack': new ref_properties.Uniform4f(context, locations.u_dem_unpack),
    'u_dem_tl': new ref_properties.Uniform2f(context, locations.u_dem_tl),
    'u_dem_scale': new ref_properties.Uniform1f(context, locations.u_dem_scale),
    'u_dem_tl_prev': new ref_properties.Uniform2f(context, locations.u_dem_tl_prev),
    'u_dem_scale_prev': new ref_properties.Uniform1f(context, locations.u_dem_scale_prev),
    'u_dem_size': new ref_properties.Uniform1f(context, locations.u_dem_size),
    'u_dem_lerp': new ref_properties.Uniform1f(context, locations.u_dem_lerp),
    'u_exaggeration': new ref_properties.Uniform1f(context, locations.u_exaggeration),
    'u_depth': new ref_properties.Uniform1i(context, locations.u_depth),
    'u_depth_size_inv': new ref_properties.Uniform2f(context, locations.u_depth_size_inv),
    'u_meter_to_dem': new ref_properties.Uniform1f(context, locations.u_meter_to_dem),
    'u_label_plane_matrix_inv': new ref_properties.UniformMatrix4f(context, locations.u_label_plane_matrix_inv)
});
function defaultTerrainUniforms(encoding) {
    return {
        'u_dem': 2,
        'u_dem_prev': 4,
        'u_dem_unpack': ref_properties.DEMData.getUnpackVector(encoding),
        'u_dem_tl': [
            0,
            0
        ],
        'u_dem_tl_prev': [
            0,
            0
        ],
        'u_dem_scale': 0,
        'u_dem_scale_prev': 0,
        'u_dem_size': 0,
        'u_dem_lerp': 1,
        'u_depth': 3,
        'u_depth_size_inv': [
            0,
            0
        ],
        'u_exaggeration': 0
    };
}

const fogUniforms = (context, locations) => ({
    'u_fog_matrix': new ref_properties.UniformMatrix4f(context, locations.u_fog_matrix),
    'u_fog_range': new ref_properties.Uniform2f(context, locations.u_fog_range),
    'u_fog_color': new ref_properties.Uniform4f(context, locations.u_fog_color),
    'u_fog_horizon_blend': new ref_properties.Uniform1f(context, locations.u_fog_horizon_blend),
    'u_fog_temporal_offset': new ref_properties.Uniform1f(context, locations.u_fog_temporal_offset)
});
const fogUniformValues = (painter, fog, tileID, fogOpacity) => {
    const fogColor = fog.properties.get('color');
    const temporalOffset = painter.frameCounter / 1000 % 1;
    const fogColorUnpremultiplied = [
        fogColor.r / fogColor.a,
        fogColor.g / fogColor.a,
        fogColor.b / fogColor.a,
        fogOpacity
    ];
    return {
        'u_fog_matrix': tileID ? painter.transform.calculateFogTileMatrix(tileID) : painter.identityMat,
        'u_fog_range': fog.getFovAdjustedRange(painter.transform._fov),
        'u_fog_color': fogColorUnpremultiplied,
        'u_fog_horizon_blend': fog.properties.get('horizon-blend'),
        'u_fog_temporal_offset': temporalOffset
    };
};

function getTokenizedAttributesAndUniforms(array) {
    const result = [];
    for (let i = 0; i < array.length; i++) {
        if (array[i] === null)
            continue;
        const token = array[i].split(' ');
        result.push(token.pop());
    }
    return result;
}
class Program {
    static cacheKey(name, defines, programConfiguration) {
        let key = `${ name }${ programConfiguration ? programConfiguration.cacheKey : '' }`;
        for (const define of defines) {
            key += `/${ define }`;
        }
        return key;
    }
    constructor(context, name, source, configuration, fixedUniforms, fixedDefines) {
        const gl = context.gl;
        this.program = gl.createProgram();
        const staticAttrInfo = getTokenizedAttributesAndUniforms(source.staticAttributes);
        const dynamicAttrInfo = configuration ? configuration.getBinderAttributes() : [];
        const allAttrInfo = staticAttrInfo.concat(dynamicAttrInfo);
        const staticUniformsInfo = source.staticUniforms ? getTokenizedAttributesAndUniforms(source.staticUniforms) : [];
        const dynamicUniformsInfo = configuration ? configuration.getBinderUniforms() : [];
        const uniformList = staticUniformsInfo.concat(dynamicUniformsInfo);
        const allUniformsInfo = [];
        for (const uniform of uniformList) {
            if (allUniformsInfo.indexOf(uniform) < 0)
                allUniformsInfo.push(uniform);
        }
        let defines = configuration ? configuration.defines() : [];
        defines = defines.concat(fixedDefines.map(define => `#define ${ define }`));
        const fragmentSource = defines.concat(prelude.fragmentSource, preludeCommonSource, preludeFog.fragmentSource, source.fragmentSource).join('\n');
        const vertexSource = defines.concat(prelude.vertexSource, preludeCommonSource, preludeFog.vertexSource, preludeTerrain.vertexSource, source.vertexSource).join('\n');
        const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
        if (gl.isContextLost()) {
            this.failedToCreate = true;
            return;
        }
        gl.shaderSource(fragmentShader, fragmentSource);
        gl.compileShader(fragmentShader);
        gl.attachShader(this.program, fragmentShader);
        const vertexShader = gl.createShader(gl.VERTEX_SHADER);
        if (gl.isContextLost()) {
            this.failedToCreate = true;
            return;
        }
        gl.shaderSource(vertexShader, vertexSource);
        gl.compileShader(vertexShader);
        gl.attachShader(this.program, vertexShader);
        this.attributes = {};
        const uniformLocations = {};
        this.numAttributes = allAttrInfo.length;
        for (let i = 0; i < this.numAttributes; i++) {
            if (allAttrInfo[i]) {
                gl.bindAttribLocation(this.program, i, allAttrInfo[i]);
                this.attributes[allAttrInfo[i]] = i;
            }
        }
        gl.linkProgram(this.program);
        gl.deleteShader(vertexShader);
        gl.deleteShader(fragmentShader);
        for (let it = 0; it < allUniformsInfo.length; it++) {
            const uniform = allUniformsInfo[it];
            if (uniform && !uniformLocations[uniform]) {
                const uniformLocation = gl.getUniformLocation(this.program, uniform);
                if (uniformLocation) {
                    uniformLocations[uniform] = uniformLocation;
                }
            }
        }
        this.fixedUniforms = fixedUniforms(context, uniformLocations);
        this.binderUniforms = configuration ? configuration.getUniforms(context, uniformLocations) : [];
        if (fixedDefines.indexOf('TERRAIN') !== -1) {
            this.terrainUniforms = terrainUniforms(context, uniformLocations);
        }
        if (fixedDefines.indexOf('FOG') !== -1) {
            this.fogUniforms = fogUniforms(context, uniformLocations);
        }
    }
    setTerrainUniformValues(context, terrainUniformValues) {
        if (!this.terrainUniforms)
            return;
        const uniforms = this.terrainUniforms;
        if (this.failedToCreate)
            return;
        context.program.set(this.program);
        for (const name in terrainUniformValues) {
            uniforms[name].set(terrainUniformValues[name]);
        }
    }
    setFogUniformValues(context, fogUniformsValues) {
        if (!this.fogUniforms)
            return;
        const uniforms = this.fogUniforms;
        if (this.failedToCreate)
            return;
        context.program.set(this.program);
        for (const name in fogUniformsValues) {
            if (uniforms[name].location) {
                uniforms[name].set(fogUniformsValues[name]);
            }
        }
    }
    draw(context, drawMode, depthMode, stencilMode, colorMode, cullFaceMode, uniformValues, layerID, layoutVertexBuffer, indexBuffer, segments, currentProperties, zoom, configuration, dynamicLayoutBuffer, dynamicLayoutBuffer2) {
        const gl = context.gl;
        if (this.failedToCreate)
            return;
        context.program.set(this.program);
        context.setDepthMode(depthMode);
        context.setStencilMode(stencilMode);
        context.setColorMode(colorMode);
        context.setCullFace(cullFaceMode);
        for (const name in this.fixedUniforms) {
            this.fixedUniforms[name].set(uniformValues[name]);
        }
        if (configuration) {
            configuration.setUniforms(context, this.binderUniforms, currentProperties, { zoom: zoom });
        }
        const primitiveSize = {
            [gl.LINES]: 2,
            [gl.TRIANGLES]: 3,
            [gl.LINE_STRIP]: 1
        }[drawMode];
        for (const segment of segments.get()) {
            const vaos = segment.vaos || (segment.vaos = {});
            const vao = vaos[layerID] || (vaos[layerID] = new VertexArrayObject());
            vao.bind(context, this, layoutVertexBuffer, configuration ? configuration.getPaintVertexBuffers() : [], indexBuffer, segment.vertexOffset, dynamicLayoutBuffer, dynamicLayoutBuffer2);
            gl.drawElements(drawMode, segment.primitiveLength * primitiveSize, gl.UNSIGNED_SHORT, segment.primitiveOffset * primitiveSize * 2);
        }
    }
}

function patternUniformValues(crossfade, painter, tile) {
    const tileRatio = 1 / pixelsToTileUnits(tile, 1, painter.transform.tileZoom);
    const numTiles = Math.pow(2, tile.tileID.overscaledZ);
    const tileSizeAtNearestZoom = tile.tileSize * Math.pow(2, painter.transform.tileZoom) / numTiles;
    const pixelX = tileSizeAtNearestZoom * (tile.tileID.canonical.x + tile.tileID.wrap * numTiles);
    const pixelY = tileSizeAtNearestZoom * tile.tileID.canonical.y;
    return {
        'u_image': 0,
        'u_texsize': tile.imageAtlasTexture.size,
        'u_scale': [
            tileRatio,
            crossfade.fromScale,
            crossfade.toScale
        ],
        'u_fade': crossfade.t,
        'u_pixel_coord_upper': [
            pixelX >> 16,
            pixelY >> 16
        ],
        'u_pixel_coord_lower': [
            pixelX & 65535,
            pixelY & 65535
        ]
    };
}
function bgPatternUniformValues(image, crossfade, painter, tile) {
    const imagePosA = painter.imageManager.getPattern(image.from.toString());
    const imagePosB = painter.imageManager.getPattern(image.to.toString());
    const {width, height} = painter.imageManager.getPixelSize();
    const numTiles = Math.pow(2, tile.tileID.overscaledZ);
    const tileSizeAtNearestZoom = tile.tileSize * Math.pow(2, painter.transform.tileZoom) / numTiles;
    const pixelX = tileSizeAtNearestZoom * (tile.tileID.canonical.x + tile.tileID.wrap * numTiles);
    const pixelY = tileSizeAtNearestZoom * tile.tileID.canonical.y;
    return {
        'u_image': 0,
        'u_pattern_tl_a': imagePosA.tl,
        'u_pattern_br_a': imagePosA.br,
        'u_pattern_tl_b': imagePosB.tl,
        'u_pattern_br_b': imagePosB.br,
        'u_texsize': [
            width,
            height
        ],
        'u_mix': crossfade.t,
        'u_pattern_size_a': imagePosA.displaySize,
        'u_pattern_size_b': imagePosB.displaySize,
        'u_scale_a': crossfade.fromScale,
        'u_scale_b': crossfade.toScale,
        'u_tile_units_to_pixels': 1 / pixelsToTileUnits(tile, 1, painter.transform.tileZoom),
        'u_pixel_coord_upper': [
            pixelX >> 16,
            pixelY >> 16
        ],
        'u_pixel_coord_lower': [
            pixelX & 65535,
            pixelY & 65535
        ]
    };
}

const fillExtrusionUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_lightpos': new ref_properties.Uniform3f(context, locations.u_lightpos),
    'u_lightintensity': new ref_properties.Uniform1f(context, locations.u_lightintensity),
    'u_lightcolor': new ref_properties.Uniform3f(context, locations.u_lightcolor),
    'u_vertical_gradient': new ref_properties.Uniform1f(context, locations.u_vertical_gradient),
    'u_opacity': new ref_properties.Uniform1f(context, locations.u_opacity)
});
const fillExtrusionPatternUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_lightpos': new ref_properties.Uniform3f(context, locations.u_lightpos),
    'u_lightintensity': new ref_properties.Uniform1f(context, locations.u_lightintensity),
    'u_lightcolor': new ref_properties.Uniform3f(context, locations.u_lightcolor),
    'u_vertical_gradient': new ref_properties.Uniform1f(context, locations.u_vertical_gradient),
    'u_height_factor': new ref_properties.Uniform1f(context, locations.u_height_factor),
    'u_image': new ref_properties.Uniform1i(context, locations.u_image),
    'u_texsize': new ref_properties.Uniform2f(context, locations.u_texsize),
    'u_pixel_coord_upper': new ref_properties.Uniform2f(context, locations.u_pixel_coord_upper),
    'u_pixel_coord_lower': new ref_properties.Uniform2f(context, locations.u_pixel_coord_lower),
    'u_scale': new ref_properties.Uniform3f(context, locations.u_scale),
    'u_fade': new ref_properties.Uniform1f(context, locations.u_fade),
    'u_opacity': new ref_properties.Uniform1f(context, locations.u_opacity)
});
const fillExtrusionUniformValues = (matrix, painter, shouldUseVerticalGradient, opacity) => {
    const light = painter.style.light;
    const _lp = light.properties.get('position');
    const lightPos = [
        _lp.x,
        _lp.y,
        _lp.z
    ];
    const lightMat = ref_properties.create$2();
    const anchor = light.properties.get('anchor');
    if (anchor === 'viewport') {
        ref_properties.fromRotation(lightMat, -painter.transform.angle);
        ref_properties.transformMat3(lightPos, lightPos, lightMat);
    }
    const lightColor = light.properties.get('color');
    return {
        'u_matrix': matrix,
        'u_lightpos': lightPos,
        'u_lightintensity': light.properties.get('intensity'),
        'u_lightcolor': [
            lightColor.r,
            lightColor.g,
            lightColor.b
        ],
        'u_vertical_gradient': +shouldUseVerticalGradient,
        'u_opacity': opacity
    };
};
const fillExtrusionPatternUniformValues = (matrix, painter, shouldUseVerticalGradient, opacity, coord, crossfade, tile) => {
    return ref_properties.extend(fillExtrusionUniformValues(matrix, painter, shouldUseVerticalGradient, opacity), patternUniformValues(crossfade, painter, tile), { 'u_height_factor': -Math.pow(2, coord.overscaledZ) / tile.tileSize / 8 });
};

const fillUniforms = (context, locations) => ({ 'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix) });
const fillPatternUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_image': new ref_properties.Uniform1i(context, locations.u_image),
    'u_texsize': new ref_properties.Uniform2f(context, locations.u_texsize),
    'u_pixel_coord_upper': new ref_properties.Uniform2f(context, locations.u_pixel_coord_upper),
    'u_pixel_coord_lower': new ref_properties.Uniform2f(context, locations.u_pixel_coord_lower),
    'u_scale': new ref_properties.Uniform3f(context, locations.u_scale),
    'u_fade': new ref_properties.Uniform1f(context, locations.u_fade)
});
const fillOutlineUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_world': new ref_properties.Uniform2f(context, locations.u_world)
});
const fillOutlinePatternUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_world': new ref_properties.Uniform2f(context, locations.u_world),
    'u_image': new ref_properties.Uniform1i(context, locations.u_image),
    'u_texsize': new ref_properties.Uniform2f(context, locations.u_texsize),
    'u_pixel_coord_upper': new ref_properties.Uniform2f(context, locations.u_pixel_coord_upper),
    'u_pixel_coord_lower': new ref_properties.Uniform2f(context, locations.u_pixel_coord_lower),
    'u_scale': new ref_properties.Uniform3f(context, locations.u_scale),
    'u_fade': new ref_properties.Uniform1f(context, locations.u_fade)
});
const fillUniformValues = matrix => ({ 'u_matrix': matrix });
const fillPatternUniformValues = (matrix, painter, crossfade, tile) => ref_properties.extend(fillUniformValues(matrix), patternUniformValues(crossfade, painter, tile));
const fillOutlineUniformValues = (matrix, drawingBufferSize) => ({
    'u_matrix': matrix,
    'u_world': drawingBufferSize
});
const fillOutlinePatternUniformValues = (matrix, painter, crossfade, tile, drawingBufferSize) => ref_properties.extend(fillPatternUniformValues(matrix, painter, crossfade, tile), { 'u_world': drawingBufferSize });

const circleUniforms = (context, locations) => ({
    'u_camera_to_center_distance': new ref_properties.Uniform1f(context, locations.u_camera_to_center_distance),
    'u_extrude_scale': new ref_properties.Uniform2f(context, locations.u_extrude_scale),
    'u_device_pixel_ratio': new ref_properties.Uniform1f(context, locations.u_device_pixel_ratio),
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix)
});
const circleUniformValues = (painter, coord, tile, layer) => {
    const transform = painter.transform;
    let extrudeScale;
    if (layer.paint.get('circle-pitch-alignment') === 'map') {
        const pixelRatio = pixelsToTileUnits(tile, 1, transform.zoom);
        extrudeScale = [
            pixelRatio,
            pixelRatio
        ];
    } else {
        extrudeScale = transform.pixelsToGLUnits;
    }
    return {
        'u_camera_to_center_distance': transform.cameraToCenterDistance,
        'u_matrix': painter.translatePosMatrix(coord.projMatrix, tile, layer.paint.get('circle-translate'), layer.paint.get('circle-translate-anchor')),
        'u_device_pixel_ratio': ref_properties.exported.devicePixelRatio,
        'u_extrude_scale': extrudeScale
    };
};
const circleDefinesValues = layer => {
    const values = [];
    if (layer.paint.get('circle-pitch-alignment') === 'map')
        values.push('PITCH_WITH_MAP');
    if (layer.paint.get('circle-pitch-scale') === 'map')
        values.push('SCALE_WITH_MAP');
    return values;
};

const collisionUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_camera_to_center_distance': new ref_properties.Uniform1f(context, locations.u_camera_to_center_distance),
    'u_extrude_scale': new ref_properties.Uniform2f(context, locations.u_extrude_scale)
});
const collisionCircleUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_inv_matrix': new ref_properties.UniformMatrix4f(context, locations.u_inv_matrix),
    'u_camera_to_center_distance': new ref_properties.Uniform1f(context, locations.u_camera_to_center_distance),
    'u_viewport_size': new ref_properties.Uniform2f(context, locations.u_viewport_size)
});
const collisionUniformValues = (matrix, transform, tile) => {
    const pixelRatio = ref_properties.EXTENT / tile.tileSize;
    return {
        'u_matrix': matrix,
        'u_camera_to_center_distance': transform.cameraToCenterDistance,
        'u_extrude_scale': [
            transform.pixelsToGLUnits[0] / pixelRatio,
            transform.pixelsToGLUnits[1] / pixelRatio
        ]
    };
};
const collisionCircleUniformValues = (matrix, invMatrix, transform) => {
    return {
        'u_matrix': matrix,
        'u_inv_matrix': invMatrix,
        'u_camera_to_center_distance': transform.cameraToCenterDistance,
        'u_viewport_size': [
            transform.width,
            transform.height
        ]
    };
};

const debugUniforms = (context, locations) => ({
    'u_color': new ref_properties.UniformColor(context, locations.u_color),
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_overlay': new ref_properties.Uniform1i(context, locations.u_overlay),
    'u_overlay_scale': new ref_properties.Uniform1f(context, locations.u_overlay_scale)
});
const debugUniformValues = (matrix, color, scaleRatio = 1) => ({
    'u_matrix': matrix,
    'u_color': color,
    'u_overlay': 0,
    'u_overlay_scale': scaleRatio
});

const heatmapUniforms = (context, locations) => ({
    'u_extrude_scale': new ref_properties.Uniform1f(context, locations.u_extrude_scale),
    'u_intensity': new ref_properties.Uniform1f(context, locations.u_intensity),
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix)
});
const heatmapTextureUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_world': new ref_properties.Uniform2f(context, locations.u_world),
    'u_image': new ref_properties.Uniform1i(context, locations.u_image),
    'u_color_ramp': new ref_properties.Uniform1i(context, locations.u_color_ramp),
    'u_opacity': new ref_properties.Uniform1f(context, locations.u_opacity)
});
const heatmapUniformValues = (matrix, tile, zoom, intensity) => ({
    'u_matrix': matrix,
    'u_extrude_scale': pixelsToTileUnits(tile, 1, zoom),
    'u_intensity': intensity
});
const heatmapTextureUniformValues = (painter, layer, textureUnit, colorRampUnit) => {
    const matrix = ref_properties.create$1();
    ref_properties.ortho(matrix, 0, painter.width, painter.height, 0, 0, 1);
    const gl = painter.context.gl;
    return {
        'u_matrix': matrix,
        'u_world': [
            gl.drawingBufferWidth,
            gl.drawingBufferHeight
        ],
        'u_image': textureUnit,
        'u_color_ramp': colorRampUnit,
        'u_opacity': layer.paint.get('heatmap-opacity')
    };
};

const lineUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_ratio': new ref_properties.Uniform1f(context, locations.u_ratio),
    'u_device_pixel_ratio': new ref_properties.Uniform1f(context, locations.u_device_pixel_ratio),
    'u_units_to_pixels': new ref_properties.Uniform2f(context, locations.u_units_to_pixels)
});
const lineGradientUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_ratio': new ref_properties.Uniform1f(context, locations.u_ratio),
    'u_device_pixel_ratio': new ref_properties.Uniform1f(context, locations.u_device_pixel_ratio),
    'u_units_to_pixels': new ref_properties.Uniform2f(context, locations.u_units_to_pixels),
    'u_image': new ref_properties.Uniform1i(context, locations.u_image),
    'u_image_height': new ref_properties.Uniform1f(context, locations.u_image_height)
});
const linePatternUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_texsize': new ref_properties.Uniform2f(context, locations.u_texsize),
    'u_ratio': new ref_properties.Uniform1f(context, locations.u_ratio),
    'u_device_pixel_ratio': new ref_properties.Uniform1f(context, locations.u_device_pixel_ratio),
    'u_image': new ref_properties.Uniform1i(context, locations.u_image),
    'u_units_to_pixels': new ref_properties.Uniform2f(context, locations.u_units_to_pixels),
    'u_scale': new ref_properties.Uniform3f(context, locations.u_scale),
    'u_fade': new ref_properties.Uniform1f(context, locations.u_fade)
});
const lineSDFUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_texsize': new ref_properties.Uniform2f(context, locations.u_texsize),
    'u_ratio': new ref_properties.Uniform1f(context, locations.u_ratio),
    'u_device_pixel_ratio': new ref_properties.Uniform1f(context, locations.u_device_pixel_ratio),
    'u_units_to_pixels': new ref_properties.Uniform2f(context, locations.u_units_to_pixels),
    'u_image': new ref_properties.Uniform1i(context, locations.u_image),
    'u_scale': new ref_properties.Uniform3f(context, locations.u_scale),
    'u_mix': new ref_properties.Uniform1f(context, locations.u_mix)
});
const lineUniformValues = (painter, tile, layer, matrix) => {
    const transform = painter.transform;
    return {
        'u_matrix': calculateMatrix(painter, tile, layer, matrix),
        'u_ratio': 1 / pixelsToTileUnits(tile, 1, transform.zoom),
        'u_device_pixel_ratio': ref_properties.exported.devicePixelRatio,
        'u_units_to_pixels': [
            1 / transform.pixelsToGLUnits[0],
            1 / transform.pixelsToGLUnits[1]
        ]
    };
};
const lineGradientUniformValues = (painter, tile, layer, matrix, imageHeight) => {
    return ref_properties.extend(lineUniformValues(painter, tile, layer, matrix), {
        'u_image': 0,
        'u_image_height': imageHeight
    });
};
const linePatternUniformValues = (painter, tile, layer, crossfade, matrix) => {
    const transform = painter.transform;
    const tileZoomRatio = calculateTileRatio(tile, transform);
    return {
        'u_matrix': calculateMatrix(painter, tile, layer, matrix),
        'u_texsize': tile.imageAtlasTexture.size,
        'u_ratio': 1 / pixelsToTileUnits(tile, 1, transform.zoom),
        'u_device_pixel_ratio': ref_properties.exported.devicePixelRatio,
        'u_image': 0,
        'u_scale': [
            tileZoomRatio,
            crossfade.fromScale,
            crossfade.toScale
        ],
        'u_fade': crossfade.t,
        'u_units_to_pixels': [
            1 / transform.pixelsToGLUnits[0],
            1 / transform.pixelsToGLUnits[1]
        ]
    };
};
const lineSDFUniformValues = (painter, tile, layer, crossfade, matrix) => {
    const tileZoomRatio = calculateTileRatio(tile, painter.transform);
    return ref_properties.extend(lineUniformValues(painter, tile, layer, matrix), {
        'u_texsize': tile.lineAtlasTexture.size,
        'u_scale': [
            tileZoomRatio,
            crossfade.fromScale,
            crossfade.toScale
        ],
        'u_image': 0,
        'u_mix': crossfade.t
    });
};
function calculateTileRatio(tile, transform) {
    return 1 / pixelsToTileUnits(tile, 1, transform.tileZoom);
}
function calculateMatrix(painter, tile, layer, matrix) {
    return painter.translatePosMatrix(matrix ? matrix : tile.tileID.projMatrix, tile, layer.paint.get('line-translate'), layer.paint.get('line-translate-anchor'));
}

const rasterUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_tl_parent': new ref_properties.Uniform2f(context, locations.u_tl_parent),
    'u_scale_parent': new ref_properties.Uniform1f(context, locations.u_scale_parent),
    'u_buffer_scale': new ref_properties.Uniform1f(context, locations.u_buffer_scale),
    'u_fade_t': new ref_properties.Uniform1f(context, locations.u_fade_t),
    'u_opacity': new ref_properties.Uniform1f(context, locations.u_opacity),
    'u_image0': new ref_properties.Uniform1i(context, locations.u_image0),
    'u_image1': new ref_properties.Uniform1i(context, locations.u_image1),
    'u_brightness_low': new ref_properties.Uniform1f(context, locations.u_brightness_low),
    'u_brightness_high': new ref_properties.Uniform1f(context, locations.u_brightness_high),
    'u_saturation_factor': new ref_properties.Uniform1f(context, locations.u_saturation_factor),
    'u_contrast_factor': new ref_properties.Uniform1f(context, locations.u_contrast_factor),
    'u_spin_weights': new ref_properties.Uniform3f(context, locations.u_spin_weights)
});
const rasterUniformValues = (matrix, parentTL, parentScaleBy, fade, layer) => ({
    'u_matrix': matrix,
    'u_tl_parent': parentTL,
    'u_scale_parent': parentScaleBy,
    'u_buffer_scale': 1,
    'u_fade_t': fade.mix,
    'u_opacity': fade.opacity * layer.paint.get('raster-opacity'),
    'u_image0': 0,
    'u_image1': 1,
    'u_brightness_low': layer.paint.get('raster-brightness-min'),
    'u_brightness_high': layer.paint.get('raster-brightness-max'),
    'u_saturation_factor': saturationFactor(layer.paint.get('raster-saturation')),
    'u_contrast_factor': contrastFactor(layer.paint.get('raster-contrast')),
    'u_spin_weights': spinWeights(layer.paint.get('raster-hue-rotate'))
});
function spinWeights(angle) {
    angle *= Math.PI / 180;
    const s = Math.sin(angle);
    const c = Math.cos(angle);
    return [
        (2 * c + 1) / 3,
        (-Math.sqrt(3) * s - c + 1) / 3,
        (Math.sqrt(3) * s - c + 1) / 3
    ];
}
function contrastFactor(contrast) {
    return contrast > 0 ? 1 / (1 - contrast) : 1 + contrast;
}
function saturationFactor(saturation) {
    return saturation > 0 ? 1 - 1 / (1.001 - saturation) : -saturation;
}

const symbolIconUniforms = (context, locations) => ({
    'u_is_size_zoom_constant': new ref_properties.Uniform1i(context, locations.u_is_size_zoom_constant),
    'u_is_size_feature_constant': new ref_properties.Uniform1i(context, locations.u_is_size_feature_constant),
    'u_size_t': new ref_properties.Uniform1f(context, locations.u_size_t),
    'u_size': new ref_properties.Uniform1f(context, locations.u_size),
    'u_camera_to_center_distance': new ref_properties.Uniform1f(context, locations.u_camera_to_center_distance),
    'u_pitch': new ref_properties.Uniform1f(context, locations.u_pitch),
    'u_rotate_symbol': new ref_properties.Uniform1i(context, locations.u_rotate_symbol),
    'u_aspect_ratio': new ref_properties.Uniform1f(context, locations.u_aspect_ratio),
    'u_fade_change': new ref_properties.Uniform1f(context, locations.u_fade_change),
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_label_plane_matrix': new ref_properties.UniformMatrix4f(context, locations.u_label_plane_matrix),
    'u_coord_matrix': new ref_properties.UniformMatrix4f(context, locations.u_coord_matrix),
    'u_is_text': new ref_properties.Uniform1i(context, locations.u_is_text),
    'u_pitch_with_map': new ref_properties.Uniform1i(context, locations.u_pitch_with_map),
    'u_texsize': new ref_properties.Uniform2f(context, locations.u_texsize),
    'u_texture': new ref_properties.Uniform1i(context, locations.u_texture)
});
const symbolSDFUniforms = (context, locations) => ({
    'u_is_size_zoom_constant': new ref_properties.Uniform1i(context, locations.u_is_size_zoom_constant),
    'u_is_size_feature_constant': new ref_properties.Uniform1i(context, locations.u_is_size_feature_constant),
    'u_size_t': new ref_properties.Uniform1f(context, locations.u_size_t),
    'u_size': new ref_properties.Uniform1f(context, locations.u_size),
    'u_camera_to_center_distance': new ref_properties.Uniform1f(context, locations.u_camera_to_center_distance),
    'u_pitch': new ref_properties.Uniform1f(context, locations.u_pitch),
    'u_rotate_symbol': new ref_properties.Uniform1i(context, locations.u_rotate_symbol),
    'u_aspect_ratio': new ref_properties.Uniform1f(context, locations.u_aspect_ratio),
    'u_fade_change': new ref_properties.Uniform1f(context, locations.u_fade_change),
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_label_plane_matrix': new ref_properties.UniformMatrix4f(context, locations.u_label_plane_matrix),
    'u_coord_matrix': new ref_properties.UniformMatrix4f(context, locations.u_coord_matrix),
    'u_is_text': new ref_properties.Uniform1i(context, locations.u_is_text),
    'u_pitch_with_map': new ref_properties.Uniform1i(context, locations.u_pitch_with_map),
    'u_texsize': new ref_properties.Uniform2f(context, locations.u_texsize),
    'u_texture': new ref_properties.Uniform1i(context, locations.u_texture),
    'u_gamma_scale': new ref_properties.Uniform1f(context, locations.u_gamma_scale),
    'u_device_pixel_ratio': new ref_properties.Uniform1f(context, locations.u_device_pixel_ratio),
    'u_is_halo': new ref_properties.Uniform1i(context, locations.u_is_halo)
});
const symbolTextAndIconUniforms = (context, locations) => ({
    'u_is_size_zoom_constant': new ref_properties.Uniform1i(context, locations.u_is_size_zoom_constant),
    'u_is_size_feature_constant': new ref_properties.Uniform1i(context, locations.u_is_size_feature_constant),
    'u_size_t': new ref_properties.Uniform1f(context, locations.u_size_t),
    'u_size': new ref_properties.Uniform1f(context, locations.u_size),
    'u_camera_to_center_distance': new ref_properties.Uniform1f(context, locations.u_camera_to_center_distance),
    'u_pitch': new ref_properties.Uniform1f(context, locations.u_pitch),
    'u_rotate_symbol': new ref_properties.Uniform1i(context, locations.u_rotate_symbol),
    'u_aspect_ratio': new ref_properties.Uniform1f(context, locations.u_aspect_ratio),
    'u_fade_change': new ref_properties.Uniform1f(context, locations.u_fade_change),
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_label_plane_matrix': new ref_properties.UniformMatrix4f(context, locations.u_label_plane_matrix),
    'u_coord_matrix': new ref_properties.UniformMatrix4f(context, locations.u_coord_matrix),
    'u_is_text': new ref_properties.Uniform1i(context, locations.u_is_text),
    'u_pitch_with_map': new ref_properties.Uniform1i(context, locations.u_pitch_with_map),
    'u_texsize': new ref_properties.Uniform2f(context, locations.u_texsize),
    'u_texsize_icon': new ref_properties.Uniform2f(context, locations.u_texsize_icon),
    'u_texture': new ref_properties.Uniform1i(context, locations.u_texture),
    'u_texture_icon': new ref_properties.Uniform1i(context, locations.u_texture_icon),
    'u_gamma_scale': new ref_properties.Uniform1f(context, locations.u_gamma_scale),
    'u_device_pixel_ratio': new ref_properties.Uniform1f(context, locations.u_device_pixel_ratio),
    'u_is_halo': new ref_properties.Uniform1i(context, locations.u_is_halo)
});
const symbolIconUniformValues = (functionType, size, rotateInShader, pitchWithMap, painter, matrix, labelPlaneMatrix, glCoordMatrix, isText, texSize) => {
    const transform = painter.transform;
    return {
        'u_is_size_zoom_constant': +(functionType === 'constant' || functionType === 'source'),
        'u_is_size_feature_constant': +(functionType === 'constant' || functionType === 'camera'),
        'u_size_t': size ? size.uSizeT : 0,
        'u_size': size ? size.uSize : 0,
        'u_camera_to_center_distance': transform.cameraToCenterDistance,
        'u_pitch': transform.pitch / 360 * 2 * Math.PI,
        'u_rotate_symbol': +rotateInShader,
        'u_aspect_ratio': transform.width / transform.height,
        'u_fade_change': painter.options.fadeDuration ? painter.symbolFadeChange : 1,
        'u_matrix': matrix,
        'u_label_plane_matrix': labelPlaneMatrix,
        'u_coord_matrix': glCoordMatrix,
        'u_is_text': +isText,
        'u_pitch_with_map': +pitchWithMap,
        'u_texsize': texSize,
        'u_texture': 0
    };
};
const symbolSDFUniformValues = (functionType, size, rotateInShader, pitchWithMap, painter, matrix, labelPlaneMatrix, glCoordMatrix, isText, texSize, isHalo) => {
    const {cameraToCenterDistance, _pitch} = painter.transform;
    return ref_properties.extend(symbolIconUniformValues(functionType, size, rotateInShader, pitchWithMap, painter, matrix, labelPlaneMatrix, glCoordMatrix, isText, texSize), {
        'u_gamma_scale': pitchWithMap ? cameraToCenterDistance * Math.cos(painter.terrain ? 0 : _pitch) : 1,
        'u_device_pixel_ratio': ref_properties.exported.devicePixelRatio,
        'u_is_halo': +isHalo
    });
};
const symbolTextAndIconUniformValues = (functionType, size, rotateInShader, pitchWithMap, painter, matrix, labelPlaneMatrix, glCoordMatrix, texSizeSDF, texSizeIcon) => {
    return ref_properties.extend(symbolSDFUniformValues(functionType, size, rotateInShader, pitchWithMap, painter, matrix, labelPlaneMatrix, glCoordMatrix, true, texSizeSDF, true), {
        'u_texsize_icon': texSizeIcon,
        'u_texture_icon': 1
    });
};

const backgroundUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_opacity': new ref_properties.Uniform1f(context, locations.u_opacity),
    'u_color': new ref_properties.UniformColor(context, locations.u_color)
});
const backgroundPatternUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_opacity': new ref_properties.Uniform1f(context, locations.u_opacity),
    'u_image': new ref_properties.Uniform1i(context, locations.u_image),
    'u_pattern_tl_a': new ref_properties.Uniform2f(context, locations.u_pattern_tl_a),
    'u_pattern_br_a': new ref_properties.Uniform2f(context, locations.u_pattern_br_a),
    'u_pattern_tl_b': new ref_properties.Uniform2f(context, locations.u_pattern_tl_b),
    'u_pattern_br_b': new ref_properties.Uniform2f(context, locations.u_pattern_br_b),
    'u_texsize': new ref_properties.Uniform2f(context, locations.u_texsize),
    'u_mix': new ref_properties.Uniform1f(context, locations.u_mix),
    'u_pattern_size_a': new ref_properties.Uniform2f(context, locations.u_pattern_size_a),
    'u_pattern_size_b': new ref_properties.Uniform2f(context, locations.u_pattern_size_b),
    'u_scale_a': new ref_properties.Uniform1f(context, locations.u_scale_a),
    'u_scale_b': new ref_properties.Uniform1f(context, locations.u_scale_b),
    'u_pixel_coord_upper': new ref_properties.Uniform2f(context, locations.u_pixel_coord_upper),
    'u_pixel_coord_lower': new ref_properties.Uniform2f(context, locations.u_pixel_coord_lower),
    'u_tile_units_to_pixels': new ref_properties.Uniform1f(context, locations.u_tile_units_to_pixels)
});
const backgroundUniformValues = (matrix, opacity, color) => ({
    'u_matrix': matrix,
    'u_opacity': opacity,
    'u_color': color
});
const backgroundPatternUniformValues = (matrix, opacity, painter, image, tile, crossfade) => ref_properties.extend(bgPatternUniformValues(image, crossfade, painter, tile), {
    'u_matrix': matrix,
    'u_opacity': opacity
});

const skyboxUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_sun_direction': new ref_properties.Uniform3f(context, locations.u_sun_direction),
    'u_cubemap': new ref_properties.Uniform1i(context, locations.u_cubemap),
    'u_opacity': new ref_properties.Uniform1f(context, locations.u_opacity),
    'u_temporal_offset': new ref_properties.Uniform1f(context, locations.u_temporal_offset)
});
const skyboxUniformValues = (matrix, sunDirection, cubemap, opacity, temporalOffset) => ({
    'u_matrix': matrix,
    'u_sun_direction': sunDirection,
    'u_cubemap': cubemap,
    'u_opacity': opacity,
    'u_temporal_offset': temporalOffset
});
const skyboxGradientUniforms = (context, locations) => ({
    'u_matrix': new ref_properties.UniformMatrix4f(context, locations.u_matrix),
    'u_color_ramp': new ref_properties.Uniform1i(context, locations.u_color_ramp),
    'u_center_direction': new ref_properties.Uniform3f(context, locations.u_center_direction),
    'u_radius': new ref_properties.Uniform1f(context, locations.u_radius),
    'u_opacity': new ref_properties.Uniform1f(context, locations.u_opacity),
    'u_temporal_offset': new ref_properties.Uniform1f(context, locations.u_temporal_offset)
});
const skyboxGradientUniformValues = (matrix, centerDirection, radius, opacity, temporalOffset) => {
    return {
        'u_matrix': matrix,
        'u_color_ramp': 0,
        'u_center_direction': centerDirection,
        'u_radius': ref_properties.degToRad(radius),
        'u_opacity': opacity,
        'u_temporal_offset': temporalOffset
    };
};

const skyboxCaptureUniforms = (context, locations) => ({
    'u_matrix_3f': new ref_properties.UniformMatrix3f(context, locations.u_matrix_3f),
    'u_sun_direction': new ref_properties.Uniform3f(context, locations.u_sun_direction),
    'u_sun_intensity': new ref_properties.Uniform1f(context, locations.u_sun_intensity),
    'u_color_tint_r': new ref_properties.Uniform4f(context, locations.u_color_tint_r),
    'u_color_tint_m': new ref_properties.Uniform4f(context, locations.u_color_tint_m),
    'u_luminance': new ref_properties.Uniform1f(context, locations.u_luminance)
});
const skyboxCaptureUniformValues = (matrix, sunDirection, sunIntensity, atmosphereColor, atmosphereHaloColor) => ({
    'u_matrix_3f': matrix,
    'u_sun_direction': sunDirection,
    'u_sun_intensity': sunIntensity,
    'u_color_tint_r': [
        atmosphereColor.r,
        atmosphereColor.g,
        atmosphereColor.b,
        atmosphereColor.a
    ],
    'u_color_tint_m': [
        atmosphereHaloColor.r,
        atmosphereHaloColor.g,
        atmosphereHaloColor.b,
        atmosphereHaloColor.a
    ],
    'u_luminance': 0.00005
});

const programUniforms = {
    fillExtrusion: fillExtrusionUniforms,
    fillExtrusionPattern: fillExtrusionPatternUniforms,
    fill: fillUniforms,
    fillPattern: fillPatternUniforms,
    fillOutline: fillOutlineUniforms,
    fillOutlinePattern: fillOutlinePatternUniforms,
    circle: circleUniforms,
    collisionBox: collisionUniforms,
    collisionCircle: collisionCircleUniforms,
    debug: debugUniforms,
    clippingMask: clippingMaskUniforms,
    heatmap: heatmapUniforms,
    heatmapTexture: heatmapTextureUniforms,
    hillshade: hillshadeUniforms,
    hillshadePrepare: hillshadePrepareUniforms,
    line: lineUniforms,
    lineGradient: lineGradientUniforms,
    linePattern: linePatternUniforms,
    lineSDF: lineSDFUniforms,
    raster: rasterUniforms,
    symbolIcon: symbolIconUniforms,
    symbolSDF: symbolSDFUniforms,
    symbolTextAndIcon: symbolTextAndIconUniforms,
    background: backgroundUniforms,
    backgroundPattern: backgroundPatternUniforms,
    terrainRaster: terrainRasterUniforms,
    terrainDepth: terrainRasterUniforms,
    skybox: skyboxUniforms,
    skyboxGradient: skyboxGradientUniforms,
    skyboxCapture: skyboxCaptureUniforms
};

let quadTriangles;
function drawCollisionDebug(painter, sourceCache, layer, coords, translate, translateAnchor, isText) {
    const context = painter.context;
    const gl = context.gl;
    const program = painter.useProgram('collisionBox');
    const tileBatches = [];
    let circleCount = 0;
    let circleOffset = 0;
    for (let i = 0; i < coords.length; i++) {
        const coord = coords[i];
        const tile = sourceCache.getTile(coord);
        const bucket = tile.getBucket(layer);
        if (!bucket)
            continue;
        let posMatrix = coord.projMatrix;
        if (translate[0] !== 0 || translate[1] !== 0) {
            posMatrix = painter.translatePosMatrix(coord.projMatrix, tile, translate, translateAnchor);
        }
        const buffers = isText ? bucket.textCollisionBox : bucket.iconCollisionBox;
        const circleArray = bucket.collisionCircleArray;
        if (circleArray.length > 0) {
            const invTransform = ref_properties.create$1();
            const transform = posMatrix;
            ref_properties.mul$1(invTransform, bucket.placementInvProjMatrix, painter.transform.glCoordMatrix);
            ref_properties.mul$1(invTransform, invTransform, bucket.placementViewportMatrix);
            tileBatches.push({
                circleArray,
                circleOffset,
                transform,
                invTransform
            });
            circleCount += circleArray.length / 4;
            circleOffset = circleCount;
        }
        if (!buffers)
            continue;
        if (painter.terrain)
            painter.terrain.setupElevationDraw(tile, program);
        program.draw(context, gl.LINES, ref_properties.DepthMode.disabled, ref_properties.StencilMode.disabled, painter.colorModeForRenderPass(), ref_properties.CullFaceMode.disabled, collisionUniformValues(posMatrix, painter.transform, tile), layer.id, buffers.layoutVertexBuffer, buffers.indexBuffer, buffers.segments, null, painter.transform.zoom, null, buffers.collisionVertexBuffer, buffers.collisionVertexBufferExt);
    }
    if (!isText || !tileBatches.length) {
        return;
    }
    const circleProgram = painter.useProgram('collisionCircle');
    const vertexData = new ref_properties.StructArrayLayout2f1f2i16();
    vertexData.resize(circleCount * 4);
    vertexData._trim();
    let vertexOffset = 0;
    for (const batch of tileBatches) {
        for (let i = 0; i < batch.circleArray.length / 4; i++) {
            const circleIdx = i * 4;
            const x = batch.circleArray[circleIdx + 0];
            const y = batch.circleArray[circleIdx + 1];
            const radius = batch.circleArray[circleIdx + 2];
            const collision = batch.circleArray[circleIdx + 3];
            vertexData.emplace(vertexOffset++, x, y, radius, collision, 0);
            vertexData.emplace(vertexOffset++, x, y, radius, collision, 1);
            vertexData.emplace(vertexOffset++, x, y, radius, collision, 2);
            vertexData.emplace(vertexOffset++, x, y, radius, collision, 3);
        }
    }
    if (!quadTriangles || quadTriangles.length < circleCount * 2) {
        quadTriangles = createQuadTriangles(circleCount);
    }
    const indexBuffer = context.createIndexBuffer(quadTriangles, true);
    const vertexBuffer = context.createVertexBuffer(vertexData, ref_properties.collisionCircleLayout.members, true);
    for (const batch of tileBatches) {
        const uniforms = collisionCircleUniformValues(batch.transform, batch.invTransform, painter.transform);
        circleProgram.draw(context, gl.TRIANGLES, ref_properties.DepthMode.disabled, ref_properties.StencilMode.disabled, painter.colorModeForRenderPass(), ref_properties.CullFaceMode.disabled, uniforms, layer.id, vertexBuffer, indexBuffer, ref_properties.SegmentVector.simpleSegment(0, batch.circleOffset * 2, batch.circleArray.length, batch.circleArray.length / 2), null, painter.transform.zoom, null, null, null);
    }
    vertexBuffer.destroy();
    indexBuffer.destroy();
}
function createQuadTriangles(quadCount) {
    const triCount = quadCount * 2;
    const array = new ref_properties.StructArrayLayout3ui6();
    array.resize(triCount);
    array._trim();
    for (let i = 0; i < triCount; i++) {
        const idx = i * 6;
        array.uint16[idx + 0] = i * 4 + 0;
        array.uint16[idx + 1] = i * 4 + 1;
        array.uint16[idx + 2] = i * 4 + 2;
        array.uint16[idx + 3] = i * 4 + 2;
        array.uint16[idx + 4] = i * 4 + 3;
        array.uint16[idx + 5] = i * 4 + 0;
    }
    return array;
}

const identityMat4 = ref_properties.identity(new Float32Array(16));
function drawSymbols(painter, sourceCache, layer, coords, variableOffsets) {
    if (painter.renderPass !== 'translucent')
        return;
    const stencilMode = ref_properties.StencilMode.disabled;
    const colorMode = painter.colorModeForRenderPass();
    const variablePlacement = layer.layout.get('text-variable-anchor');
    if (variablePlacement) {
        updateVariableAnchors(coords, painter, layer, sourceCache, layer.layout.get('text-rotation-alignment'), layer.layout.get('text-pitch-alignment'), variableOffsets);
    }
    if (layer.paint.get('icon-opacity').constantOr(1) !== 0) {
        drawLayerSymbols(painter, sourceCache, layer, coords, false, layer.paint.get('icon-translate'), layer.paint.get('icon-translate-anchor'), layer.layout.get('icon-rotation-alignment'), layer.layout.get('icon-pitch-alignment'), layer.layout.get('icon-keep-upright'), stencilMode, colorMode);
    }
    if (layer.paint.get('text-opacity').constantOr(1) !== 0) {
        drawLayerSymbols(painter, sourceCache, layer, coords, true, layer.paint.get('text-translate'), layer.paint.get('text-translate-anchor'), layer.layout.get('text-rotation-alignment'), layer.layout.get('text-pitch-alignment'), layer.layout.get('text-keep-upright'), stencilMode, colorMode);
    }
    if (sourceCache.map.showCollisionBoxes) {
        drawCollisionDebug(painter, sourceCache, layer, coords, layer.paint.get('text-translate'), layer.paint.get('text-translate-anchor'), true);
        drawCollisionDebug(painter, sourceCache, layer, coords, layer.paint.get('icon-translate'), layer.paint.get('icon-translate-anchor'), false);
    }
}
function calculateVariableRenderShift(anchor, width, height, textOffset, textBoxScale, renderTextSize) {
    const {horizontalAlign, verticalAlign} = ref_properties.getAnchorAlignment(anchor);
    const shiftX = -(horizontalAlign - 0.5) * width;
    const shiftY = -(verticalAlign - 0.5) * height;
    const variableOffset = ref_properties.evaluateVariableOffset(anchor, textOffset);
    return new ref_properties.pointGeometry((shiftX / textBoxScale + variableOffset[0]) * renderTextSize, (shiftY / textBoxScale + variableOffset[1]) * renderTextSize);
}
function updateVariableAnchors(coords, painter, layer, sourceCache, rotationAlignment, pitchAlignment, variableOffsets) {
    const tr = painter.transform;
    const rotateWithMap = rotationAlignment === 'map';
    const pitchWithMap = pitchAlignment === 'map';
    for (const coord of coords) {
        const tile = sourceCache.getTile(coord);
        const bucket = tile.getBucket(layer);
        if (!bucket || !bucket.text || !bucket.text.segments.get().length)
            continue;
        const sizeData = bucket.textSizeData;
        const size = ref_properties.evaluateSizeForZoom(sizeData, tr.zoom);
        const pixelToTileScale = pixelsToTileUnits(tile, 1, painter.transform.zoom);
        const labelPlaneMatrix = getLabelPlaneMatrix(coord.projMatrix, pitchWithMap, rotateWithMap, painter.transform, pixelToTileScale);
        const updateTextFitIcon = layer.layout.get('icon-text-fit') !== 'none' && bucket.hasIconData();
        if (size) {
            const tileScale = Math.pow(2, tr.zoom - tile.tileID.overscaledZ);
            const elevation = tr.elevation;
            const getElevation = elevation ? p => elevation.getAtTileOffset(coord, p.x, p.y) : _ => 0;
            updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, variableOffsets, ref_properties.symbolSize, tr, labelPlaneMatrix, coord.projMatrix, tileScale, size, updateTextFitIcon, getElevation);
        }
    }
}
function updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, variableOffsets, symbolSize, transform, labelPlaneMatrix, posMatrix, tileScale, size, updateTextFitIcon, getElevation) {
    const placedSymbols = bucket.text.placedSymbolArray;
    const dynamicTextLayoutVertexArray = bucket.text.dynamicLayoutVertexArray;
    const dynamicIconLayoutVertexArray = bucket.icon.dynamicLayoutVertexArray;
    const placedTextShifts = {};
    dynamicTextLayoutVertexArray.clear();
    for (let s = 0; s < placedSymbols.length; s++) {
        const symbol = placedSymbols.get(s);
        const skipOrientation = bucket.allowVerticalPlacement && !symbol.placedOrientation;
        const variableOffset = !symbol.hidden && symbol.crossTileID && !skipOrientation ? variableOffsets[symbol.crossTileID] : null;
        if (!variableOffset) {
            hideGlyphs(symbol.numGlyphs, dynamicTextLayoutVertexArray);
        } else {
            const tileAnchor = new ref_properties.pointGeometry(symbol.anchorX, symbol.anchorY);
            const elevation = getElevation(tileAnchor);
            const projectedAnchor = project(tileAnchor, pitchWithMap ? posMatrix : labelPlaneMatrix, elevation);
            const perspectiveRatio = getPerspectiveRatio(transform.cameraToCenterDistance, projectedAnchor.signedDistanceFromCamera);
            let renderTextSize = symbolSize.evaluateSizeForFeature(bucket.textSizeData, size, symbol) * perspectiveRatio / ref_properties.ONE_EM;
            if (pitchWithMap) {
                renderTextSize *= bucket.tilePixelRatio / tileScale;
            }
            const {width, height, anchor, textOffset, textBoxScale} = variableOffset;
            const shift = calculateVariableRenderShift(anchor, width, height, textOffset, textBoxScale, renderTextSize);
            const shiftedAnchor = pitchWithMap ? project(tileAnchor.add(shift), labelPlaneMatrix, elevation).point : projectedAnchor.point.add(rotateWithMap ? shift.rotate(-transform.angle) : shift);
            const angle = bucket.allowVerticalPlacement && symbol.placedOrientation === ref_properties.WritingMode.vertical ? Math.PI / 2 : 0;
            for (let g = 0; g < symbol.numGlyphs; g++) {
                ref_properties.addDynamicAttributes(dynamicTextLayoutVertexArray, shiftedAnchor, angle);
            }
            if (updateTextFitIcon && symbol.associatedIconIndex >= 0) {
                placedTextShifts[symbol.associatedIconIndex] = {
                    shiftedAnchor,
                    angle
                };
            }
        }
    }
    if (updateTextFitIcon) {
        dynamicIconLayoutVertexArray.clear();
        const placedIcons = bucket.icon.placedSymbolArray;
        for (let i = 0; i < placedIcons.length; i++) {
            const placedIcon = placedIcons.get(i);
            if (placedIcon.hidden) {
                hideGlyphs(placedIcon.numGlyphs, dynamicIconLayoutVertexArray);
            } else {
                const shift = placedTextShifts[i];
                if (!shift) {
                    hideGlyphs(placedIcon.numGlyphs, dynamicIconLayoutVertexArray);
                } else {
                    for (let g = 0; g < placedIcon.numGlyphs; g++) {
                        ref_properties.addDynamicAttributes(dynamicIconLayoutVertexArray, shift.shiftedAnchor, shift.angle);
                    }
                }
            }
        }
        bucket.icon.dynamicLayoutVertexBuffer.updateData(dynamicIconLayoutVertexArray);
    }
    bucket.text.dynamicLayoutVertexBuffer.updateData(dynamicTextLayoutVertexArray);
}
function getSymbolProgramName(isSDF, isText, bucket) {
    if (bucket.iconsInText && isText) {
        return 'symbolTextAndIcon';
    } else if (isSDF) {
        return 'symbolSDF';
    } else {
        return 'symbolIcon';
    }
}
function drawLayerSymbols(painter, sourceCache, layer, coords, isText, translate, translateAnchor, rotationAlignment, pitchAlignment, keepUpright, stencilMode, colorMode) {
    const context = painter.context;
    const gl = context.gl;
    const tr = painter.transform;
    const rotateWithMap = rotationAlignment === 'map';
    const pitchWithMap = pitchAlignment === 'map';
    const alongLine = rotateWithMap && layer.layout.get('symbol-placement') !== 'point';
    const rotateInShader = rotateWithMap && !pitchWithMap && !alongLine;
    const hasSortKey = layer.layout.get('symbol-sort-key').constantOr(1) !== undefined;
    let sortFeaturesByKey = false;
    const depthMode = painter.depthModeForSublayer(0, ref_properties.DepthMode.ReadOnly);
    const variablePlacement = layer.layout.get('text-variable-anchor');
    const tileRenderState = [];
    const defines = painter.terrain && pitchWithMap ? ['PITCH_WITH_MAP_TERRAIN'] : null;
    for (const coord of coords) {
        const tile = sourceCache.getTile(coord);
        const bucket = tile.getBucket(layer);
        if (!bucket)
            continue;
        const buffers = isText ? bucket.text : bucket.icon;
        if (!buffers || !buffers.segments.get().length)
            continue;
        const programConfiguration = buffers.programConfigurations.get(layer.id);
        const isSDF = isText || bucket.sdfIcons;
        const sizeData = isText ? bucket.textSizeData : bucket.iconSizeData;
        const transformed = pitchWithMap || tr.pitch !== 0;
        const program = painter.useProgram(getSymbolProgramName(isSDF, isText, bucket), programConfiguration, defines);
        const size = ref_properties.evaluateSizeForZoom(sizeData, tr.zoom);
        let texSize;
        let texSizeIcon = [
            0,
            0
        ];
        let atlasTexture;
        let atlasInterpolation;
        let atlasTextureIcon = null;
        let atlasInterpolationIcon;
        if (isText) {
            atlasTexture = tile.glyphAtlasTexture;
            atlasInterpolation = gl.LINEAR;
            texSize = tile.glyphAtlasTexture.size;
            if (bucket.iconsInText) {
                texSizeIcon = tile.imageAtlasTexture.size;
                atlasTextureIcon = tile.imageAtlasTexture;
                const zoomDependentSize = sizeData.kind === 'composite' || sizeData.kind === 'camera';
                atlasInterpolationIcon = transformed || painter.options.rotating || painter.options.zooming || zoomDependentSize ? gl.LINEAR : gl.NEAREST;
            }
        } else {
            const iconScaled = layer.layout.get('icon-size').constantOr(0) !== 1 || bucket.iconsNeedLinear;
            atlasTexture = tile.imageAtlasTexture;
            atlasInterpolation = isSDF || painter.options.rotating || painter.options.zooming || iconScaled || transformed ? gl.LINEAR : gl.NEAREST;
            texSize = tile.imageAtlasTexture.size;
        }
        const s = pixelsToTileUnits(tile, 1, painter.transform.zoom);
        const labelPlaneMatrix = getLabelPlaneMatrix(coord.projMatrix, pitchWithMap, rotateWithMap, painter.transform, s);
        const labelPlaneMatrixInv = painter.terrain && pitchWithMap && alongLine ? ref_properties.invert(new Float32Array(16), labelPlaneMatrix) : identityMat4;
        const glCoordMatrix = getGlCoordMatrix(coord.projMatrix, pitchWithMap, rotateWithMap, painter.transform, s);
        const hasVariableAnchors = variablePlacement && bucket.hasTextData();
        const updateTextFitIcon = layer.layout.get('icon-text-fit') !== 'none' && hasVariableAnchors && bucket.hasIconData();
        if (alongLine) {
            const elevation = tr.elevation;
            const getElevation = elevation ? p => elevation.getAtTileOffset(coord, p.x, p.y) : null;
            updateLineLabels(bucket, coord.projMatrix, painter, isText, labelPlaneMatrix, glCoordMatrix, pitchWithMap, keepUpright, getElevation);
        }
        const matrix = painter.translatePosMatrix(coord.projMatrix, tile, translate, translateAnchor), uLabelPlaneMatrix = alongLine || isText && variablePlacement || updateTextFitIcon ? identityMat4 : labelPlaneMatrix, uglCoordMatrix = painter.translatePosMatrix(glCoordMatrix, tile, translate, translateAnchor, true);
        const hasHalo = isSDF && layer.paint.get(isText ? 'text-halo-width' : 'icon-halo-width').constantOr(1) !== 0;
        let uniformValues;
        if (isSDF) {
            if (!bucket.iconsInText) {
                uniformValues = symbolSDFUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, painter, matrix, uLabelPlaneMatrix, uglCoordMatrix, isText, texSize, true);
            } else {
                uniformValues = symbolTextAndIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, painter, matrix, uLabelPlaneMatrix, uglCoordMatrix, texSize, texSizeIcon);
            }
        } else {
            uniformValues = symbolIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, painter, matrix, uLabelPlaneMatrix, uglCoordMatrix, isText, texSize);
        }
        const state = {
            program,
            buffers,
            uniformValues,
            atlasTexture,
            atlasTextureIcon,
            atlasInterpolation,
            atlasInterpolationIcon,
            isSDF,
            hasHalo,
            tile,
            labelPlaneMatrixInv
        };
        if (hasSortKey && bucket.canOverlap) {
            sortFeaturesByKey = true;
            const oldSegments = buffers.segments.get();
            for (const segment of oldSegments) {
                tileRenderState.push({
                    segments: new ref_properties.SegmentVector([segment]),
                    sortKey: segment.sortKey,
                    state
                });
            }
        } else {
            tileRenderState.push({
                segments: buffers.segments,
                sortKey: 0,
                state
            });
        }
    }
    if (sortFeaturesByKey) {
        tileRenderState.sort((a, b) => a.sortKey - b.sortKey);
    }
    for (const segmentState of tileRenderState) {
        const state = segmentState.state;
        if (painter.terrain)
            painter.terrain.setupElevationDraw(state.tile, state.program, {
                useDepthForOcclusion: true,
                labelPlaneMatrixInv: state.labelPlaneMatrixInv
            });
        context.activeTexture.set(gl.TEXTURE0);
        state.atlasTexture.bind(state.atlasInterpolation, gl.CLAMP_TO_EDGE);
        if (state.atlasTextureIcon) {
            context.activeTexture.set(gl.TEXTURE1);
            if (state.atlasTextureIcon) {
                state.atlasTextureIcon.bind(state.atlasInterpolationIcon, gl.CLAMP_TO_EDGE);
            }
        }
        if (state.isSDF) {
            const uniformValues = state.uniformValues;
            if (state.hasHalo) {
                uniformValues['u_is_halo'] = 1;
                drawSymbolElements(state.buffers, segmentState.segments, layer, painter, state.program, depthMode, stencilMode, colorMode, uniformValues);
            }
            uniformValues['u_is_halo'] = 0;
        }
        drawSymbolElements(state.buffers, segmentState.segments, layer, painter, state.program, depthMode, stencilMode, colorMode, state.uniformValues);
    }
}
function drawSymbolElements(buffers, segments, layer, painter, program, depthMode, stencilMode, colorMode, uniformValues) {
    const context = painter.context;
    const gl = context.gl;
    program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, ref_properties.CullFaceMode.disabled, uniformValues, layer.id, buffers.layoutVertexBuffer, buffers.indexBuffer, segments, layer.paint, painter.transform.zoom, buffers.programConfigurations.get(layer.id), buffers.dynamicLayoutVertexBuffer, buffers.opacityVertexBuffer);
}

function drawCircles(painter, sourceCache, layer, coords) {
    if (painter.renderPass !== 'translucent')
        return;
    const opacity = layer.paint.get('circle-opacity');
    const strokeWidth = layer.paint.get('circle-stroke-width');
    const strokeOpacity = layer.paint.get('circle-stroke-opacity');
    const sortFeaturesByKey = layer.layout.get('circle-sort-key').constantOr(1) !== undefined;
    if (opacity.constantOr(1) === 0 && (strokeWidth.constantOr(1) === 0 || strokeOpacity.constantOr(1) === 0)) {
        return;
    }
    const context = painter.context;
    const gl = context.gl;
    const depthMode = painter.depthModeForSublayer(0, ref_properties.DepthMode.ReadOnly);
    const stencilMode = ref_properties.StencilMode.disabled;
    const colorMode = painter.colorModeForRenderPass();
    const segmentsRenderStates = [];
    for (let i = 0; i < coords.length; i++) {
        const coord = coords[i];
        const tile = sourceCache.getTile(coord);
        const bucket = tile.getBucket(layer);
        if (!bucket)
            continue;
        const programConfiguration = bucket.programConfigurations.get(layer.id);
        const definesValues = circleDefinesValues(layer);
        const program = painter.useProgram('circle', programConfiguration, definesValues);
        const layoutVertexBuffer = bucket.layoutVertexBuffer;
        const indexBuffer = bucket.indexBuffer;
        const uniformValues = circleUniformValues(painter, coord, tile, layer);
        const state = {
            programConfiguration,
            program,
            layoutVertexBuffer,
            indexBuffer,
            uniformValues,
            tile
        };
        if (sortFeaturesByKey) {
            const oldSegments = bucket.segments.get();
            for (const segment of oldSegments) {
                segmentsRenderStates.push({
                    segments: new ref_properties.SegmentVector([segment]),
                    sortKey: segment.sortKey,
                    state
                });
            }
        } else {
            segmentsRenderStates.push({
                segments: bucket.segments,
                sortKey: 0,
                state
            });
        }
    }
    if (sortFeaturesByKey) {
        segmentsRenderStates.sort((a, b) => a.sortKey - b.sortKey);
    }
    for (const segmentsState of segmentsRenderStates) {
        const {programConfiguration, program, layoutVertexBuffer, indexBuffer, uniformValues, tile} = segmentsState.state;
        const segments = segmentsState.segments;
        if (painter.terrain)
            painter.terrain.setupElevationDraw(tile, program, { useDepthForOcclusion: true });
        painter.prepareDrawProgram(context, program, tile.tileID.toUnwrapped());
        program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, ref_properties.CullFaceMode.disabled, uniformValues, layer.id, layoutVertexBuffer, indexBuffer, segments, layer.paint, painter.transform.zoom, programConfiguration);
    }
}

function drawHeatmap(painter, sourceCache, layer, coords) {
    if (layer.paint.get('heatmap-opacity') === 0) {
        return;
    }
    if (painter.renderPass === 'offscreen') {
        const context = painter.context;
        const gl = context.gl;
        const stencilMode = ref_properties.StencilMode.disabled;
        const colorMode = new ref_properties.ColorMode([
            gl.ONE,
            gl.ONE
        ], ref_properties.Color.transparent, [
            true,
            true,
            true,
            true
        ]);
        bindFramebuffer(context, painter, layer);
        context.clear({ color: ref_properties.Color.transparent });
        for (let i = 0; i < coords.length; i++) {
            const coord = coords[i];
            if (sourceCache.hasRenderableParent(coord))
                continue;
            const tile = sourceCache.getTile(coord);
            const bucket = tile.getBucket(layer);
            if (!bucket)
                continue;
            const programConfiguration = bucket.programConfigurations.get(layer.id);
            const program = painter.useProgram('heatmap', programConfiguration);
            const {zoom} = painter.transform;
            if (painter.terrain)
                painter.terrain.setupElevationDraw(tile, program);
            painter.prepareDrawProgram(context, program, coord.toUnwrapped());
            program.draw(context, gl.TRIANGLES, ref_properties.DepthMode.disabled, stencilMode, colorMode, ref_properties.CullFaceMode.disabled, heatmapUniformValues(coord.projMatrix, tile, zoom, layer.paint.get('heatmap-intensity')), layer.id, bucket.layoutVertexBuffer, bucket.indexBuffer, bucket.segments, layer.paint, painter.transform.zoom, programConfiguration);
        }
        context.viewport.set([
            0,
            0,
            painter.width,
            painter.height
        ]);
    } else if (painter.renderPass === 'translucent') {
        painter.context.setColorMode(painter.colorModeForRenderPass());
        renderTextureToMap(painter, layer);
    }
}
function bindFramebuffer(context, painter, layer) {
    const gl = context.gl;
    context.activeTexture.set(gl.TEXTURE1);
    context.viewport.set([
        0,
        0,
        painter.width / 4,
        painter.height / 4
    ]);
    let fbo = layer.heatmapFbo;
    if (!fbo) {
        const texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        fbo = layer.heatmapFbo = context.createFramebuffer(painter.width / 4, painter.height / 4, false);
        bindTextureToFramebuffer(context, painter, texture, fbo);
    } else {
        gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
        context.bindFramebuffer.set(fbo.framebuffer);
    }
}
function bindTextureToFramebuffer(context, painter, texture, fbo) {
    const gl = context.gl;
    const internalFormat = context.extRenderToTextureHalfFloat ? context.extTextureHalfFloat.HALF_FLOAT_OES : gl.UNSIGNED_BYTE;
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, painter.width / 4, painter.height / 4, 0, gl.RGBA, internalFormat, null);
    fbo.colorAttachment.set(texture);
}
function renderTextureToMap(painter, layer) {
    const context = painter.context;
    const gl = context.gl;
    const fbo = layer.heatmapFbo;
    if (!fbo)
        return;
    context.activeTexture.set(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
    context.activeTexture.set(gl.TEXTURE1);
    let colorRampTexture = layer.colorRampTexture;
    if (!colorRampTexture) {
        colorRampTexture = layer.colorRampTexture = new ref_properties.Texture(context, layer.colorRamp, gl.RGBA);
    }
    colorRampTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
    painter.useProgram('heatmapTexture').draw(context, gl.TRIANGLES, ref_properties.DepthMode.disabled, ref_properties.StencilMode.disabled, painter.colorModeForRenderPass(), ref_properties.CullFaceMode.disabled, heatmapTextureUniformValues(painter, layer, 0, 1), layer.id, painter.viewportBuffer, painter.quadTriangleIndexBuffer, painter.viewportSegments, layer.paint, painter.transform.zoom);
}

function drawLine(painter, sourceCache, layer, coords) {
    if (painter.renderPass !== 'translucent')
        return;
    const opacity = layer.paint.get('line-opacity');
    const width = layer.paint.get('line-width');
    if (opacity.constantOr(1) === 0 || width.constantOr(1) === 0)
        return;
    const depthMode = painter.depthModeForSublayer(0, ref_properties.DepthMode.ReadOnly);
    const colorMode = painter.colorModeForRenderPass();
    const dasharrayProperty = layer.paint.get('line-dasharray');
    const dasharray = dasharrayProperty.constantOr(1);
    const capProperty = layer.layout.get('line-cap');
    const patternProperty = layer.paint.get('line-pattern');
    const image = patternProperty.constantOr(1);
    const gradient = layer.paint.get('line-gradient');
    const crossfade = layer.getCrossfadeParameters();
    const programId = image ? 'linePattern' : dasharray ? 'lineSDF' : gradient ? 'lineGradient' : 'line';
    const context = painter.context;
    const gl = context.gl;
    for (const coord of coords) {
        const tile = sourceCache.getTile(coord);
        if (image && !tile.patternsLoaded())
            continue;
        const bucket = tile.getBucket(layer);
        if (!bucket)
            continue;
        painter.prepareDrawTile(coord);
        const programConfiguration = bucket.programConfigurations.get(layer.id);
        const program = painter.useProgram(programId, programConfiguration);
        const constantPattern = patternProperty.constantOr(null);
        if (constantPattern && tile.imageAtlas) {
            const atlas = tile.imageAtlas;
            const posTo = atlas.patternPositions[constantPattern.to.toString()];
            const posFrom = atlas.patternPositions[constantPattern.from.toString()];
            if (posTo && posFrom)
                programConfiguration.setConstantPatternPositions(posTo, posFrom);
        }
        const constantDash = dasharrayProperty.constantOr(null);
        const constantCap = capProperty.constantOr(null);
        if (!image && constantDash && constantCap && tile.lineAtlas) {
            const atlas = tile.lineAtlas;
            const posTo = atlas.getDash(constantDash.to, constantCap);
            const posFrom = atlas.getDash(constantDash.from, constantCap);
            if (posTo && posFrom)
                programConfiguration.setConstantPatternPositions(posTo, posFrom);
        }
        const matrix = painter.terrain ? coord.projMatrix : null;
        const uniformValues = image ? linePatternUniformValues(painter, tile, layer, crossfade, matrix) : dasharray ? lineSDFUniformValues(painter, tile, layer, crossfade, matrix) : gradient ? lineGradientUniformValues(painter, tile, layer, matrix, bucket.lineClipsArray.length) : lineUniformValues(painter, tile, layer, matrix);
        if (image) {
            context.activeTexture.set(gl.TEXTURE0);
            tile.imageAtlasTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
            programConfiguration.updatePaintBuffers(crossfade);
        } else if (dasharray) {
            context.activeTexture.set(gl.TEXTURE0);
            tile.lineAtlasTexture.bind(gl.LINEAR, gl.REPEAT);
            programConfiguration.updatePaintBuffers(crossfade);
        } else if (gradient) {
            const layerGradient = bucket.gradients[layer.id];
            let gradientTexture = layerGradient.texture;
            if (layer.gradientVersion !== layerGradient.version) {
                let textureResolution = 256;
                if (layer.stepInterpolant) {
                    const sourceMaxZoom = sourceCache.getSource().maxzoom;
                    const potentialOverzoom = coord.canonical.z === sourceMaxZoom ? Math.ceil(1 << painter.transform.maxZoom - coord.canonical.z) : 1;
                    const lineLength = bucket.maxLineLength / ref_properties.EXTENT;
                    const maxTilePixelSize = 1024;
                    const maxTextureCoverage = lineLength * maxTilePixelSize * potentialOverzoom;
                    textureResolution = ref_properties.clamp(ref_properties.nextPowerOfTwo(maxTextureCoverage), 256, context.maxTextureSize);
                }
                layerGradient.gradient = ref_properties.renderColorRamp({
                    expression: layer.gradientExpression(),
                    evaluationKey: 'lineProgress',
                    resolution: textureResolution,
                    image: layerGradient.gradient || undefined,
                    clips: bucket.lineClipsArray
                });
                if (layerGradient.texture) {
                    layerGradient.texture.update(layerGradient.gradient);
                } else {
                    layerGradient.texture = new ref_properties.Texture(context, layerGradient.gradient, gl.RGBA);
                }
                layerGradient.version = layer.gradientVersion;
                gradientTexture = layerGradient.texture;
            }
            context.activeTexture.set(gl.TEXTURE0);
            gradientTexture.bind(layer.stepInterpolant ? gl.NEAREST : gl.LINEAR, gl.CLAMP_TO_EDGE);
        }
        painter.prepareDrawProgram(context, program, coord.toUnwrapped());
        program.draw(context, gl.TRIANGLES, depthMode, painter.stencilModeForClipping(coord), colorMode, ref_properties.CullFaceMode.disabled, uniformValues, layer.id, bucket.layoutVertexBuffer, bucket.indexBuffer, bucket.segments, layer.paint, painter.transform.zoom, programConfiguration, bucket.layoutVertexBuffer2);
    }
}

function drawFill(painter, sourceCache, layer, coords) {
    const color = layer.paint.get('fill-color');
    const opacity = layer.paint.get('fill-opacity');
    if (opacity.constantOr(1) === 0) {
        return;
    }
    const colorMode = painter.colorModeForRenderPass();
    const pattern = layer.paint.get('fill-pattern');
    const pass = painter.opaquePassEnabledForLayer() && (!pattern.constantOr(1) && color.constantOr(ref_properties.Color.transparent).a === 1 && opacity.constantOr(0) === 1) ? 'opaque' : 'translucent';
    if (painter.renderPass === pass) {
        const depthMode = painter.depthModeForSublayer(1, painter.renderPass === 'opaque' ? ref_properties.DepthMode.ReadWrite : ref_properties.DepthMode.ReadOnly);
        drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode, false);
    }
    if (painter.renderPass === 'translucent' && layer.paint.get('fill-antialias')) {
        const depthMode = painter.depthModeForSublayer(layer.getPaintProperty('fill-outline-color') ? 2 : 0, ref_properties.DepthMode.ReadOnly);
        drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode, true);
    }
}
function drawFillTiles(painter, sourceCache, layer, coords, depthMode, colorMode, isOutline) {
    const gl = painter.context.gl;
    const patternProperty = layer.paint.get('fill-pattern');
    const image = patternProperty && patternProperty.constantOr(1);
    const crossfade = layer.getCrossfadeParameters();
    let drawMode, programName, uniformValues, indexBuffer, segments;
    if (!isOutline) {
        programName = image ? 'fillPattern' : 'fill';
        drawMode = gl.TRIANGLES;
    } else {
        programName = image && !layer.getPaintProperty('fill-outline-color') ? 'fillOutlinePattern' : 'fillOutline';
        drawMode = gl.LINES;
    }
    for (const coord of coords) {
        const tile = sourceCache.getTile(coord);
        if (image && !tile.patternsLoaded())
            continue;
        const bucket = tile.getBucket(layer);
        if (!bucket)
            continue;
        painter.prepareDrawTile(coord);
        const programConfiguration = bucket.programConfigurations.get(layer.id);
        const program = painter.useProgram(programName, programConfiguration);
        if (image) {
            painter.context.activeTexture.set(gl.TEXTURE0);
            tile.imageAtlasTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
            programConfiguration.updatePaintBuffers(crossfade);
        }
        const constantPattern = patternProperty.constantOr(null);
        if (constantPattern && tile.imageAtlas) {
            const atlas = tile.imageAtlas;
            const posTo = atlas.patternPositions[constantPattern.to.toString()];
            const posFrom = atlas.patternPositions[constantPattern.from.toString()];
            if (posTo && posFrom)
                programConfiguration.setConstantPatternPositions(posTo, posFrom);
        }
        const tileMatrix = painter.translatePosMatrix(coord.projMatrix, tile, layer.paint.get('fill-translate'), layer.paint.get('fill-translate-anchor'));
        if (!isOutline) {
            indexBuffer = bucket.indexBuffer;
            segments = bucket.segments;
            uniformValues = image ? fillPatternUniformValues(tileMatrix, painter, crossfade, tile) : fillUniformValues(tileMatrix);
        } else {
            indexBuffer = bucket.indexBuffer2;
            segments = bucket.segments2;
            const drawingBufferSize = painter.terrain && painter.terrain.renderingToTexture ? painter.terrain.drapeBufferSize : [
                gl.drawingBufferWidth,
                gl.drawingBufferHeight
            ];
            uniformValues = programName === 'fillOutlinePattern' && image ? fillOutlinePatternUniformValues(tileMatrix, painter, crossfade, tile, drawingBufferSize) : fillOutlineUniformValues(tileMatrix, drawingBufferSize);
        }
        painter.prepareDrawProgram(painter.context, program, coord.toUnwrapped());
        program.draw(painter.context, drawMode, depthMode, painter.stencilModeForClipping(coord), colorMode, ref_properties.CullFaceMode.disabled, uniformValues, layer.id, bucket.layoutVertexBuffer, indexBuffer, segments, layer.paint, painter.transform.zoom, programConfiguration);
    }
}

function draw$1(painter, source, layer, coords) {
    const opacity = layer.paint.get('fill-extrusion-opacity');
    if (opacity === 0) {
        return;
    }
    if (painter.renderPass === 'translucent') {
        const depthMode = new ref_properties.DepthMode(painter.context.gl.LEQUAL, ref_properties.DepthMode.ReadWrite, painter.depthRangeFor3D);
        if (opacity === 1 && !layer.paint.get('fill-extrusion-pattern').constantOr(1)) {
            const colorMode = painter.colorModeForRenderPass();
            drawExtrusionTiles(painter, source, layer, coords, depthMode, ref_properties.StencilMode.disabled, colorMode);
        } else {
            drawExtrusionTiles(painter, source, layer, coords, depthMode, ref_properties.StencilMode.disabled, ref_properties.ColorMode.disabled);
            drawExtrusionTiles(painter, source, layer, coords, depthMode, painter.stencilModeFor3D(), painter.colorModeForRenderPass());
        }
    }
}
function drawExtrusionTiles(painter, source, layer, coords, depthMode, stencilMode, colorMode) {
    const context = painter.context;
    const gl = context.gl;
    const patternProperty = layer.paint.get('fill-extrusion-pattern');
    const image = patternProperty.constantOr(1);
    const crossfade = layer.getCrossfadeParameters();
    const opacity = layer.paint.get('fill-extrusion-opacity');
    for (const coord of coords) {
        const tile = source.getTile(coord);
        const bucket = tile.getBucket(layer);
        if (!bucket)
            continue;
        const programConfiguration = bucket.programConfigurations.get(layer.id);
        const program = painter.useProgram(image ? 'fillExtrusionPattern' : 'fillExtrusion', programConfiguration);
        if (painter.terrain) {
            const terrain = painter.terrain;
            if (!bucket.enableTerrain)
                continue;
            terrain.setupElevationDraw(tile, program, { useMeterToDem: true });
            flatRoofsUpdate(context, source, coord, bucket, layer, terrain);
            if (!bucket.centroidVertexBuffer) {
                const attrIndex = program.attributes['a_centroid_pos'];
                if (attrIndex !== undefined)
                    gl.vertexAttrib2f(attrIndex, 0, 0);
            }
        }
        if (image) {
            painter.context.activeTexture.set(gl.TEXTURE0);
            tile.imageAtlasTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
            programConfiguration.updatePaintBuffers(crossfade);
        }
        const constantPattern = patternProperty.constantOr(null);
        if (constantPattern && tile.imageAtlas) {
            const atlas = tile.imageAtlas;
            const posTo = atlas.patternPositions[constantPattern.to.toString()];
            const posFrom = atlas.patternPositions[constantPattern.from.toString()];
            if (posTo && posFrom)
                programConfiguration.setConstantPatternPositions(posTo, posFrom);
        }
        const matrix = painter.translatePosMatrix(coord.projMatrix, tile, layer.paint.get('fill-extrusion-translate'), layer.paint.get('fill-extrusion-translate-anchor'));
        const shouldUseVerticalGradient = layer.paint.get('fill-extrusion-vertical-gradient');
        const uniformValues = image ? fillExtrusionPatternUniformValues(matrix, painter, shouldUseVerticalGradient, opacity, coord, crossfade, tile) : fillExtrusionUniformValues(matrix, painter, shouldUseVerticalGradient, opacity);
        painter.prepareDrawProgram(context, program, coord.toUnwrapped());
        program.draw(context, context.gl.TRIANGLES, depthMode, stencilMode, colorMode, ref_properties.CullFaceMode.backCCW, uniformValues, layer.id, bucket.layoutVertexBuffer, bucket.indexBuffer, bucket.segments, layer.paint, painter.transform.zoom, programConfiguration, painter.terrain ? bucket.centroidVertexBuffer : null);
    }
}
function flatRoofsUpdate(context, source, coord, bucket, layer, terrain) {
    const neighborCoord = [
        coord => {
            let x = coord.canonical.x - 1;
            let w = coord.wrap;
            if (x < 0) {
                x = (1 << coord.canonical.z) - 1;
                w--;
            }
            return new ref_properties.OverscaledTileID(coord.overscaledZ, w, coord.canonical.z, x, coord.canonical.y);
        },
        coord => {
            let x = coord.canonical.x + 1;
            let w = coord.wrap;
            if (x === 1 << coord.canonical.z) {
                x = 0;
                w++;
            }
            return new ref_properties.OverscaledTileID(coord.overscaledZ, w, coord.canonical.z, x, coord.canonical.y);
        },
        coord => new ref_properties.OverscaledTileID(coord.overscaledZ, coord.wrap, coord.canonical.z, coord.canonical.x, (coord.canonical.y === 0 ? 1 << coord.canonical.z : coord.canonical.y) - 1),
        coord => new ref_properties.OverscaledTileID(coord.overscaledZ, coord.wrap, coord.canonical.z, coord.canonical.x, coord.canonical.y === (1 << coord.canonical.z) - 1 ? 0 : coord.canonical.y + 1)
    ];
    const getLoadedBucket = nid => {
        const maxzoom = source.getSource().maxzoom;
        const getBucket = key => {
            const n = source.getTileByID(key);
            if (n && n.hasData()) {
                return n.getBucket(layer);
            }
        };
        let b0, b1, b2;
        if (nid.overscaledZ === nid.canonical.z || nid.overscaledZ >= maxzoom)
            b0 = getBucket(nid.key);
        if (nid.overscaledZ >= maxzoom)
            b1 = getBucket(nid.calculateScaledKey(nid.overscaledZ + 1));
        if (nid.overscaledZ > maxzoom)
            b2 = getBucket(nid.calculateScaledKey(nid.overscaledZ - 1));
        return b0 || b1 || b2;
    };
    const projectedToBorder = [
        0,
        0,
        0
    ];
    const xjoin = (a, b) => {
        projectedToBorder[0] = Math.min(a.min.y, b.min.y);
        projectedToBorder[1] = Math.max(a.max.y, b.max.y);
        projectedToBorder[2] = ref_properties.EXTENT - b.min.x > a.max.x ? b.min.x - ref_properties.EXTENT : a.max.x;
        return projectedToBorder;
    };
    const yjoin = (a, b) => {
        projectedToBorder[0] = Math.min(a.min.x, b.min.x);
        projectedToBorder[1] = Math.max(a.max.x, b.max.x);
        projectedToBorder[2] = ref_properties.EXTENT - b.min.y > a.max.y ? b.min.y - ref_properties.EXTENT : a.max.y;
        return projectedToBorder;
    };
    const projectCombinedSpanToBorder = [
        (a, b) => xjoin(a, b),
        (a, b) => xjoin(b, a),
        (a, b) => yjoin(a, b),
        (a, b) => yjoin(b, a)
    ];
    const centroid = new ref_properties.pointGeometry(0, 0);
    const error = 3;
    let demTile, neighborDEMTile, neighborTileID;
    const flatBase = (min, max, edge, verticalEdge, maxOffsetFromBorder) => {
        const points = [
            [
                verticalEdge ? edge : min,
                verticalEdge ? min : edge,
                0
            ],
            [
                verticalEdge ? edge : max,
                verticalEdge ? max : edge,
                0
            ]
        ];
        const coord3 = maxOffsetFromBorder < 0 ? ref_properties.EXTENT + maxOffsetFromBorder : maxOffsetFromBorder;
        const thirdPoint = [
            verticalEdge ? coord3 : (min + max) / 2,
            verticalEdge ? (min + max) / 2 : coord3,
            0
        ];
        if (edge === 0 && maxOffsetFromBorder < 0 || edge !== 0 && maxOffsetFromBorder > 0) {
            terrain.getForTilePoints(neighborTileID, [thirdPoint], true, neighborDEMTile);
        } else {
            points.push(thirdPoint);
        }
        terrain.getForTilePoints(coord, points, true, demTile);
        return Math.max(points[0][2], points[1][2], thirdPoint[2]) / terrain.exaggeration();
    };
    for (let i = 0; i < 4; i++) {
        const a = bucket.borders[i];
        if (a.length === 0) {
            bucket.borderDone[i] = true;
        }
        if (bucket.borderDone[i])
            continue;
        const nid = neighborTileID = neighborCoord[i](coord);
        const nBucket = getLoadedBucket(nid);
        if (!nBucket || !nBucket.enableTerrain)
            continue;
        neighborDEMTile = terrain.findDEMTileFor(nid);
        if (!neighborDEMTile || !neighborDEMTile.dem)
            continue;
        if (!demTile) {
            const dem = terrain.findDEMTileFor(coord);
            if (!(dem && dem.dem))
                return;
            demTile = dem;
        }
        const j = (i < 2 ? 1 : 5) - i;
        const b = nBucket.borders[j];
        let ib = 0;
        for (let ia = 0; ia < a.length; ia++) {
            const parta = bucket.featuresOnBorder[a[ia]];
            const partABorderRange = parta.borders[i];
            let partb;
            while (ib < b.length) {
                partb = nBucket.featuresOnBorder[b[ib]];
                const partBBorderRange = partb.borders[j];
                if (partBBorderRange[1] > partABorderRange[0] + error)
                    break;
                if (!nBucket.borderDone[j])
                    nBucket.encodeCentroid(undefined, partb, false);
                ib++;
            }
            if (partb && ib < b.length) {
                const saveIb = ib;
                let count = 0;
                while (true) {
                    const partBBorderRange = partb.borders[j];
                    if (partBBorderRange[0] > partABorderRange[1] - error)
                        break;
                    count++;
                    if (++ib === b.length)
                        break;
                    partb = nBucket.featuresOnBorder[b[ib]];
                }
                partb = nBucket.featuresOnBorder[b[saveIb]];
                if (parta.intersectsCount() > 1 || partb.intersectsCount() > 1 || count !== 1) {
                    if (count !== 1) {
                        ib = saveIb;
                    }
                    bucket.encodeCentroid(undefined, parta, false);
                    if (!nBucket.borderDone[j])
                        nBucket.encodeCentroid(undefined, partb, false);
                    continue;
                }
                const span = projectCombinedSpanToBorder[i](parta, partb);
                const edge = i % 2 ? ref_properties.EXTENT - 1 : 0;
                centroid.x = flatBase(span[0], Math.min(ref_properties.EXTENT - 1, span[1]), edge, i < 2, span[2]);
                centroid.y = 0;
                bucket.encodeCentroid(centroid, parta, false);
                if (!nBucket.borderDone[j])
                    nBucket.encodeCentroid(centroid, partb, false);
            } else {
                bucket.encodeCentroid(undefined, parta, false);
            }
        }
        bucket.borderDone[i] = bucket.needsCentroidUpdate = true;
        if (!nBucket.borderDone[j]) {
            nBucket.borderDone[j] = nBucket.needsCentroidUpdate = true;
        }
    }
    if (bucket.needsCentroidUpdate || !bucket.centroidVertexBuffer && bucket.centroidVertexArray.length !== 0) {
        bucket.uploadCentroid(context);
    }
}

function drawRaster(painter, sourceCache, layer, tileIDs, variableOffsets, isInitialLoad) {
    if (painter.renderPass !== 'translucent')
        return;
    if (layer.paint.get('raster-opacity') === 0)
        return;
    if (!tileIDs.length)
        return;
    const context = painter.context;
    const gl = context.gl;
    const source = sourceCache.getSource();
    const program = painter.useProgram('raster');
    const colorMode = painter.colorModeForRenderPass();
    const renderingToTexture = painter.terrain && painter.terrain.renderingToTexture;
    const [stencilModes, coords] = source instanceof ImageSource || renderingToTexture ? [
        {},
        tileIDs
    ] : painter.stencilConfigForOverlap(tileIDs);
    const minTileZ = coords[coords.length - 1].overscaledZ;
    const align = !painter.options.moving;
    for (const coord of coords) {
        const depthMode = renderingToTexture ? ref_properties.DepthMode.disabled : painter.depthModeForSublayer(coord.overscaledZ - minTileZ, layer.paint.get('raster-opacity') === 1 ? ref_properties.DepthMode.ReadWrite : ref_properties.DepthMode.ReadOnly, gl.LESS);
        const unwrappedTileID = coord.toUnwrapped();
        const tile = sourceCache.getTile(coord);
        if (renderingToTexture && !(tile && tile.hasData()))
            continue;
        const projMatrix = renderingToTexture ? coord.projMatrix : painter.transform.calculateProjMatrix(unwrappedTileID, align);
        const stencilMode = painter.terrain && renderingToTexture ? painter.terrain.stencilModeForRTTOverlap(coord) : stencilModes[coord.overscaledZ];
        const rasterFadeDuration = isInitialLoad ? 0 : layer.paint.get('raster-fade-duration');
        tile.registerFadeDuration(rasterFadeDuration);
        const parentTile = sourceCache.findLoadedParent(coord, 0);
        const fade = rasterFade(tile, parentTile, sourceCache, painter.transform, rasterFadeDuration);
        if (painter.terrain)
            painter.terrain.prepareDrawTile(coord);
        let parentScaleBy, parentTL;
        const textureFilter = layer.paint.get('raster-resampling') === 'nearest' ? gl.NEAREST : gl.LINEAR;
        context.activeTexture.set(gl.TEXTURE0);
        tile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
        context.activeTexture.set(gl.TEXTURE1);
        if (parentTile) {
            parentTile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
            parentScaleBy = Math.pow(2, parentTile.tileID.overscaledZ - tile.tileID.overscaledZ);
            parentTL = [
                tile.tileID.canonical.x * parentScaleBy % 1,
                tile.tileID.canonical.y * parentScaleBy % 1
            ];
        } else {
            tile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
        }
        const uniformValues = rasterUniformValues(projMatrix, parentTL || [
            0,
            0
        ], parentScaleBy || 1, fade, layer);
        painter.prepareDrawProgram(context, program, unwrappedTileID);
        if (source instanceof ImageSource) {
            program.draw(context, gl.TRIANGLES, depthMode, ref_properties.StencilMode.disabled, colorMode, ref_properties.CullFaceMode.disabled, uniformValues, layer.id, source.boundsBuffer, painter.quadTriangleIndexBuffer, source.boundsSegments);
        } else {
            program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, ref_properties.CullFaceMode.disabled, uniformValues, layer.id, painter.rasterBoundsBuffer, painter.quadTriangleIndexBuffer, painter.rasterBoundsSegments);
        }
    }
}

function drawBackground(painter, sourceCache, layer, coords) {
    const color = layer.paint.get('background-color');
    const opacity = layer.paint.get('background-opacity');
    if (opacity === 0)
        return;
    const context = painter.context;
    const gl = context.gl;
    const transform = painter.transform;
    const tileSize = transform.tileSize;
    const image = layer.paint.get('background-pattern');
    if (painter.isPatternMissing(image))
        return;
    const pass = !image && color.a === 1 && opacity === 1 && painter.opaquePassEnabledForLayer() ? 'opaque' : 'translucent';
    if (painter.renderPass !== pass)
        return;
    const stencilMode = ref_properties.StencilMode.disabled;
    const depthMode = painter.depthModeForSublayer(0, pass === 'opaque' ? ref_properties.DepthMode.ReadWrite : ref_properties.DepthMode.ReadOnly);
    const colorMode = painter.colorModeForRenderPass();
    const program = painter.useProgram(image ? 'backgroundPattern' : 'background');
    const tileIDs = coords ? coords : transform.coveringTiles({ tileSize });
    if (image) {
        context.activeTexture.set(gl.TEXTURE0);
        painter.imageManager.bind(painter.context);
    }
    const crossfade = layer.getCrossfadeParameters();
    for (const tileID of tileIDs) {
        const unwrappedTileID = tileID.toUnwrapped();
        const matrix = coords ? tileID.projMatrix : painter.transform.calculateProjMatrix(unwrappedTileID);
        painter.prepareDrawTile(tileID);
        const uniformValues = image ? backgroundPatternUniformValues(matrix, opacity, painter, image, {
            tileID,
            tileSize
        }, crossfade) : backgroundUniformValues(matrix, opacity, color);
        painter.prepareDrawProgram(context, program, unwrappedTileID);
        program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, ref_properties.CullFaceMode.disabled, uniformValues, layer.id, painter.tileExtentBuffer, painter.quadTriangleIndexBuffer, painter.tileExtentSegments);
    }
}

const topColor = new ref_properties.Color(1, 0, 0, 1);
const btmColor = new ref_properties.Color(0, 1, 0, 1);
const leftColor = new ref_properties.Color(0, 0, 1, 1);
const rightColor = new ref_properties.Color(1, 0, 1, 1);
const centerColor = new ref_properties.Color(0, 1, 1, 1);
function drawDebugPadding(painter) {
    const padding = painter.transform.padding;
    const lineWidth = 3;
    drawHorizontalLine(painter, painter.transform.height - (padding.top || 0), lineWidth, topColor);
    drawHorizontalLine(painter, padding.bottom || 0, lineWidth, btmColor);
    drawVerticalLine(painter, padding.left || 0, lineWidth, leftColor);
    drawVerticalLine(painter, painter.transform.width - (padding.right || 0), lineWidth, rightColor);
    const center = painter.transform.centerPoint;
    drawCrosshair(painter, center.x, painter.transform.height - center.y, centerColor);
}
function drawCrosshair(painter, x, y, color) {
    const size = 20;
    const lineWidth = 2;
    drawDebugSSRect(painter, x - lineWidth / 2, y - size / 2, lineWidth, size, color);
    drawDebugSSRect(painter, x - size / 2, y - lineWidth / 2, size, lineWidth, color);
}
function drawHorizontalLine(painter, y, lineWidth, color) {
    drawDebugSSRect(painter, 0, y + lineWidth / 2, painter.transform.width, lineWidth, color);
}
function drawVerticalLine(painter, x, lineWidth, color) {
    drawDebugSSRect(painter, x - lineWidth / 2, 0, lineWidth, painter.transform.height, color);
}
function drawDebugSSRect(painter, x, y, width, height, color) {
    const context = painter.context;
    const gl = context.gl;
    gl.enable(gl.SCISSOR_TEST);
    gl.scissor(x * ref_properties.exported.devicePixelRatio, y * ref_properties.exported.devicePixelRatio, width * ref_properties.exported.devicePixelRatio, height * ref_properties.exported.devicePixelRatio);
    context.clear({ color });
    gl.disable(gl.SCISSOR_TEST);
}
function drawDebug(painter, sourceCache, coords) {
    for (let i = 0; i < coords.length; i++) {
        drawDebugTile(painter, sourceCache, coords[i]);
    }
}
function drawDebugTile(painter, sourceCache, coord) {
    const context = painter.context;
    const gl = context.gl;
    const posMatrix = coord.projMatrix;
    const program = painter.useProgram('debug');
    const tile = sourceCache.getTileByID(coord.key);
    if (painter.terrain)
        painter.terrain.setupElevationDraw(tile, program);
    const depthMode = ref_properties.DepthMode.disabled;
    const stencilMode = ref_properties.StencilMode.disabled;
    const colorMode = painter.colorModeForRenderPass();
    const id = '$debug';
    context.activeTexture.set(gl.TEXTURE0);
    painter.emptyTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
    program.draw(context, gl.LINE_STRIP, depthMode, stencilMode, colorMode, ref_properties.CullFaceMode.disabled, debugUniformValues(posMatrix, ref_properties.Color.red), id, painter.debugBuffer, painter.tileBorderIndexBuffer, painter.debugSegments);
    const tileRawData = tile.latestRawTileData;
    const tileByteLength = tileRawData && tileRawData.byteLength || 0;
    const tileSizeKb = Math.floor(tileByteLength / 1024);
    const tileSize = sourceCache.getTile(coord).tileSize;
    const scaleRatio = 512 / Math.min(tileSize, 512) * (coord.overscaledZ / painter.transform.zoom) * 0.5;
    let tileIdText = coord.canonical.toString();
    if (coord.overscaledZ !== coord.canonical.z) {
        tileIdText += ` => ${ coord.overscaledZ }`;
    }
    const tileLabel = `${ tileIdText } ${ tileSizeKb }kb`;
    drawTextToOverlay(painter, tileLabel);
    program.draw(context, gl.TRIANGLES, depthMode, stencilMode, ref_properties.ColorMode.alphaBlended, ref_properties.CullFaceMode.disabled, debugUniformValues(posMatrix, ref_properties.Color.transparent, scaleRatio), id, painter.debugBuffer, painter.quadTriangleIndexBuffer, painter.debugSegments);
}
function drawTextToOverlay(painter, text) {
    painter.initDebugOverlayCanvas();
    const canvas = painter.debugOverlayCanvas;
    const gl = painter.context.gl;
    const ctx2d = painter.debugOverlayCanvas.getContext('2d');
    ctx2d.clearRect(0, 0, canvas.width, canvas.height);
    ctx2d.shadowColor = 'white';
    ctx2d.shadowBlur = 2;
    ctx2d.lineWidth = 1.5;
    ctx2d.strokeStyle = 'white';
    ctx2d.textBaseline = 'top';
    ctx2d.font = `bold ${ 36 }px Open Sans, sans-serif`;
    ctx2d.fillText(text, 5, 5);
    ctx2d.strokeText(text, 5, 5);
    painter.debugOverlayTexture.update(canvas);
    painter.debugOverlayTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
}

function drawCustom(painter, sourceCache, layer) {
    const context = painter.context;
    const implementation = layer.implementation;
    if (painter.renderPass === 'offscreen') {
        const prerender = implementation.prerender;
        if (prerender) {
            painter.setCustomLayerDefaults();
            context.setColorMode(painter.colorModeForRenderPass());
            prerender.call(implementation, context.gl, painter.transform.customLayerMatrix());
            context.setDirty();
            painter.setBaseState();
        }
    } else if (painter.renderPass === 'translucent') {
        painter.setCustomLayerDefaults();
        context.setColorMode(painter.colorModeForRenderPass());
        context.setStencilMode(ref_properties.StencilMode.disabled);
        const depthMode = implementation.renderingMode === '3d' ? new ref_properties.DepthMode(painter.context.gl.LEQUAL, ref_properties.DepthMode.ReadWrite, painter.depthRangeFor3D) : painter.depthModeForSublayer(0, ref_properties.DepthMode.ReadOnly);
        context.setDepthMode(depthMode);
        implementation.render(context.gl, painter.transform.customLayerMatrix());
        context.setDirty();
        painter.setBaseState();
        context.bindFramebuffer.set(null);
    }
}

const skyboxAttributes = ref_properties.createLayout([{
        name: 'a_pos_3f',
        components: 3,
        type: 'Float32'
    }]);
const {members, size, alignment} = skyboxAttributes;

function addVertex(vertexArray, x, y, z) {
    vertexArray.emplaceBack(x, y, z);
}
class SkyboxGeometry {
    constructor(context) {
        this.vertexArray = new ref_properties.StructArrayLayout3f12();
        this.indices = new ref_properties.StructArrayLayout3ui6();
        addVertex(this.vertexArray, -1, -1, 1);
        addVertex(this.vertexArray, 1, -1, 1);
        addVertex(this.vertexArray, -1, 1, 1);
        addVertex(this.vertexArray, 1, 1, 1);
        addVertex(this.vertexArray, -1, -1, -1);
        addVertex(this.vertexArray, 1, -1, -1);
        addVertex(this.vertexArray, -1, 1, -1);
        addVertex(this.vertexArray, 1, 1, -1);
        this.indices.emplaceBack(5, 1, 3);
        this.indices.emplaceBack(3, 7, 5);
        this.indices.emplaceBack(6, 2, 0);
        this.indices.emplaceBack(0, 4, 6);
        this.indices.emplaceBack(2, 6, 7);
        this.indices.emplaceBack(7, 3, 2);
        this.indices.emplaceBack(5, 4, 0);
        this.indices.emplaceBack(0, 1, 5);
        this.indices.emplaceBack(0, 2, 3);
        this.indices.emplaceBack(3, 1, 0);
        this.indices.emplaceBack(7, 6, 4);
        this.indices.emplaceBack(4, 5, 7);
        this.vertexBuffer = context.createVertexBuffer(this.vertexArray, members);
        this.indexBuffer = context.createIndexBuffer(this.indices);
        this.segment = ref_properties.SegmentVector.simpleSegment(0, 0, 36, 12);
    }
}

function drawSky(painter, sourceCache, layer) {
    const opacity = layer.paint.get('sky-opacity');
    if (opacity === 0) {
        return;
    }
    const context = painter.context;
    const type = layer.paint.get('sky-type');
    const depthMode = new ref_properties.DepthMode(context.gl.LEQUAL, ref_properties.DepthMode.ReadOnly, [
        0,
        1
    ]);
    const temporalOffset = painter.frameCounter / 1000 % 1;
    if (type === 'atmosphere') {
        if (painter.renderPass === 'offscreen') {
            if (layer.needsSkyboxCapture(painter)) {
                captureSkybox(painter, layer, 32, 32);
                layer.markSkyboxValid(painter);
            }
        } else if (painter.renderPass === 'sky') {
            drawSkyboxFromCapture(painter, layer, depthMode, opacity, temporalOffset);
        }
    } else if (type === 'gradient') {
        if (painter.renderPass === 'sky') {
            drawSkyboxGradient(painter, layer, depthMode, opacity, temporalOffset);
        }
    } else ;
}
function drawSkyboxGradient(painter, layer, depthMode, opacity, temporalOffset) {
    const context = painter.context;
    const gl = context.gl;
    const transform = painter.transform;
    const program = painter.useProgram('skyboxGradient');
    if (!layer.skyboxGeometry) {
        layer.skyboxGeometry = new SkyboxGeometry(context);
    }
    context.activeTexture.set(gl.TEXTURE0);
    let colorRampTexture = layer.colorRampTexture;
    if (!colorRampTexture) {
        colorRampTexture = layer.colorRampTexture = new ref_properties.Texture(context, layer.colorRamp, gl.RGBA);
    }
    colorRampTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
    const uniformValues = skyboxGradientUniformValues(transform.skyboxMatrix, layer.getCenter(painter, false), layer.paint.get('sky-gradient-radius'), opacity, temporalOffset);
    painter.prepareDrawProgram(context, program);
    program.draw(context, gl.TRIANGLES, depthMode, ref_properties.StencilMode.disabled, painter.colorModeForRenderPass(), ref_properties.CullFaceMode.backCW, uniformValues, 'skyboxGradient', layer.skyboxGeometry.vertexBuffer, layer.skyboxGeometry.indexBuffer, layer.skyboxGeometry.segment);
}
function drawSkyboxFromCapture(painter, layer, depthMode, opacity, temporalOffset) {
    const context = painter.context;
    const gl = context.gl;
    const transform = painter.transform;
    const program = painter.useProgram('skybox');
    context.activeTexture.set(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_CUBE_MAP, layer.skyboxTexture);
    const uniformValues = skyboxUniformValues(transform.skyboxMatrix, layer.getCenter(painter, false), 0, opacity, temporalOffset);
    painter.prepareDrawProgram(context, program);
    program.draw(context, gl.TRIANGLES, depthMode, ref_properties.StencilMode.disabled, painter.colorModeForRenderPass(), ref_properties.CullFaceMode.backCW, uniformValues, 'skybox', layer.skyboxGeometry.vertexBuffer, layer.skyboxGeometry.indexBuffer, layer.skyboxGeometry.segment);
}
function drawSkyboxFace(context, layer, program, faceRotate, sunDirection, i) {
    const gl = context.gl;
    const atmosphereColor = layer.paint.get('sky-atmosphere-color');
    const atmosphereHaloColor = layer.paint.get('sky-atmosphere-halo-color');
    const sunIntensity = layer.paint.get('sky-atmosphere-sun-intensity');
    const uniformValues = skyboxCaptureUniformValues(ref_properties.fromMat4([], faceRotate), sunDirection, sunIntensity, atmosphereColor, atmosphereHaloColor);
    const glFace = gl.TEXTURE_CUBE_MAP_POSITIVE_X + i;
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, glFace, layer.skyboxTexture, 0);
    program.draw(context, gl.TRIANGLES, ref_properties.DepthMode.disabled, ref_properties.StencilMode.disabled, ref_properties.ColorMode.unblended, ref_properties.CullFaceMode.frontCW, uniformValues, 'skyboxCapture', layer.skyboxGeometry.vertexBuffer, layer.skyboxGeometry.indexBuffer, layer.skyboxGeometry.segment);
}
function captureSkybox(painter, layer, width, height) {
    const context = painter.context;
    const gl = context.gl;
    let fbo = layer.skyboxFbo;
    if (!fbo) {
        fbo = layer.skyboxFbo = context.createFramebuffer(width, height, false);
        layer.skyboxGeometry = new SkyboxGeometry(context);
        layer.skyboxTexture = context.gl.createTexture();
        gl.bindTexture(gl.TEXTURE_CUBE_MAP, layer.skyboxTexture);
        gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        for (let i = 0; i < 6; ++i) {
            const glFace = gl.TEXTURE_CUBE_MAP_POSITIVE_X + i;
            gl.texImage2D(glFace, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
        }
    }
    context.bindFramebuffer.set(fbo.framebuffer);
    context.viewport.set([
        0,
        0,
        width,
        height
    ]);
    const sunDirection = layer.getCenter(painter, true);
    const program = painter.useProgram('skyboxCapture');
    const faceRotate = new Float64Array(16);
    ref_properties.identity(faceRotate);
    ref_properties.rotateY(faceRotate, faceRotate, -Math.PI * 0.5);
    drawSkyboxFace(context, layer, program, faceRotate, sunDirection, 0);
    ref_properties.identity(faceRotate);
    ref_properties.rotateY(faceRotate, faceRotate, Math.PI * 0.5);
    drawSkyboxFace(context, layer, program, faceRotate, sunDirection, 1);
    ref_properties.identity(faceRotate);
    ref_properties.rotateX(faceRotate, faceRotate, -Math.PI * 0.5);
    drawSkyboxFace(context, layer, program, faceRotate, sunDirection, 2);
    ref_properties.identity(faceRotate);
    ref_properties.rotateX(faceRotate, faceRotate, Math.PI * 0.5);
    drawSkyboxFace(context, layer, program, faceRotate, sunDirection, 3);
    ref_properties.identity(faceRotate);
    drawSkyboxFace(context, layer, program, faceRotate, sunDirection, 4);
    ref_properties.identity(faceRotate);
    ref_properties.rotateY(faceRotate, faceRotate, Math.PI);
    drawSkyboxFace(context, layer, program, faceRotate, sunDirection, 5);
    context.viewport.set([
        0,
        0,
        painter.width,
        painter.height
    ]);
}

const draw = {
    symbol: drawSymbols,
    circle: drawCircles,
    heatmap: drawHeatmap,
    line: drawLine,
    fill: drawFill,
    'fill-extrusion': draw$1,
    hillshade: drawHillshade,
    raster: drawRaster,
    background: drawBackground,
    sky: drawSky,
    debug: drawDebug,
    custom: drawCustom
};
class Painter {
    constructor(gl, transform) {
        this.context = new Context(gl);
        this.transform = transform;
        this._tileTextures = {};
        this.frameCopies = [];
        this.loadTimeStamps = [];
        this.setup();
        this.numSublayers = ref_properties.SourceCache.maxUnderzooming + ref_properties.SourceCache.maxOverzooming + 1;
        this.depthEpsilon = 1 / Math.pow(2, 16);
        this.crossTileSymbolIndex = new CrossTileSymbolIndex();
        this.gpuTimers = {};
        this.frameCounter = 0;
    }
    updateTerrain(style, cameraChanging) {
        const enabled = !!style && !!style.terrain;
        if (!enabled && (!this._terrain || !this._terrain.enabled))
            return;
        if (!this._terrain) {
            this._terrain = new Terrain(this, style);
        }
        const terrain = this._terrain;
        this.transform.elevation = enabled ? terrain : null;
        terrain.update(style, this.transform, cameraChanging);
    }
    _updateFog(style) {
        const fog = style.fog;
        if (!fog || fog.getOpacity(this.transform.pitch) < 1 || fog.properties.get('horizon-blend') < 0.03) {
            this.transform.fogCullDistSq = null;
            return;
        }
        const [start, end] = fog.getFovAdjustedRange(this.transform._fov);
        const fogBoundFraction = 0.78;
        const fogCullDist = start + (end - start) * fogBoundFraction;
        this.transform.fogCullDistSq = fogCullDist * fogCullDist;
    }
    get terrain() {
        return this._terrain && this._terrain.enabled ? this._terrain : null;
    }
    resize(width, height) {
        this.width = width * ref_properties.exported.devicePixelRatio;
        this.height = height * ref_properties.exported.devicePixelRatio;
        this.context.viewport.set([
            0,
            0,
            this.width,
            this.height
        ]);
        if (this.style) {
            for (const layerId of this.style.order) {
                this.style._layers[layerId].resize();
            }
        }
    }
    setup() {
        const context = this.context;
        const tileExtentArray = new ref_properties.StructArrayLayout2i4();
        tileExtentArray.emplaceBack(0, 0);
        tileExtentArray.emplaceBack(ref_properties.EXTENT, 0);
        tileExtentArray.emplaceBack(0, ref_properties.EXTENT);
        tileExtentArray.emplaceBack(ref_properties.EXTENT, ref_properties.EXTENT);
        this.tileExtentBuffer = context.createVertexBuffer(tileExtentArray, ref_properties.posAttributes.members);
        this.tileExtentSegments = ref_properties.SegmentVector.simpleSegment(0, 0, 4, 2);
        const debugArray = new ref_properties.StructArrayLayout2i4();
        debugArray.emplaceBack(0, 0);
        debugArray.emplaceBack(ref_properties.EXTENT, 0);
        debugArray.emplaceBack(0, ref_properties.EXTENT);
        debugArray.emplaceBack(ref_properties.EXTENT, ref_properties.EXTENT);
        this.debugBuffer = context.createVertexBuffer(debugArray, ref_properties.posAttributes.members);
        this.debugSegments = ref_properties.SegmentVector.simpleSegment(0, 0, 4, 5);
        const rasterBoundsArray = new ref_properties.StructArrayLayout4i8();
        rasterBoundsArray.emplaceBack(0, 0, 0, 0);
        rasterBoundsArray.emplaceBack(ref_properties.EXTENT, 0, ref_properties.EXTENT, 0);
        rasterBoundsArray.emplaceBack(0, ref_properties.EXTENT, 0, ref_properties.EXTENT);
        rasterBoundsArray.emplaceBack(ref_properties.EXTENT, ref_properties.EXTENT, ref_properties.EXTENT, ref_properties.EXTENT);
        this.rasterBoundsBuffer = context.createVertexBuffer(rasterBoundsArray, rasterBoundsAttributes.members);
        this.rasterBoundsSegments = ref_properties.SegmentVector.simpleSegment(0, 0, 4, 2);
        const viewportArray = new ref_properties.StructArrayLayout2i4();
        viewportArray.emplaceBack(0, 0);
        viewportArray.emplaceBack(1, 0);
        viewportArray.emplaceBack(0, 1);
        viewportArray.emplaceBack(1, 1);
        this.viewportBuffer = context.createVertexBuffer(viewportArray, ref_properties.posAttributes.members);
        this.viewportSegments = ref_properties.SegmentVector.simpleSegment(0, 0, 4, 2);
        const tileLineStripIndices = new ref_properties.StructArrayLayout1ui2();
        tileLineStripIndices.emplaceBack(0);
        tileLineStripIndices.emplaceBack(1);
        tileLineStripIndices.emplaceBack(3);
        tileLineStripIndices.emplaceBack(2);
        tileLineStripIndices.emplaceBack(0);
        this.tileBorderIndexBuffer = context.createIndexBuffer(tileLineStripIndices);
        const quadTriangleIndices = new ref_properties.StructArrayLayout3ui6();
        quadTriangleIndices.emplaceBack(0, 1, 2);
        quadTriangleIndices.emplaceBack(2, 1, 3);
        this.quadTriangleIndexBuffer = context.createIndexBuffer(quadTriangleIndices);
        this.emptyTexture = new ref_properties.Texture(context, {
            width: 1,
            height: 1,
            data: new Uint8Array([
                0,
                0,
                0,
                0
            ])
        }, context.gl.RGBA);
        this.identityMat = ref_properties.create$1();
        const gl = this.context.gl;
        this.stencilClearMode = new ref_properties.StencilMode({
            func: gl.ALWAYS,
            mask: 0
        }, 0, 255, gl.ZERO, gl.ZERO, gl.ZERO);
        this.loadTimeStamps.push(ref_properties.window.performance.now());
    }
    clearStencil() {
        const context = this.context;
        const gl = context.gl;
        this.nextStencilID = 1;
        this.currentStencilSource = undefined;
        const matrix = ref_properties.create$1();
        ref_properties.ortho(matrix, 0, this.width, this.height, 0, 0, 1);
        ref_properties.scale$1(matrix, matrix, [
            gl.drawingBufferWidth,
            gl.drawingBufferHeight,
            0
        ]);
        this.useProgram('clippingMask').draw(context, gl.TRIANGLES, ref_properties.DepthMode.disabled, this.stencilClearMode, ref_properties.ColorMode.disabled, ref_properties.CullFaceMode.disabled, clippingMaskUniformValues(matrix), '$clipping', this.viewportBuffer, this.quadTriangleIndexBuffer, this.viewportSegments);
    }
    _renderTileClippingMasks(layer, sourceCache, tileIDs) {
        if (!sourceCache || this.currentStencilSource === sourceCache.id || !layer.isTileClipped() || !tileIDs || !tileIDs.length)
            return;
        this.currentStencilSource = sourceCache.id;
        const context = this.context;
        const gl = context.gl;
        if (this.nextStencilID + tileIDs.length > 256) {
            this.clearStencil();
        }
        context.setColorMode(ref_properties.ColorMode.disabled);
        context.setDepthMode(ref_properties.DepthMode.disabled);
        const program = this.useProgram('clippingMask');
        this._tileClippingMaskIDs = {};
        for (const tileID of tileIDs) {
            const id = this._tileClippingMaskIDs[tileID.key] = this.nextStencilID++;
            program.draw(context, gl.TRIANGLES, ref_properties.DepthMode.disabled, new ref_properties.StencilMode({
                func: gl.ALWAYS,
                mask: 0
            }, id, 255, gl.KEEP, gl.KEEP, gl.REPLACE), ref_properties.ColorMode.disabled, ref_properties.CullFaceMode.disabled, clippingMaskUniformValues(tileID.projMatrix), '$clipping', this.tileExtentBuffer, this.quadTriangleIndexBuffer, this.tileExtentSegments);
        }
    }
    stencilModeFor3D() {
        this.currentStencilSource = undefined;
        if (this.nextStencilID + 1 > 256) {
            this.clearStencil();
        }
        const id = this.nextStencilID++;
        const gl = this.context.gl;
        return new ref_properties.StencilMode({
            func: gl.NOTEQUAL,
            mask: 255
        }, id, 255, gl.KEEP, gl.KEEP, gl.REPLACE);
    }
    stencilModeForClipping(tileID) {
        if (this.terrain)
            return this.terrain.stencilModeForRTTOverlap(tileID);
        const gl = this.context.gl;
        return new ref_properties.StencilMode({
            func: gl.EQUAL,
            mask: 255
        }, this._tileClippingMaskIDs[tileID.key], 0, gl.KEEP, gl.KEEP, gl.REPLACE);
    }
    stencilConfigForOverlap(tileIDs) {
        const gl = this.context.gl;
        const coords = tileIDs.sort((a, b) => b.overscaledZ - a.overscaledZ);
        const minTileZ = coords[coords.length - 1].overscaledZ;
        const stencilValues = coords[0].overscaledZ - minTileZ + 1;
        if (stencilValues > 1) {
            this.currentStencilSource = undefined;
            if (this.nextStencilID + stencilValues > 256) {
                this.clearStencil();
            }
            const zToStencilMode = {};
            for (let i = 0; i < stencilValues; i++) {
                zToStencilMode[i + minTileZ] = new ref_properties.StencilMode({
                    func: gl.GEQUAL,
                    mask: 255
                }, i + this.nextStencilID, 255, gl.KEEP, gl.KEEP, gl.REPLACE);
            }
            this.nextStencilID += stencilValues;
            return [
                zToStencilMode,
                coords
            ];
        }
        return [
            { [minTileZ]: ref_properties.StencilMode.disabled },
            coords
        ];
    }
    colorModeForRenderPass() {
        const gl = this.context.gl;
        if (this._showOverdrawInspector) {
            const numOverdrawSteps = 8;
            const a = 1 / numOverdrawSteps;
            return new ref_properties.ColorMode([
                gl.CONSTANT_COLOR,
                gl.ONE
            ], new ref_properties.Color(a, a, a, 0), [
                true,
                true,
                true,
                true
            ]);
        } else if (this.renderPass === 'opaque') {
            return ref_properties.ColorMode.unblended;
        } else {
            return ref_properties.ColorMode.alphaBlended;
        }
    }
    depthModeForSublayer(n, mask, func) {
        if (!this.opaquePassEnabledForLayer())
            return ref_properties.DepthMode.disabled;
        const depth = 1 - ((1 + this.currentLayer) * this.numSublayers + n) * this.depthEpsilon;
        return new ref_properties.DepthMode(func || this.context.gl.LEQUAL, mask, [
            depth,
            depth
        ]);
    }
    opaquePassEnabledForLayer() {
        return this.currentLayer < this.opaquePassCutoff;
    }
    render(style, options) {
        this.style = style;
        this.options = options;
        this.lineAtlas = style.lineAtlas;
        this.imageManager = style.imageManager;
        this.glyphManager = style.glyphManager;
        this.symbolFadeChange = style.placement.symbolFadeChange(ref_properties.exported.now());
        this.imageManager.beginFrame();
        const layerIds = this.style.order;
        const sourceCaches = this.style._sourceCaches;
        for (const id in sourceCaches) {
            const sourceCache = sourceCaches[id];
            if (sourceCache.used) {
                sourceCache.prepare(this.context);
            }
        }
        const coordsAscending = {};
        const coordsDescending = {};
        const coordsDescendingSymbol = {};
        for (const id in sourceCaches) {
            const sourceCache = sourceCaches[id];
            coordsAscending[id] = sourceCache.getVisibleCoordinates();
            coordsDescending[id] = coordsAscending[id].slice().reverse();
            coordsDescendingSymbol[id] = sourceCache.getVisibleCoordinates(true).reverse();
        }
        this.opaquePassCutoff = Infinity;
        for (let i = 0; i < layerIds.length; i++) {
            const layerId = layerIds[i];
            if (this.style._layers[layerId].is3D()) {
                this.opaquePassCutoff = i;
                break;
            }
        }
        if (this.terrain) {
            this.terrain.updateTileBinding(coordsDescendingSymbol);
            this.opaquePassCutoff = 0;
        }
        if (!ref_properties.isMapAuthenticated(this.context.gl))
            return;
        this.renderPass = 'offscreen';
        for (const layerId of layerIds) {
            const layer = this.style._layers[layerId];
            const sourceCache = style._getLayerSourceCache(layer);
            if (!layer.hasOffscreenPass() || layer.isHidden(this.transform.zoom))
                continue;
            const coords = sourceCache ? coordsDescending[sourceCache.id] : undefined;
            if (!(layer.type === 'custom' || layer.isSky()) && !(coords && coords.length))
                continue;
            this.renderLayer(this, sourceCache, layer, coords);
        }
        this.depthRangeFor3D = [
            0,
            1 - (style.order.length + 2) * this.numSublayers * this.depthEpsilon
        ];
        if (this.terrain && (this.style.hasSymbolLayers() || this.style.hasCircleLayers())) {
            this.terrain.drawDepth();
        }
        this.context.bindFramebuffer.set(null);
        this.context.viewport.set([
            0,
            0,
            this.width,
            this.height
        ]);
        let clearColor = ref_properties.Color.transparent;
        if (this.style.fog) {
            clearColor = this.style.fog.properties.get('color');
        }
        this.context.clear({
            color: options.showOverdrawInspector ? ref_properties.Color.black : clearColor,
            depth: 1
        });
        this.clearStencil();
        this._showOverdrawInspector = options.showOverdrawInspector;
        this.renderPass = 'opaque';
        if (!this.terrain) {
            for (this.currentLayer = layerIds.length - 1; this.currentLayer >= 0; this.currentLayer--) {
                const layer = this.style._layers[layerIds[this.currentLayer]];
                const sourceCache = style._getLayerSourceCache(layer);
                if (layer.isSky())
                    continue;
                const coords = sourceCache ? coordsDescending[sourceCache.id] : undefined;
                this._renderTileClippingMasks(layer, sourceCache, coords);
                this.renderLayer(this, sourceCache, layer, coords);
            }
        }
        this.renderPass = 'sky';
        if (this.transform.isHorizonVisible()) {
            for (this.currentLayer = 0; this.currentLayer < layerIds.length; this.currentLayer++) {
                const layer = this.style._layers[layerIds[this.currentLayer]];
                const sourceCache = style._getLayerSourceCache(layer);
                if (!layer.isSky())
                    continue;
                const coords = sourceCache ? coordsDescending[sourceCache.id] : undefined;
                this.renderLayer(this, sourceCache, layer, coords);
            }
        }
        this.renderPass = 'translucent';
        this.currentLayer = 0;
        while (this.currentLayer < layerIds.length) {
            const layer = this.style._layers[layerIds[this.currentLayer]];
            const sourceCache = style._getLayerSourceCache(layer);
            if (layer.isSky()) {
                ++this.currentLayer;
                continue;
            }
            if (this.terrain && this.style.isLayerDraped(layer)) {
                if (layer.isHidden(this.transform.zoom)) {
                    ++this.currentLayer;
                    continue;
                }
                const terrain = this.terrain;
                this.currentLayer;
                this.currentLayer = terrain.renderBatch(this.currentLayer);
                continue;
            }
            const coords = sourceCache ? (layer.type === 'symbol' ? coordsDescendingSymbol : coordsDescending)[sourceCache.id] : undefined;
            this._renderTileClippingMasks(layer, sourceCache, sourceCache ? coordsAscending[sourceCache.id] : undefined);
            this.renderLayer(this, sourceCache, layer, coords);
            ++this.currentLayer;
        }
        if (this.terrain) {
            this.terrain.postRender();
        }
        if (this.options.showTileBoundaries || this.options.showQueryGeometry) {
            let selectedSource = null;
            const layers = ref_properties.values(this.style._layers);
            layers.forEach(layer => {
                const sourceCache = style._getLayerSourceCache(layer);
                if (sourceCache && !layer.isHidden(this.transform.zoom)) {
                    if (!selectedSource || selectedSource.getSource().maxzoom < sourceCache.getSource().maxzoom) {
                        selectedSource = sourceCache;
                    }
                }
            });
            if (selectedSource) {
                if (this.options.showTileBoundaries) {
                    draw.debug(this, selectedSource, selectedSource.getVisibleCoordinates());
                }
            }
        }
        if (this.options.showPadding) {
            drawDebugPadding(this);
        }
        this.context.setDefault();
        this.frameCounter = (this.frameCounter + 1) % ref_properties.MAX_SAFE_INTEGER;
        if (this.tileLoaded && this.options.speedIndexTiming) {
            this.loadTimeStamps.push(ref_properties.window.performance.now());
            this.saveCanvasCopy();
        }
    }
    renderLayer(painter, sourceCache, layer, coords) {
        if (layer.isHidden(this.transform.zoom))
            return;
        if (layer.type !== 'background' && layer.type !== 'sky' && layer.type !== 'custom' && !(coords && coords.length))
            return;
        this.id = layer.id;
        this.gpuTimingStart(layer);
        draw[layer.type](painter, sourceCache, layer, coords, this.style.placement.variableOffsets, this.options.isInitialLoad);
        this.gpuTimingEnd();
    }
    gpuTimingStart(layer) {
        if (!this.options.gpuTiming)
            return;
        const ext = this.context.extTimerQuery;
        let layerTimer = this.gpuTimers[layer.id];
        if (!layerTimer) {
            layerTimer = this.gpuTimers[layer.id] = {
                calls: 0,
                cpuTime: 0,
                query: ext.createQueryEXT()
            };
        }
        layerTimer.calls++;
        ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, layerTimer.query);
    }
    gpuTimingEnd() {
        if (!this.options.gpuTiming)
            return;
        const ext = this.context.extTimerQuery;
        ext.endQueryEXT(ext.TIME_ELAPSED_EXT);
    }
    collectGpuTimers() {
        const currentLayerTimers = this.gpuTimers;
        this.gpuTimers = {};
        return currentLayerTimers;
    }
    queryGpuTimers(gpuTimers) {
        const layers = {};
        for (const layerId in gpuTimers) {
            const gpuTimer = gpuTimers[layerId];
            const ext = this.context.extTimerQuery;
            const gpuTime = ext.getQueryObjectEXT(gpuTimer.query, ext.QUERY_RESULT_EXT) / (1000 * 1000);
            ext.deleteQueryEXT(gpuTimer.query);
            layers[layerId] = gpuTime;
        }
        return layers;
    }
    translatePosMatrix(matrix, tile, translate, translateAnchor, inViewportPixelUnitsUnits) {
        if (!translate[0] && !translate[1])
            return matrix;
        const angle = inViewportPixelUnitsUnits ? translateAnchor === 'map' ? this.transform.angle : 0 : translateAnchor === 'viewport' ? -this.transform.angle : 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
            ];
        }
        const translation = [
            inViewportPixelUnitsUnits ? translate[0] : pixelsToTileUnits(tile, translate[0], this.transform.zoom),
            inViewportPixelUnitsUnits ? translate[1] : pixelsToTileUnits(tile, translate[1], this.transform.zoom),
            0
        ];
        const translatedMatrix = new Float32Array(16);
        ref_properties.translate(translatedMatrix, matrix, translation);
        return translatedMatrix;
    }
    saveTileTexture(texture) {
        const textures = this._tileTextures[texture.size[0]];
        if (!textures) {
            this._tileTextures[texture.size[0]] = [texture];
        } else {
            textures.push(texture);
        }
    }
    getTileTexture(size) {
        const textures = this._tileTextures[size];
        return textures && textures.length > 0 ? textures.pop() : null;
    }
    isPatternMissing(image) {
        if (!image)
            return false;
        if (!image.from || !image.to)
            return true;
        const imagePosA = this.imageManager.getPattern(image.from.toString());
        const imagePosB = this.imageManager.getPattern(image.to.toString());
        return !imagePosA || !imagePosB;
    }
    currentGlobalDefines() {
        const terrain = this.terrain && !this.terrain.renderingToTexture;
        const rtt = this.terrain && this.terrain.renderingToTexture;
        const fog = this.style && this.style.fog;
        const defines = [];
        if (terrain)
            defines.push('TERRAIN');
        if (fog && !rtt && fog.getOpacity(this.transform.pitch) !== 0) {
            defines.push('FOG');
        }
        if (rtt)
            defines.push('RENDER_TO_TEXTURE');
        if (this._showOverdrawInspector)
            defines.push('OVERDRAW_INSPECTOR');
        return defines;
    }
    useProgram(name, programConfiguration, fixedDefines) {
        this.cache = this.cache || {};
        const defines = fixedDefines || [];
        const globalDefines = this.currentGlobalDefines();
        const allDefines = globalDefines.concat(defines);
        const key = Program.cacheKey(name, allDefines, programConfiguration);
        if (!this.cache[key]) {
            this.cache[key] = new Program(this.context, name, shaders[name], programConfiguration, programUniforms[name], allDefines);
        }
        return this.cache[key];
    }
    setCustomLayerDefaults() {
        this.context.unbindVAO();
        this.context.cullFace.setDefault();
        this.context.frontFace.setDefault();
        this.context.cullFaceSide.setDefault();
        this.context.activeTexture.setDefault();
        this.context.pixelStoreUnpack.setDefault();
        this.context.pixelStoreUnpackPremultiplyAlpha.setDefault();
        this.context.pixelStoreUnpackFlipY.setDefault();
    }
    setBaseState() {
        const gl = this.context.gl;
        this.context.cullFace.set(false);
        this.context.viewport.set([
            0,
            0,
            this.width,
            this.height
        ]);
        this.context.blendEquation.set(gl.FUNC_ADD);
    }
    initDebugOverlayCanvas() {
        if (this.debugOverlayCanvas == null) {
            this.debugOverlayCanvas = ref_properties.window.document.createElement('canvas');
            this.debugOverlayCanvas.width = 512;
            this.debugOverlayCanvas.height = 512;
            const gl = this.context.gl;
            this.debugOverlayTexture = new ref_properties.Texture(this.context, this.debugOverlayCanvas, gl.RGBA);
        }
    }
    destroy() {
        if (this._terrain) {
            this._terrain.destroy();
        }
        this.emptyTexture.destroy();
        if (this.debugOverlayTexture) {
            this.debugOverlayTexture.destroy();
        }
    }
    prepareDrawTile(tileID) {
        if (this.terrain) {
            this.terrain.prepareDrawTile(tileID);
        }
    }
    prepareDrawProgram(context, program, tileID) {
        if (this.terrain && this.terrain.renderingToTexture) {
            return;
        }
        const fog = this.style.fog;
        if (fog) {
            const fogOpacity = fog.getOpacity(this.transform.pitch);
            if (fogOpacity !== 0) {
                program.setFogUniformValues(context, fogUniformValues(this, fog, tileID, fogOpacity));
            }
        }
    }
    setTileLoadedFlag(flag) {
        this.tileLoaded = flag;
    }
    saveCanvasCopy() {
        this.frameCopies.push(this.canvasCopy());
        this.tileLoaded = false;
    }
    canvasCopy() {
        const gl = this.context.gl;
        const texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, 0);
        return texture;
    }
    getCanvasCopiesAndTimestamps() {
        return {
            canvasCopies: this.frameCopies,
            timeStamps: this.loadTimeStamps
        };
    }
    averageElevationNeedsEasing() {
        if (!this.transform._elevation)
            return false;
        const fog = this.style && this.style.fog;
        if (!fog)
            return false;
        const fogOpacity = fog.getOpacity(this.transform.pitch);
        if (fogOpacity === 0)
            return false;
        return true;
    }
}

class EdgeInsets {
    constructor(top = 0, bottom = 0, left = 0, right = 0) {
        if (isNaN(top) || top < 0 || isNaN(bottom) || bottom < 0 || isNaN(left) || left < 0 || isNaN(right) || right < 0) {
            throw new Error('Invalid value for edge-insets, top, bottom, left and right must all be numbers');
        }
        this.top = top;
        this.bottom = bottom;
        this.left = left;
        this.right = right;
    }
    interpolate(start, target, t) {
        if (target.top != null && start.top != null)
            this.top = ref_properties.number(start.top, target.top, t);
        if (target.bottom != null && start.bottom != null)
            this.bottom = ref_properties.number(start.bottom, target.bottom, t);
        if (target.left != null && start.left != null)
            this.left = ref_properties.number(start.left, target.left, t);
        if (target.right != null && start.right != null)
            this.right = ref_properties.number(start.right, target.right, t);
        return this;
    }
    getCenter(width, height) {
        const x = ref_properties.clamp((this.left + width - this.right) / 2, 0, width);
        const y = ref_properties.clamp((this.top + height - this.bottom) / 2, 0, height);
        return new ref_properties.pointGeometry(x, y);
    }
    equals(other) {
        return this.top === other.top && this.bottom === other.bottom && this.left === other.left && this.right === other.right;
    }
    clone() {
        return new EdgeInsets(this.top, this.bottom, this.left, this.right);
    }
    toJSON() {
        return {
            top: this.top,
            bottom: this.bottom,
            left: this.left,
            right: this.right
        };
    }
}

function getColumn(matrix, col) {
    return [
        matrix[col * 4],
        matrix[col * 4 + 1],
        matrix[col * 4 + 2],
        matrix[col * 4 + 3]
    ];
}
function setColumn(matrix, col, values) {
    matrix[col * 4 + 0] = values[0];
    matrix[col * 4 + 1] = values[1];
    matrix[col * 4 + 2] = values[2];
    matrix[col * 4 + 3] = values[3];
}
function updateTransformOrientation(matrix, orientation) {
    const position = getColumn(matrix, 3);
    ref_properties.fromQuat(matrix, orientation);
    setColumn(matrix, 3, position);
}
function updateTransformPosition(matrix, position) {
    setColumn(matrix, 3, [
        position[0],
        position[1],
        position[2],
        1
    ]);
}
function wrapCameraPosition(position) {
    if (!position)
        return;
    const mercatorCoordinate = Array.isArray(position) ? new ref_properties.MercatorCoordinate(position[0], position[1], position[2]) : position;
    mercatorCoordinate.x = ref_properties.wrap(mercatorCoordinate.x, 0, 1);
    return mercatorCoordinate;
}
function orientationFromPitchBearing(pitch, bearing) {
    const orientation = ref_properties.identity$1([]);
    ref_properties.rotateZ$1(orientation, orientation, -bearing);
    ref_properties.rotateX$1(orientation, orientation, -pitch);
    return orientation;
}
function orientationFromFrame(forward, up) {
    const xyForward = [
        forward[0],
        forward[1],
        0
    ];
    const xyUp = [
        up[0],
        up[1],
        0
    ];
    const epsilon = 1e-15;
    if (ref_properties.length(xyForward) >= epsilon) {
        const xyDir = ref_properties.normalize([], xyForward);
        ref_properties.scale(xyUp, xyDir, ref_properties.dot(xyUp, xyDir));
        up[0] = xyUp[0];
        up[1] = xyUp[1];
    }
    const right = ref_properties.cross([], up, forward);
    if (ref_properties.len(right) < epsilon) {
        return null;
    }
    const bearing = Math.atan2(-right[1], right[0]);
    const pitch = Math.atan2(Math.sqrt(forward[0] * forward[0] + forward[1] * forward[1]), -forward[2]);
    return orientationFromPitchBearing(pitch, bearing);
}
class FreeCameraOptions {
    constructor(position, orientation) {
        this.position = position;
        this.orientation = orientation;
    }
    get position() {
        return this._position;
    }
    set position(position) {
        this._position = this._renderWorldCopies ? wrapCameraPosition(position) : position;
    }
    lookAtPoint(location, up) {
        this.orientation = null;
        if (!this.position) {
            return;
        }
        const altitude = this._elevation ? this._elevation.getAtPointOrZero(ref_properties.MercatorCoordinate.fromLngLat(location)) : 0;
        const pos = this.position;
        const target = ref_properties.MercatorCoordinate.fromLngLat(location, altitude);
        const forward = [
            target.x - pos.x,
            target.y - pos.y,
            target.z - pos.z
        ];
        if (!up)
            up = [
                0,
                0,
                1
            ];
        up[2] = Math.abs(up[2]);
        this.orientation = orientationFromFrame(forward, up);
    }
    setPitchBearing(pitch, bearing) {
        this.orientation = orientationFromPitchBearing(ref_properties.degToRad(pitch), ref_properties.degToRad(-bearing));
    }
}
class FreeCamera {
    constructor(position, orientation) {
        this._transform = ref_properties.identity([]);
        this._orientation = ref_properties.identity$1([]);
        if (orientation) {
            this._orientation = orientation;
            updateTransformOrientation(this._transform, this._orientation);
        }
        if (position) {
            updateTransformPosition(this._transform, position);
        }
    }
    get mercatorPosition() {
        const pos = this.position;
        return new ref_properties.MercatorCoordinate(pos[0], pos[1], pos[2]);
    }
    get position() {
        const col = getColumn(this._transform, 3);
        return [
            col[0],
            col[1],
            col[2]
        ];
    }
    set position(value) {
        updateTransformPosition(this._transform, value);
    }
    get orientation() {
        return this._orientation;
    }
    set orientation(value) {
        this._orientation = value;
        updateTransformOrientation(this._transform, this._orientation);
    }
    getPitchBearing() {
        const f = this.forward();
        const r = this.right();
        return {
            bearing: Math.atan2(-r[1], r[0]),
            pitch: Math.atan2(Math.sqrt(f[0] * f[0] + f[1] * f[1]), -f[2])
        };
    }
    setPitchBearing(pitch, bearing) {
        this._orientation = orientationFromPitchBearing(pitch, bearing);
        updateTransformOrientation(this._transform, this._orientation);
    }
    forward() {
        const col = getColumn(this._transform, 2);
        return [
            -col[0],
            -col[1],
            -col[2]
        ];
    }
    up() {
        const col = getColumn(this._transform, 1);
        return [
            -col[0],
            -col[1],
            -col[2]
        ];
    }
    right() {
        const col = getColumn(this._transform, 0);
        return [
            col[0],
            col[1],
            col[2]
        ];
    }
    getCameraToWorld(worldSize, pixelsPerMeter) {
        const cameraToWorld = new Float64Array(16);
        ref_properties.invert(cameraToWorld, this.getWorldToCamera(worldSize, pixelsPerMeter));
        return cameraToWorld;
    }
    getWorldToCameraPosition(worldSize, pixelsPerMeter, uniformScale) {
        const invPosition = this.position;
        ref_properties.scale(invPosition, invPosition, -worldSize);
        const matrix = new Float64Array(16);
        ref_properties.fromScaling(matrix, [
            uniformScale,
            uniformScale,
            uniformScale
        ]);
        ref_properties.translate(matrix, matrix, invPosition);
        matrix[10] *= pixelsPerMeter;
        return matrix;
    }
    getWorldToCamera(worldSize, pixelsPerMeter) {
        const matrix = new Float64Array(16);
        const invOrientation = new Float64Array(4);
        const invPosition = this.position;
        ref_properties.conjugate(invOrientation, this._orientation);
        ref_properties.scale(invPosition, invPosition, -worldSize);
        ref_properties.fromQuat(matrix, invOrientation);
        ref_properties.translate(matrix, matrix, invPosition);
        matrix[1] *= -1;
        matrix[5] *= -1;
        matrix[9] *= -1;
        matrix[13] *= -1;
        matrix[8] *= pixelsPerMeter;
        matrix[9] *= pixelsPerMeter;
        matrix[10] *= pixelsPerMeter;
        matrix[11] *= pixelsPerMeter;
        return matrix;
    }
    getCameraToClipPerspective(fovy, aspectRatio, nearZ, farZ) {
        const matrix = new Float64Array(16);
        ref_properties.perspective(matrix, fovy, aspectRatio, nearZ, farZ);
        return matrix;
    }
    getDistanceToElevation(elevationMeters) {
        const z0 = elevationMeters === 0 ? 0 : ref_properties.mercatorZfromAltitude(elevationMeters, this.position[1]);
        const f = this.forward();
        return (z0 - this.position[2]) / f[2];
    }
    clone() {
        return new FreeCamera([...this.position], [...this.orientation]);
    }
}

const NUM_WORLD_COPIES = 3;
const DEFAULT_MIN_ZOOM = 0;
class Transform {
    constructor(minZoom, maxZoom, minPitch, maxPitch, renderWorldCopies) {
        this.tileSize = 512;
        this.maxValidLatitude = 90;
        this._renderWorldCopies = renderWorldCopies === undefined ? true : renderWorldCopies;
        this._minZoom = minZoom || DEFAULT_MIN_ZOOM;
        this._maxZoom = maxZoom || 22;
        this._minPitch = minPitch === undefined || minPitch === null ? 0 : minPitch;
        this._maxPitch = maxPitch === undefined || maxPitch === null ? 60 : maxPitch;
        this.setMaxBounds();
        this.width = 0;
        this.height = 0;
        this._center = new ref_properties.LngLat(0, 0);
        this.zoom = 0;
        this.angle = 0;
        this._fov = 0.6435011087932844;
        this._pitch = 0;
        this._unmodified = true;
        this._edgeInsets = new EdgeInsets();
        this._projMatrixCache = {};
        this._alignedProjMatrixCache = {};
        this._fogTileMatrixCache = {};
        this._camera = new FreeCamera();
        this._centerAltitude = 0;
        this._averageElevation = 0;
        this.cameraElevationReference = 'ground';
        this._horizonShift = 0.1;
    }
    clone() {
        const clone = new Transform(this._minZoom, this._maxZoom, this._minPitch, this.maxPitch, this._renderWorldCopies);
        clone._elevation = this._elevation;
        clone._centerAltitude = this._centerAltitude;
        clone.tileSize = this.tileSize;
        clone.latRange = this.latRange;
        clone.width = this.width;
        clone.height = this.height;
        clone.cameraElevationReference = this.cameraElevationReference;
        clone._center = this._center;
        clone._setZoom(this.zoom);
        clone._cameraZoom = this._cameraZoom;
        clone.angle = this.angle;
        clone._fov = this._fov;
        clone._pitch = this._pitch;
        clone._averageElevation = this._averageElevation;
        clone._unmodified = this._unmodified;
        clone._edgeInsets = this._edgeInsets.clone();
        clone._camera = this._camera.clone();
        clone._calcMatrices();
        clone.freezeTileCoverage = this.freezeTileCoverage;
        return clone;
    }
    get elevation() {
        return this._elevation;
    }
    set elevation(elevation) {
        if (this._elevation === elevation)
            return;
        this._elevation = elevation;
        if (!elevation) {
            this._cameraZoom = null;
            this._centerAltitude = 0;
        } else {
            if (this._updateCenterElevation())
                this._updateCameraOnTerrain();
        }
        this._calcMatrices();
    }
    updateElevation(constrainCameraOverTerrain) {
        if (this._terrainEnabled() && this._cameraZoom == null) {
            if (this._updateCenterElevation())
                this._updateCameraOnTerrain();
        }
        if (constrainCameraOverTerrain) {
            this._constrainCameraAltitude();
        }
        this._calcMatrices();
    }
    get minZoom() {
        return this._minZoom;
    }
    set minZoom(zoom) {
        if (this._minZoom === zoom)
            return;
        this._minZoom = zoom;
        this.zoom = Math.max(this.zoom, zoom);
    }
    get maxZoom() {
        return this._maxZoom;
    }
    set maxZoom(zoom) {
        if (this._maxZoom === zoom)
            return;
        this._maxZoom = zoom;
        this.zoom = Math.min(this.zoom, zoom);
    }
    get minPitch() {
        return this._minPitch;
    }
    set minPitch(pitch) {
        if (this._minPitch === pitch)
            return;
        this._minPitch = pitch;
        this.pitch = Math.max(this.pitch, pitch);
    }
    get maxPitch() {
        return this._maxPitch;
    }
    set maxPitch(pitch) {
        if (this._maxPitch === pitch)
            return;
        this._maxPitch = pitch;
        this.pitch = Math.min(this.pitch, pitch);
    }
    get renderWorldCopies() {
        return this._renderWorldCopies;
    }
    set renderWorldCopies(renderWorldCopies) {
        if (renderWorldCopies === undefined) {
            renderWorldCopies = true;
        } else if (renderWorldCopies === null) {
            renderWorldCopies = false;
        }
        this._renderWorldCopies = renderWorldCopies;
    }
    get worldSize() {
        return this.tileSize * this.scale;
    }
    get cameraWorldSize() {
        const distance = Math.max(this._camera.getDistanceToElevation(this._averageElevation), Number.EPSILON);
        return this._worldSizeFromZoom(this._zoomFromMercatorZ(distance));
    }
    get pixelsPerMeter() {
        return ref_properties.mercatorZfromAltitude(1, this.center.lat) * this.worldSize;
    }
    get cameraPixelsPerMeter() {
        return ref_properties.mercatorZfromAltitude(1, this.center.lat) * this.cameraWorldSize;
    }
    get centerOffset() {
        return this.centerPoint._sub(this.size._div(2));
    }
    get size() {
        return new ref_properties.pointGeometry(this.width, this.height);
    }
    get bearing() {
        return -this.angle / Math.PI * 180;
    }
    set bearing(bearing) {
        const b = -ref_properties.wrap(bearing, -180, 180) * Math.PI / 180;
        if (this.angle === b)
            return;
        this._unmodified = false;
        this.angle = b;
        this._calcMatrices();
        this.rotationMatrix = create$1();
        rotate(this.rotationMatrix, this.rotationMatrix, this.angle);
    }
    get pitch() {
        return this._pitch / Math.PI * 180;
    }
    set pitch(pitch) {
        const p = ref_properties.clamp(pitch, this.minPitch, this.maxPitch) / 180 * Math.PI;
        if (this._pitch === p)
            return;
        this._unmodified = false;
        this._pitch = p;
        this._calcMatrices();
    }
    get fov() {
        return this._fov / Math.PI * 180;
    }
    set fov(fov) {
        fov = Math.max(0.01, Math.min(60, fov));
        if (this._fov === fov)
            return;
        this._unmodified = false;
        this._fov = fov / 180 * Math.PI;
        this._calcMatrices();
    }
    get averageElevation() {
        return this._averageElevation;
    }
    set averageElevation(averageElevation) {
        this._averageElevation = averageElevation;
        this._calcFogMatrices();
    }
    get zoom() {
        return this._zoom;
    }
    set zoom(zoom) {
        const z = Math.min(Math.max(zoom, this.minZoom), this.maxZoom);
        if (this._zoom === z)
            return;
        this._unmodified = false;
        this._setZoom(z);
        if (this._terrainEnabled()) {
            this._updateCameraOnTerrain();
        }
        this._constrain();
        this._calcMatrices();
    }
    _setZoom(z) {
        this._zoom = z;
        this.scale = this.zoomScale(z);
        this.tileZoom = Math.floor(z);
        this.zoomFraction = z - this.tileZoom;
    }
    _updateCenterElevation() {
        if (!this._elevation)
            return false;
        const elevationAtCenter = this._elevation.getAtPointOrZero(ref_properties.MercatorCoordinate.fromLngLat(this.center), -1);
        if (elevationAtCenter === -1) {
            this._cameraZoom = null;
            return false;
        }
        this._centerAltitude = elevationAtCenter;
        return true;
    }
    _updateCameraOnTerrain() {
        const height = this.cameraToCenterDistance / this.worldSize;
        const terrainElevation = ref_properties.mercatorZfromAltitude(this._centerAltitude, this.center.lat);
        this._cameraZoom = this._zoomFromMercatorZ(terrainElevation + height);
    }
    sampleAverageElevation() {
        if (!this._elevation)
            return 0;
        const elevation = this._elevation;
        const elevationSamplePoints = [
            [
                0.5,
                0.2
            ],
            [
                0.3,
                0.5
            ],
            [
                0.5,
                0.5
            ],
            [
                0.7,
                0.5
            ],
            [
                0.5,
                0.8
            ]
        ];
        const horizon = this.horizonLineFromTop();
        let elevationSum = 0;
        let weightSum = 0;
        for (let i = 0; i < elevationSamplePoints.length; i++) {
            const pt = new ref_properties.pointGeometry(elevationSamplePoints[i][0] * this.width, horizon + elevationSamplePoints[i][1] * (this.height - horizon));
            const hit = elevation.pointCoordinate(pt);
            if (!hit)
                continue;
            const distanceToHit = Math.hypot(hit[0] - this._camera.position[0], hit[1] - this._camera.position[1]);
            const weight = 1 / distanceToHit;
            elevationSum += hit[3] * weight;
            weightSum += weight;
        }
        if (weightSum === 0)
            return NaN;
        return elevationSum / weightSum;
    }
    get center() {
        return this._center;
    }
    set center(center) {
        if (center.lat === this._center.lat && center.lng === this._center.lng)
            return;
        this._unmodified = false;
        this._center = center;
        if (this._terrainEnabled()) {
            if (this.cameraElevationReference === 'ground') {
                if (this._updateCenterElevation())
                    this._updateCameraOnTerrain();
                else
                    this._cameraZoom = null;
            } else {
                this._updateZoomFromElevation();
            }
        }
        this._constrain();
        this._calcMatrices();
    }
    _updateZoomFromElevation() {
        if (this._cameraZoom == null || !this._elevation)
            return;
        const cameraZoom = this._cameraZoom;
        const elevationAtCenter = this._elevation.getAtPointOrZero(ref_properties.MercatorCoordinate.fromLngLat(this.center));
        const mercatorElevation = ref_properties.mercatorZfromAltitude(elevationAtCenter, this.center.lat);
        const altitude = this._mercatorZfromZoom(cameraZoom);
        const minHeight = this._mercatorZfromZoom(this._maxZoom);
        const height = Math.max(altitude - mercatorElevation, minHeight);
        this._setZoom(this._zoomFromMercatorZ(height));
    }
    get padding() {
        return this._edgeInsets.toJSON();
    }
    set padding(padding) {
        if (this._edgeInsets.equals(padding))
            return;
        this._unmodified = false;
        this._edgeInsets.interpolate(this._edgeInsets, padding, 1);
        this._calcMatrices();
    }
    computeZoomRelativeTo(position) {
        const centerOnTargetAltitude = this.rayIntersectionCoordinate(this.pointRayIntersection(this.centerPoint, position.toAltitude()));
        let targetPosition;
        if (position.z < this._camera.position[2]) {
            targetPosition = [
                centerOnTargetAltitude.x,
                centerOnTargetAltitude.y,
                centerOnTargetAltitude.z
            ];
        } else {
            targetPosition = [
                position.x,
                position.y,
                position.z
            ];
        }
        const distToTarget = ref_properties.length(ref_properties.sub([], this._camera.position, targetPosition));
        return ref_properties.clamp(this._zoomFromMercatorZ(distToTarget), this._minZoom, this._maxZoom);
    }
    setFreeCameraOptions(options) {
        if (!this.height)
            return;
        if (!options.position && !options.orientation)
            return;
        this._updateCameraState();
        let changed = false;
        if (options.orientation && !ref_properties.exactEquals(options.orientation, this._camera.orientation)) {
            changed = this._setCameraOrientation(options.orientation);
        }
        if (options.position) {
            const newPosition = [
                options.position.x,
                options.position.y,
                options.position.z
            ];
            if (!ref_properties.exactEquals$1(newPosition, this._camera.position)) {
                this._setCameraPosition(newPosition);
                changed = true;
            }
        }
        if (changed) {
            this._updateStateFromCamera();
            this.recenterOnTerrain();
        }
    }
    getFreeCameraOptions() {
        this._updateCameraState();
        const pos = this._camera.position;
        const options = new FreeCameraOptions();
        options.position = new ref_properties.MercatorCoordinate(pos[0], pos[1], pos[2]);
        options.orientation = this._camera.orientation;
        options._elevation = this.elevation;
        options._renderWorldCopies = this._renderWorldCopies;
        return options;
    }
    _setCameraOrientation(orientation) {
        if (!ref_properties.length$1(orientation))
            return false;
        ref_properties.normalize$1(orientation, orientation);
        const forward = ref_properties.transformQuat([], [
            0,
            0,
            -1
        ], orientation);
        const up = ref_properties.transformQuat([], [
            0,
            -1,
            0
        ], orientation);
        if (up[2] < 0)
            return false;
        const updatedOrientation = orientationFromFrame(forward, up);
        if (!updatedOrientation)
            return false;
        this._camera.orientation = updatedOrientation;
        return true;
    }
    _setCameraPosition(position) {
        const minWorldSize = this.zoomScale(this.minZoom) * this.tileSize;
        const maxWorldSize = this.zoomScale(this.maxZoom) * this.tileSize;
        const distToCenter = this.cameraToCenterDistance;
        position[2] = ref_properties.clamp(position[2], distToCenter / maxWorldSize, distToCenter / minWorldSize);
        this._camera.position = position;
    }
    get centerPoint() {
        return this._edgeInsets.getCenter(this.width, this.height);
    }
    get fovAboveCenter() {
        return this._fov * (0.5 + this.centerOffset.y / this.height);
    }
    isPaddingEqual(padding) {
        return this._edgeInsets.equals(padding);
    }
    interpolatePadding(start, target, t) {
        this._unmodified = false;
        this._edgeInsets.interpolate(start, target, t);
        this._constrain();
        this._calcMatrices();
    }
    coveringZoomLevel(options) {
        const z = (options.roundZoom ? Math.round : Math.floor)(this.zoom + this.scaleZoom(this.tileSize / options.tileSize));
        return Math.max(0, z);
    }
    getVisibleUnwrappedCoordinates(tileID) {
        const result = [new ref_properties.UnwrappedTileID(0, tileID)];
        if (this._renderWorldCopies) {
            const utl = this.pointCoordinate(new ref_properties.pointGeometry(0, 0));
            const utr = this.pointCoordinate(new ref_properties.pointGeometry(this.width, 0));
            const ubl = this.pointCoordinate(new ref_properties.pointGeometry(this.width, this.height));
            const ubr = this.pointCoordinate(new ref_properties.pointGeometry(0, this.height));
            const w0 = Math.floor(Math.min(utl.x, utr.x, ubl.x, ubr.x));
            const w1 = Math.floor(Math.max(utl.x, utr.x, ubl.x, ubr.x));
            const extraWorldCopy = 1;
            for (let w = w0 - extraWorldCopy; w <= w1 + extraWorldCopy; w++) {
                if (w === 0)
                    continue;
                result.push(new ref_properties.UnwrappedTileID(w, tileID));
            }
        }
        return result;
    }
    coveringTiles(options) {
        let z = this.coveringZoomLevel(options);
        const actualZ = z;
        const useElevationData = !!options.useElevationData;
        if (options.minzoom !== undefined && z < options.minzoom)
            return [];
        if (options.maxzoom !== undefined && z > options.maxzoom)
            z = options.maxzoom;
        const centerCoord = ref_properties.MercatorCoordinate.fromLngLat(this.center);
        const numTiles = 1 << z;
        const centerPoint = [
            numTiles * centerCoord.x,
            numTiles * centerCoord.y,
            0
        ];
        const cameraFrustum = Frustum.fromInvProjectionMatrix(this.invProjMatrix, this.worldSize, z);
        const cameraCoord = this.pointCoordinate(this.getCameraPoint());
        const meterToTile = numTiles * ref_properties.mercatorZfromAltitude(1, this.center.lat);
        const cameraAltitude = this._camera.position[2] / ref_properties.mercatorZfromAltitude(1, this.center.lat);
        const cameraPoint = [
            numTiles * cameraCoord.x,
            numTiles * cameraCoord.y,
            cameraAltitude
        ];
        const zoomSplitDistance = this.cameraToCenterDistance / options.tileSize * (options.roundZoom ? 1 : 0.502);
        const minZoom = this.pitch <= 60 && this._edgeInsets.top <= this._edgeInsets.bottom && !this._elevation ? z : 0;
        const maxRange = options.isTerrainDEM && this._elevation ? this._elevation.exaggeration() * 10000 : this._centerAltitude;
        const minRange = options.isTerrainDEM ? -maxRange : this._elevation ? this._elevation.getMinElevationBelowMSL() : 0;
        const newRootTile = wrap => {
            const max = maxRange;
            const min = -maxRange;
            return {
                aabb: new Aabb([
                    wrap * numTiles,
                    0,
                    min
                ], [
                    (wrap + 1) * numTiles,
                    numTiles,
                    max
                ]),
                zoom: 0,
                x: 0,
                y: 0,
                wrap,
                fullyVisible: false
            };
        };
        const stack = [];
        const result = [];
        const maxZoom = z;
        const overscaledZ = options.reparseOverscaled ? actualZ : z;
        const getAABBFromElevation = it => {
            if (!this._elevation || !it.tileID)
                return;
            const minmax = this._elevation.getMinMaxForTile(it.tileID);
            const aabb = it.aabb;
            if (minmax) {
                aabb.min[2] = minmax.min;
                aabb.max[2] = minmax.max;
                aabb.center[2] = (aabb.min[2] + aabb.max[2]) / 2;
            } else {
                it.shouldSplit = shouldSplit(it);
                if (!it.shouldSplit) {
                    aabb.min[2] = aabb.max[2] = aabb.center[2] = this._centerAltitude;
                }
            }
        };
        const square = a => a * a;
        const cameraHeightSqr = square((cameraAltitude - this._centerAltitude) * meterToTile);
        const distToSplitScale = (dzSqr, dSqr) => {
            const acuteAngleThresholdSin = 0.707;
            const stretchTile = 1.1;
            if (dSqr * square(acuteAngleThresholdSin) < dzSqr)
                return 1;
            const r = Math.sqrt(dSqr / dzSqr);
            const k = r - 1 / acuteAngleThresholdSin;
            return r / (1 / acuteAngleThresholdSin + (Math.pow(stretchTile, k + 1) - 1) / (stretchTile - 1) - 1);
        };
        const shouldSplit = it => {
            if (it.zoom < minZoom) {
                return true;
            } else if (it.zoom === maxZoom) {
                return false;
            }
            if (it.shouldSplit != null) {
                return it.shouldSplit;
            }
            const dx = it.aabb.distanceX(cameraPoint);
            const dy = it.aabb.distanceY(cameraPoint);
            let dzSqr = cameraHeightSqr;
            if (useElevationData) {
                dzSqr = square(it.aabb.distanceZ(cameraPoint) * meterToTile);
            }
            const distanceSqr = dx * dx + dy * dy + dzSqr;
            const distToSplit = (1 << maxZoom - it.zoom) * zoomSplitDistance;
            const distToSplitSqr = square(distToSplit * distToSplitScale(Math.max(dzSqr, cameraHeightSqr), distanceSqr));
            return distanceSqr < distToSplitSqr;
        };
        if (this._renderWorldCopies) {
            for (let i = 1; i <= NUM_WORLD_COPIES; i++) {
                stack.push(newRootTile(-i));
                stack.push(newRootTile(i));
            }
        }
        stack.push(newRootTile(0));
        while (stack.length > 0) {
            const it = stack.pop();
            const x = it.x;
            const y = it.y;
            let fullyVisible = it.fullyVisible;
            if (!fullyVisible) {
                const intersectResult = it.aabb.intersects(cameraFrustum);
                if (intersectResult === 0)
                    continue;
                fullyVisible = intersectResult === 2;
            }
            if (it.zoom === maxZoom || !shouldSplit(it)) {
                const tileZoom = it.zoom === maxZoom ? overscaledZ : it.zoom;
                if (!!options.minzoom && options.minzoom > tileZoom) {
                    continue;
                }
                const dx = centerPoint[0] - (0.5 + x + (it.wrap << it.zoom)) * (1 << z - it.zoom);
                const dy = centerPoint[1] - 0.5 - y;
                const id = it.tileID ? it.tileID : new ref_properties.OverscaledTileID(tileZoom, it.wrap, it.zoom, x, y);
                result.push({
                    tileID: id,
                    distanceSq: dx * dx + dy * dy
                });
                continue;
            }
            for (let i = 0; i < 4; i++) {
                const childX = (x << 1) + i % 2;
                const childY = (y << 1) + (i >> 1);
                const aabb = it.aabb.quadrant(i);
                const child = {
                    aabb,
                    zoom: it.zoom + 1,
                    x: childX,
                    y: childY,
                    wrap: it.wrap,
                    fullyVisible,
                    tileID: undefined,
                    shouldSplit: undefined
                };
                if (useElevationData) {
                    child.tileID = new ref_properties.OverscaledTileID(it.zoom + 1 === maxZoom ? overscaledZ : it.zoom + 1, it.wrap, it.zoom + 1, childX, childY);
                    getAABBFromElevation(child);
                }
                stack.push(child);
            }
        }
        if (this.fogCullDistSq) {
            const fogCullDistSq = this.fogCullDistSq;
            result.splice(0, result.length, ...result.filter(entry => {
                const min = [
                    0,
                    0,
                    0,
                    1
                ];
                const max = [
                    ref_properties.EXTENT,
                    ref_properties.EXTENT,
                    0,
                    1
                ];
                const fogTileMatrix = this.calculateFogTileMatrix(entry.tileID.toUnwrapped());
                ref_properties.transformMat4(min, min, fogTileMatrix);
                ref_properties.transformMat4(max, max, fogTileMatrix);
                const sqDist = ref_properties.getAABBPointSquareDist(min, max);
                if (sqDist === 0) {
                    return true;
                }
                let overHorizonLine = false;
                const horizonLineFromTop = this.horizonLineFromTop();
                if (sqDist > fogCullDistSq && horizonLineFromTop !== 0) {
                    const projMatrix = this.calculateProjMatrix(entry.tileID.toUnwrapped());
                    let minmax;
                    if (useElevationData && this._elevation) {
                        minmax = this._elevation.getMinMaxForTile(entry.tileID);
                    }
                    if (!minmax) {
                        minmax = {
                            min: minRange,
                            max: maxRange
                        };
                    }
                    const cornerFar = ref_properties.furthestTileCorner(this.bearing);
                    const farX = cornerFar[0] * ref_properties.EXTENT;
                    const farY = cornerFar[1] * ref_properties.EXTENT;
                    const worldFar = [
                        farX,
                        farY,
                        minmax.max
                    ];
                    ref_properties.transformMat4$1(worldFar, worldFar, projMatrix);
                    const screenCoordY = (1 - worldFar[1]) * this.height * 0.5;
                    overHorizonLine = screenCoordY < horizonLineFromTop;
                }
                return sqDist < fogCullDistSq || overHorizonLine;
            }));
        }
        const cover = result.sort((a, b) => a.distanceSq - b.distanceSq).map(a => a.tileID);
        return cover;
    }
    resize(width, height) {
        this.width = width;
        this.height = height;
        this.pixelsToGLUnits = [
            2 / width,
            -2 / height
        ];
        this._constrain();
        this._calcMatrices();
    }
    get unmodified() {
        return this._unmodified;
    }
    zoomScale(zoom) {
        return Math.pow(2, zoom);
    }
    scaleZoom(scale) {
        return Math.log(scale) / Math.LN2;
    }
    project(lnglat) {
        const lat = ref_properties.clamp(lnglat.lat, -this.maxValidLatitude, this.maxValidLatitude);
        return new ref_properties.pointGeometry(ref_properties.mercatorXfromLng(lnglat.lng) * this.worldSize, ref_properties.mercatorYfromLat(lat) * this.worldSize);
    }
    unproject(point) {
        return new ref_properties.MercatorCoordinate(point.x / this.worldSize, point.y / this.worldSize).toLngLat();
    }
    get point() {
        return this.project(this.center);
    }
    setLocationAtPoint(lnglat, point) {
        const a = this.pointCoordinate(point);
        const b = this.pointCoordinate(this.centerPoint);
        const loc = this.locationCoordinate(lnglat);
        const newCenter = new ref_properties.MercatorCoordinate(loc.x - (a.x - b.x), loc.y - (a.y - b.y));
        this.center = this.coordinateLocation(newCenter);
        if (this._renderWorldCopies) {
            this.center = this.center.wrap();
        }
    }
    setLocation(location) {
        this.center = this.coordinateLocation(location);
        if (this._renderWorldCopies) {
            this.center = this.center.wrap();
        }
    }
    locationPoint(lnglat) {
        return this._coordinatePoint(this.locationCoordinate(lnglat), false);
    }
    locationPoint3D(lnglat) {
        return this._coordinatePoint(this.locationCoordinate(lnglat), true);
    }
    pointLocation(p) {
        return this.coordinateLocation(this.pointCoordinate(p));
    }
    pointLocation3D(p) {
        return this.coordinateLocation(this.pointCoordinate3D(p));
    }
    locationCoordinate(lnglat) {
        return ref_properties.MercatorCoordinate.fromLngLat(lnglat);
    }
    coordinateLocation(coord) {
        return coord.toLngLat();
    }
    pointRayIntersection(p, z) {
        const targetZ = z !== undefined && z !== null ? z : this._centerAltitude;
        const p0 = [
            p.x,
            p.y,
            0,
            1
        ];
        const p1 = [
            p.x,
            p.y,
            1,
            1
        ];
        ref_properties.transformMat4(p0, p0, this.pixelMatrixInverse);
        ref_properties.transformMat4(p1, p1, this.pixelMatrixInverse);
        const w0 = p0[3];
        const w1 = p1[3];
        ref_properties.scale$2(p0, p0, 1 / w0);
        ref_properties.scale$2(p1, p1, 1 / w1);
        const z0 = p0[2];
        const z1 = p1[2];
        const t = z0 === z1 ? 0 : (targetZ - z0) / (z1 - z0);
        return {
            p0,
            p1,
            t
        };
    }
    screenPointToMercatorRay(p) {
        const p0 = [
            p.x,
            p.y,
            0,
            1
        ];
        const p1 = [
            p.x,
            p.y,
            1,
            1
        ];
        ref_properties.transformMat4(p0, p0, this.pixelMatrixInverse);
        ref_properties.transformMat4(p1, p1, this.pixelMatrixInverse);
        ref_properties.scale$2(p0, p0, 1 / p0[3]);
        ref_properties.scale$2(p1, p1, 1 / p1[3]);
        p0[2] = ref_properties.mercatorZfromAltitude(p0[2], this._center.lat) * this.worldSize;
        p1[2] = ref_properties.mercatorZfromAltitude(p1[2], this._center.lat) * this.worldSize;
        ref_properties.scale$2(p0, p0, 1 / this.worldSize);
        ref_properties.scale$2(p1, p1, 1 / this.worldSize);
        return new Ray([
            p0[0],
            p0[1],
            p0[2]
        ], ref_properties.normalize([], ref_properties.sub([], p1, p0)));
    }
    rayIntersectionCoordinate(rayIntersection) {
        const {p0, p1, t} = rayIntersection;
        const z0 = ref_properties.mercatorZfromAltitude(p0[2], this._center.lat);
        const z1 = ref_properties.mercatorZfromAltitude(p1[2], this._center.lat);
        return new ref_properties.MercatorCoordinate(ref_properties.number(p0[0], p1[0], t) / this.worldSize, ref_properties.number(p0[1], p1[1], t) / this.worldSize, ref_properties.number(z0, z1, t));
    }
    pointCoordinate(p) {
        const horizonOffset = this.horizonLineFromTop(false);
        const clamped = new ref_properties.pointGeometry(p.x, Math.max(horizonOffset, p.y));
        return this.rayIntersectionCoordinate(this.pointRayIntersection(clamped));
    }
    pointCoordinate3D(p) {
        if (!this.elevation)
            return this.pointCoordinate(p);
        const elevation = this.elevation;
        let raycast = this.elevation.pointCoordinate(p);
        if (raycast)
            return new ref_properties.MercatorCoordinate(raycast[0], raycast[1], raycast[2]);
        let start = 0, end = this.horizonLineFromTop();
        if (p.y > end)
            return this.pointCoordinate(p);
        const samples = 10;
        const threshold = 0.02 * end;
        const r = p.clone();
        for (let i = 0; i < samples && end - start > threshold; i++) {
            r.y = ref_properties.number(start, end, 0.66);
            const rCast = elevation.pointCoordinate(r);
            if (rCast) {
                end = r.y;
                raycast = rCast;
            } else {
                start = r.y;
            }
        }
        return raycast ? new ref_properties.MercatorCoordinate(raycast[0], raycast[1], raycast[2]) : this.pointCoordinate(p);
    }
    isPointAboveHorizon(p) {
        if (!this.elevation) {
            const horizon = this.horizonLineFromTop();
            return p.y < horizon;
        } else {
            return !this.elevation.pointCoordinate(p);
        }
    }
    _coordinatePoint(coord, sampleTerrainIn3D) {
        const elevation = sampleTerrainIn3D && this.elevation ? this.elevation.getAtPointOrZero(coord, this._centerAltitude) : this._centerAltitude;
        const p = [
            coord.x * this.worldSize,
            coord.y * this.worldSize,
            elevation + coord.toAltitude(),
            1
        ];
        ref_properties.transformMat4(p, p, this.pixelMatrix);
        return p[3] > 0 ? new ref_properties.pointGeometry(p[0] / p[3], p[1] / p[3]) : new ref_properties.pointGeometry(Number.MAX_VALUE, Number.MAX_VALUE);
    }
    getBounds() {
        if (this._terrainEnabled())
            return this._getBounds3D();
        return new ref_properties.LngLatBounds().extend(this.pointLocation(new ref_properties.pointGeometry(this._edgeInsets.left, this._edgeInsets.top))).extend(this.pointLocation(new ref_properties.pointGeometry(this.width - this._edgeInsets.right, this._edgeInsets.top))).extend(this.pointLocation(new ref_properties.pointGeometry(this.width - this._edgeInsets.right, this.height - this._edgeInsets.bottom))).extend(this.pointLocation(new ref_properties.pointGeometry(this._edgeInsets.left, this.height - this._edgeInsets.bottom)));
    }
    _getBounds3D() {
        const elevation = this.elevation;
        const minmax = elevation.visibleDemTiles.reduce((acc, t) => {
            if (t.dem) {
                const tree = t.dem.tree;
                acc.min = Math.min(acc.min, tree.minimums[0]);
                acc.max = Math.max(acc.max, tree.maximums[0]);
            }
            return acc;
        }, {
            min: Number.MAX_VALUE,
            max: 0
        });
        minmax.min *= elevation.exaggeration();
        minmax.max *= elevation.exaggeration();
        const top = this.horizonLineFromTop();
        return [
            new ref_properties.pointGeometry(0, top),
            new ref_properties.pointGeometry(this.width, top),
            new ref_properties.pointGeometry(this.width, this.height),
            new ref_properties.pointGeometry(0, this.height)
        ].reduce((acc, p) => {
            return acc.extend(this.coordinateLocation(this.rayIntersectionCoordinate(this.pointRayIntersection(p, minmax.min)))).extend(this.coordinateLocation(this.rayIntersectionCoordinate(this.pointRayIntersection(p, minmax.max))));
        }, new ref_properties.LngLatBounds());
    }
    horizonLineFromTop(clampToTop = true) {
        const h = this.height / 2 / Math.tan(this._fov / 2) / Math.tan(Math.max(this._pitch, 0.1)) + this.centerOffset.y;
        const horizonEpsilon = 0.03;
        const offset = this.height / 2 - h * (1 - horizonEpsilon);
        return clampToTop ? Math.max(0, offset) : offset;
    }
    getMaxBounds() {
        if (!this.latRange || this.latRange.length !== 2 || !this.lngRange || this.lngRange.length !== 2)
            return null;
        return new ref_properties.LngLatBounds([
            this.lngRange[0],
            this.latRange[0]
        ], [
            this.lngRange[1],
            this.latRange[1]
        ]);
    }
    setMaxBounds(bounds) {
        if (bounds) {
            this.lngRange = [
                bounds.getWest(),
                bounds.getEast()
            ];
            this.latRange = [
                bounds.getSouth(),
                bounds.getNorth()
            ];
            this._constrain();
        } else {
            this.lngRange = null;
            this.latRange = [
                -this.maxValidLatitude,
                this.maxValidLatitude
            ];
        }
    }
    calculatePosMatrix(unwrappedTileID, worldSize) {
        const canonical = unwrappedTileID.canonical;
        const scale = worldSize / this.zoomScale(canonical.z);
        const unwrappedX = canonical.x + Math.pow(2, canonical.z) * unwrappedTileID.wrap;
        const posMatrix = ref_properties.identity(new Float64Array(16));
        ref_properties.translate(posMatrix, posMatrix, [
            unwrappedX * scale,
            canonical.y * scale,
            0
        ]);
        ref_properties.scale$1(posMatrix, posMatrix, [
            scale / ref_properties.EXTENT,
            scale / ref_properties.EXTENT,
            1
        ]);
        return posMatrix;
    }
    calculateFogTileMatrix(unwrappedTileID) {
        const fogTileMatrixKey = unwrappedTileID.key;
        const cache = this._fogTileMatrixCache;
        if (cache[fogTileMatrixKey]) {
            return cache[fogTileMatrixKey];
        }
        const posMatrix = this.calculatePosMatrix(unwrappedTileID, this.cameraWorldSize);
        ref_properties.multiply(posMatrix, this.worldToFogMatrix, posMatrix);
        cache[fogTileMatrixKey] = new Float32Array(posMatrix);
        return cache[fogTileMatrixKey];
    }
    calculateProjMatrix(unwrappedTileID, aligned = false) {
        const projMatrixKey = unwrappedTileID.key;
        const cache = aligned ? this._alignedProjMatrixCache : this._projMatrixCache;
        if (cache[projMatrixKey]) {
            return cache[projMatrixKey];
        }
        const posMatrix = this.calculatePosMatrix(unwrappedTileID, this.worldSize);
        ref_properties.multiply(posMatrix, aligned ? this.alignedProjMatrix : this.projMatrix, posMatrix);
        cache[projMatrixKey] = new Float32Array(posMatrix);
        return cache[projMatrixKey];
    }
    customLayerMatrix() {
        return this.mercatorMatrix.slice();
    }
    recenterOnTerrain() {
        if (!this._elevation)
            return;
        const elevation = this._elevation;
        this._updateCameraState();
        const start = this._camera.position;
        const dir = this._camera.forward();
        if (start[2] <= 0 || dir[2] >= 0)
            return;
        const metersToMerc = ref_properties.mercatorZfromAltitude(1, this._center.lat);
        start[2] /= metersToMerc;
        dir[2] /= metersToMerc;
        ref_properties.normalize(dir, dir);
        const t = elevation.raycast(start, dir, elevation.exaggeration());
        if (t) {
            const point = ref_properties.scaleAndAdd([], start, dir, t);
            const newCenter = new ref_properties.MercatorCoordinate(point[0], point[1], ref_properties.mercatorZfromAltitude(point[2], ref_properties.latFromMercatorY(point[1])));
            const pos = this._camera.position;
            const camToNew = [
                newCenter.x - pos[0],
                newCenter.y - pos[1],
                newCenter.z - pos[2]
            ];
            const maxAltitude = newCenter.z + ref_properties.length(camToNew);
            this._cameraZoom = this._zoomFromMercatorZ(maxAltitude);
            this._centerAltitude = newCenter.toAltitude();
            this._center = newCenter.toLngLat();
            this._updateZoomFromElevation();
            this._constrain();
            this._calcMatrices();
        }
    }
    _constrainCameraAltitude() {
        if (!this._elevation)
            return;
        const elevation = this._elevation;
        this._updateCameraState();
        const elevationAtCamera = elevation.getAtPointOrZero(this._camera.mercatorPosition);
        const minHeight = this._minimumHeightOverTerrain() * Math.cos(ref_properties.degToRad(this._maxPitch));
        const terrainElevation = ref_properties.mercatorZfromAltitude(elevationAtCamera, this._center.lat);
        const cameraHeight = this._camera.position[2] - terrainElevation;
        if (cameraHeight < minHeight) {
            const center = ref_properties.MercatorCoordinate.fromLngLat(this._center, this._centerAltitude);
            const cameraPos = this._camera.mercatorPosition;
            const cameraToCenter = [
                center.x - cameraPos.x,
                center.y - cameraPos.y,
                center.z - cameraPos.z
            ];
            const prevDistToCamera = ref_properties.length(cameraToCenter);
            cameraToCenter[2] -= minHeight - cameraHeight;
            const newDistToCamera = ref_properties.length(cameraToCenter);
            if (newDistToCamera === 0)
                return;
            ref_properties.scale(cameraToCenter, cameraToCenter, prevDistToCamera / newDistToCamera);
            this._camera.position = [
                center.x - cameraToCenter[0],
                center.y - cameraToCenter[1],
                center.z - cameraToCenter[2]
            ];
            this._camera.orientation = orientationFromFrame(cameraToCenter, this._camera.up());
            this._updateStateFromCamera();
        }
    }
    _constrain() {
        if (!this.center || !this.width || !this.height || this._constraining)
            return;
        this._constraining = true;
        let minY = -90;
        let maxY = 90;
        let minX = -180;
        let maxX = 180;
        let sy, sx, x2, y2;
        const size = this.size, unmodified = this._unmodified;
        if (this.latRange) {
            const latRange = this.latRange;
            minY = ref_properties.mercatorYfromLat(latRange[1]) * this.worldSize;
            maxY = ref_properties.mercatorYfromLat(latRange[0]) * this.worldSize;
            sy = maxY - minY < size.y ? size.y / (maxY - minY) : 0;
        }
        if (this.lngRange) {
            const lngRange = this.lngRange;
            minX = ref_properties.mercatorXfromLng(lngRange[0]) * this.worldSize;
            maxX = ref_properties.mercatorXfromLng(lngRange[1]) * this.worldSize;
            sx = maxX - minX < size.x ? size.x / (maxX - minX) : 0;
        }
        const point = this.point;
        const s = Math.max(sx || 0, sy || 0);
        if (s) {
            this.center = this.unproject(new ref_properties.pointGeometry(sx ? (maxX + minX) / 2 : point.x, sy ? (maxY + minY) / 2 : point.y));
            this.zoom += this.scaleZoom(s);
            this._unmodified = unmodified;
            this._constraining = false;
            return;
        }
        if (this.latRange) {
            const y = point.y, h2 = size.y / 2;
            if (y - h2 < minY)
                y2 = minY + h2;
            if (y + h2 > maxY)
                y2 = maxY - h2;
        }
        if (this.lngRange) {
            const x = point.x, w2 = size.x / 2;
            if (x - w2 < minX)
                x2 = minX + w2;
            if (x + w2 > maxX)
                x2 = maxX - w2;
        }
        if (x2 !== undefined || y2 !== undefined) {
            this.center = this.unproject(new ref_properties.pointGeometry(x2 !== undefined ? x2 : point.x, y2 !== undefined ? y2 : point.y));
        }
        this._constrainCameraAltitude();
        this._unmodified = unmodified;
        this._constraining = false;
    }
    _minZoomForBounds() {
        const minZoomForDim = (dim, range) => {
            return Math.log2(dim / (this.tileSize * Math.abs(range[1] - range[0])));
        };
        let minLatZoom = DEFAULT_MIN_ZOOM;
        if (this.latRange) {
            const latRange = this.latRange;
            minLatZoom = minZoomForDim(this.height, [
                ref_properties.mercatorYfromLat(latRange[0]),
                ref_properties.mercatorYfromLat(latRange[1])
            ]);
        }
        let minLngZoom = DEFAULT_MIN_ZOOM;
        if (this.lngRange) {
            const lngRange = this.lngRange;
            minLngZoom = minZoomForDim(this.width, [
                ref_properties.mercatorXfromLng(lngRange[0]),
                ref_properties.mercatorXfromLng(lngRange[1])
            ]);
        }
        return Math.max(minLatZoom, minLngZoom);
    }
    _maxCameraBoundsDistance() {
        return this._mercatorZfromZoom(this._minZoomForBounds());
    }
    _calcMatrices() {
        if (!this.height)
            return;
        const halfFov = this._fov / 2;
        const offset = this.centerOffset;
        this.cameraToCenterDistance = 0.5 / Math.tan(halfFov) * this.height;
        const pixelsPerMeter = this.pixelsPerMeter;
        this._updateCameraState();
        const groundAngle = Math.PI / 2 + this._pitch;
        const fovAboveCenter = this.fovAboveCenter;
        const minElevationInPixels = this.elevation ? this.elevation.getMinElevationBelowMSL() * pixelsPerMeter : 0;
        const cameraToSeaLevelDistance = (this._camera.position[2] * this.worldSize - minElevationInPixels) / Math.cos(this._pitch);
        const topHalfSurfaceDistance = Math.sin(fovAboveCenter) * cameraToSeaLevelDistance / Math.sin(ref_properties.clamp(Math.PI - groundAngle - fovAboveCenter, 0.01, Math.PI - 0.01));
        const point = this.point;
        const x = point.x, y = point.y;
        const furthestDistance = Math.cos(Math.PI / 2 - this._pitch) * topHalfSurfaceDistance + cameraToSeaLevelDistance;
        const horizonDistance = cameraToSeaLevelDistance * (1 / this._horizonShift);
        const farZ = Math.min(furthestDistance * 1.01, horizonDistance);
        const nearZ = this.height / 50;
        const worldToCamera = this._camera.getWorldToCamera(this.worldSize, pixelsPerMeter);
        const cameraToClip = this._camera.getCameraToClipPerspective(this._fov, this.width / this.height, nearZ, farZ);
        cameraToClip[8] = -offset.x * 2 / this.width;
        cameraToClip[9] = offset.y * 2 / this.height;
        let m = ref_properties.mul$1([], cameraToClip, worldToCamera);
        this.mercatorMatrix = ref_properties.scale$1([], m, [
            this.worldSize,
            this.worldSize,
            this.worldSize / pixelsPerMeter
        ]);
        this.projMatrix = m;
        this.invProjMatrix = ref_properties.invert(new Float64Array(16), this.projMatrix);
        const view = new Float32Array(16);
        ref_properties.identity(view);
        ref_properties.scale$1(view, view, [
            1,
            -1,
            1
        ]);
        ref_properties.rotateX(view, view, this._pitch);
        ref_properties.rotateZ(view, view, this.angle);
        const projection = ref_properties.perspective(new Float32Array(16), this._fov, this.width / this.height, nearZ, farZ);
        const skyboxHorizonShift = (Math.PI / 2 - this._pitch) * (this.height / this._fov) * this._horizonShift;
        projection[8] = -offset.x * 2 / this.width;
        projection[9] = (offset.y + skyboxHorizonShift) * 2 / this.height;
        this.skyboxMatrix = ref_properties.multiply(view, projection, view);
        const xShift = this.width % 2 / 2, yShift = this.height % 2 / 2, angleCos = Math.cos(this.angle), angleSin = Math.sin(this.angle), dx = x - Math.round(x) + angleCos * xShift + angleSin * yShift, dy = y - Math.round(y) + angleCos * yShift + angleSin * xShift;
        const alignedM = new Float64Array(m);
        ref_properties.translate(alignedM, alignedM, [
            dx > 0.5 ? dx - 1 : dx,
            dy > 0.5 ? dy - 1 : dy,
            0
        ]);
        this.alignedProjMatrix = alignedM;
        m = ref_properties.create$1();
        ref_properties.scale$1(m, m, [
            this.width / 2,
            -this.height / 2,
            1
        ]);
        ref_properties.translate(m, m, [
            1,
            -1,
            0
        ]);
        this.labelPlaneMatrix = m;
        m = ref_properties.create$1();
        ref_properties.scale$1(m, m, [
            1,
            -1,
            1
        ]);
        ref_properties.translate(m, m, [
            -1,
            -1,
            0
        ]);
        ref_properties.scale$1(m, m, [
            2 / this.width,
            2 / this.height,
            1
        ]);
        this.glCoordMatrix = m;
        this.pixelMatrix = ref_properties.multiply(new Float64Array(16), this.labelPlaneMatrix, this.projMatrix);
        this._calcFogMatrices();
        m = ref_properties.invert(new Float64Array(16), this.pixelMatrix);
        if (!m)
            throw new Error('failed to invert matrix');
        this.pixelMatrixInverse = m;
        this._projMatrixCache = {};
        this._alignedProjMatrixCache = {};
    }
    _calcFogMatrices() {
        this._fogTileMatrixCache = {};
        const cameraWorldSize = this.cameraWorldSize;
        const cameraPixelsPerMeter = this.cameraPixelsPerMeter;
        const cameraPos = this._camera.position;
        const windowScaleFactor = 1 / this.height;
        const metersToPixel = [
            cameraWorldSize,
            cameraWorldSize,
            cameraPixelsPerMeter
        ];
        ref_properties.scale(metersToPixel, metersToPixel, windowScaleFactor);
        ref_properties.scale(cameraPos, cameraPos, -1);
        ref_properties.multiply$1(cameraPos, cameraPos, metersToPixel);
        const m = ref_properties.create$1();
        ref_properties.translate(m, m, cameraPos);
        ref_properties.scale$1(m, m, metersToPixel);
        this.mercatorFogMatrix = m;
        this.worldToFogMatrix = this._camera.getWorldToCameraPosition(cameraWorldSize, cameraPixelsPerMeter, windowScaleFactor);
    }
    _updateCameraState() {
        if (!this.height)
            return;
        this._camera.setPitchBearing(this._pitch, this.angle);
        const dir = this._camera.forward();
        const distance = this.cameraToCenterDistance;
        const center = this.point;
        const zoom = this._cameraZoom ? this._cameraZoom : this._zoom;
        const altitude = this._mercatorZfromZoom(zoom);
        const height = altitude - ref_properties.mercatorZfromAltitude(this._centerAltitude, this.center.lat);
        const updatedWorldSize = this.cameraToCenterDistance / height;
        this._camera.position = [
            center.x / this.worldSize - dir[0] * distance / updatedWorldSize,
            center.y / this.worldSize - dir[1] * distance / updatedWorldSize,
            ref_properties.mercatorZfromAltitude(this._centerAltitude, this._center.lat) + -dir[2] * distance / updatedWorldSize
        ];
    }
    _translateCameraConstrained(translation) {
        const maxDistance = this._maxCameraBoundsDistance();
        const maxZ = maxDistance * Math.cos(this._pitch);
        const z = this._camera.position[2];
        const deltaZ = translation[2];
        let t = 1;
        if (deltaZ > 0) {
            t = Math.min((maxZ - z) / deltaZ, 1);
        }
        this._camera.position = ref_properties.scaleAndAdd([], this._camera.position, translation, t);
        this._updateStateFromCamera();
    }
    _updateStateFromCamera() {
        const position = this._camera.position;
        const dir = this._camera.forward();
        const {pitch, bearing} = this._camera.getPitchBearing();
        const centerAltitude = ref_properties.mercatorZfromAltitude(this._centerAltitude, this.center.lat);
        const minHeight = this._mercatorZfromZoom(this._maxZoom) * Math.cos(ref_properties.degToRad(this._maxPitch));
        const height = Math.max((position[2] - centerAltitude) / Math.cos(pitch), minHeight);
        const zoom = this._zoomFromMercatorZ(height);
        ref_properties.scaleAndAdd(position, position, dir, height);
        this._pitch = ref_properties.clamp(pitch, ref_properties.degToRad(this.minPitch), ref_properties.degToRad(this.maxPitch));
        this.angle = ref_properties.wrap(bearing, -Math.PI, Math.PI);
        this._setZoom(ref_properties.clamp(zoom, this._minZoom, this._maxZoom));
        if (this._terrainEnabled())
            this._updateCameraOnTerrain();
        this._center = new ref_properties.MercatorCoordinate(position[0], position[1], position[2]).toLngLat();
        this._unmodified = false;
        this._constrain();
        this._calcMatrices();
    }
    _worldSizeFromZoom(zoom) {
        return Math.pow(2, zoom) * this.tileSize;
    }
    _mercatorZfromZoom(zoom) {
        return this.cameraToCenterDistance / this._worldSizeFromZoom(zoom);
    }
    _minimumHeightOverTerrain() {
        const MAX_DRAPE_OVERZOOM = 2;
        const zoom = Math.min((this._cameraZoom != null ? this._cameraZoom : this._zoom) + MAX_DRAPE_OVERZOOM, this._maxZoom);
        return this._mercatorZfromZoom(zoom);
    }
    _zoomFromMercatorZ(z) {
        return this.scaleZoom(this.cameraToCenterDistance / (z * this.tileSize));
    }
    _terrainEnabled() {
        return !!this._elevation;
    }
    isHorizonVisibleForPoints(p0, p1) {
        const minX = Math.min(p0.x, p1.x);
        const maxX = Math.max(p0.x, p1.x);
        const minY = Math.min(p0.y, p1.y);
        const maxY = Math.max(p0.y, p1.y);
        const min = new ref_properties.pointGeometry(minX, minY);
        const max = new ref_properties.pointGeometry(maxX, maxY);
        const corners = [
            min,
            max,
            new ref_properties.pointGeometry(minX, maxY),
            new ref_properties.pointGeometry(maxX, minY)
        ];
        const minWX = this._renderWorldCopies ? -NUM_WORLD_COPIES : 0;
        const maxWX = this._renderWorldCopies ? 1 + NUM_WORLD_COPIES : 1;
        const minWY = 0;
        const maxWY = 1;
        for (const corner of corners) {
            const rayIntersection = this.pointRayIntersection(corner);
            if (rayIntersection.t < 0) {
                return true;
            }
            const coordinate = this.rayIntersectionCoordinate(rayIntersection);
            if (coordinate.x < minWX || coordinate.y < minWY || coordinate.x > maxWX || coordinate.y > maxWY) {
                return true;
            }
        }
        return false;
    }
    isHorizonVisible() {
        const horizonAngleEpsilon = 2;
        if (this.pitch + ref_properties.radToDeg(this.fovAboveCenter) > 90 - horizonAngleEpsilon) {
            return true;
        }
        return this.isHorizonVisibleForPoints(new ref_properties.pointGeometry(0, 0), new ref_properties.pointGeometry(this.width, this.height));
    }
    zoomDeltaToMovement(center, zoomDelta) {
        const distance = ref_properties.length(ref_properties.sub([], this._camera.position, center));
        const relativeZoom = this._zoomFromMercatorZ(distance) + zoomDelta;
        return distance - this._mercatorZfromZoom(relativeZoom);
    }
    getCameraPoint() {
        const pitch = this._pitch;
        const yOffset = Math.tan(pitch) * (this.cameraToCenterDistance || 1);
        return this.centerPoint.add(new ref_properties.pointGeometry(0, yOffset));
    }
}

function throttle(fn, time) {
    let pending = false;
    let timerId = null;
    const later = () => {
        timerId = null;
        if (pending) {
            fn();
            timerId = setTimeout(later, time);
            pending = false;
        }
    };
    return () => {
        pending = true;
        if (!timerId) {
            later();
        }
        return timerId;
    };
}

class Hash {
    constructor(hashName) {
        this._hashName = hashName && encodeURIComponent(hashName);
        ref_properties.bindAll([
            '_getCurrentHash',
            '_onHashChange',
            '_updateHash'
        ], this);
        this._updateHash = throttle(this._updateHashUnthrottled.bind(this), 30 * 1000 / 100);
    }
    addTo(map) {
        this._map = map;
        ref_properties.window.addEventListener('hashchange', this._onHashChange, false);
        this._map.on('moveend', this._updateHash);
        return this;
    }
    remove() {
        ref_properties.window.removeEventListener('hashchange', this._onHashChange, false);
        this._map.off('moveend', this._updateHash);
        clearTimeout(this._updateHash());
        delete this._map;
        return this;
    }
    getHashString(mapFeedback) {
        const center = this._map.getCenter(), zoom = Math.round(this._map.getZoom() * 100) / 100, precision = Math.ceil((zoom * Math.LN2 + Math.log(512 / 360 / 0.5)) / Math.LN10), m = Math.pow(10, precision), lng = Math.round(center.lng * m) / m, lat = Math.round(center.lat * m) / m, bearing = this._map.getBearing(), pitch = this._map.getPitch();
        let hash = '';
        if (mapFeedback) {
            hash += `/${ lng }/${ lat }/${ zoom }`;
        } else {
            hash += `${ zoom }/${ lat }/${ lng }`;
        }
        if (bearing || pitch)
            hash += `/${ Math.round(bearing * 10) / 10 }`;
        if (pitch)
            hash += `/${ Math.round(pitch) }`;
        if (this._hashName) {
            const hashName = this._hashName;
            let found = false;
            const parts = ref_properties.window.location.hash.slice(1).split('&').map(part => {
                const key = part.split('=')[0];
                if (key === hashName) {
                    found = true;
                    return `${ key }=${ hash }`;
                }
                return part;
            }).filter(a => a);
            if (!found) {
                parts.push(`${ hashName }=${ hash }`);
            }
            return `#${ parts.join('&') }`;
        }
        return `#${ hash }`;
    }
    _getCurrentHash() {
        const hash = ref_properties.window.location.hash.replace('#', '');
        if (this._hashName) {
            let keyval;
            hash.split('&').map(part => part.split('=')).forEach(part => {
                if (part[0] === this._hashName) {
                    keyval = part;
                }
            });
            return (keyval ? keyval[1] || '' : '').split('/');
        }
        return hash.split('/');
    }
    _onHashChange() {
        const loc = this._getCurrentHash();
        if (loc.length >= 3 && !loc.some(v => isNaN(v))) {
            const bearing = this._map.dragRotate.isEnabled() && this._map.touchZoomRotate.isEnabled() ? +(loc[3] || 0) : this._map.getBearing();
            this._map.jumpTo({
                center: [
                    +loc[2],
                    +loc[1]
                ],
                zoom: +loc[0],
                bearing,
                pitch: +(loc[4] || 0)
            });
            return true;
        }
        return false;
    }
    _updateHashUnthrottled() {
        const location = ref_properties.window.location.href.replace(/(#.+)?$/, this.getHashString());
        ref_properties.window.history.replaceState(ref_properties.window.history.state, null, location);
    }
}

const defaultInertiaOptions = {
    linearity: 0.3,
    easing: ref_properties.bezier(0, 0, 0.3, 1)
};
const defaultPanInertiaOptions = ref_properties.extend({
    deceleration: 2500,
    maxSpeed: 1400
}, defaultInertiaOptions);
const defaultZoomInertiaOptions = ref_properties.extend({
    deceleration: 20,
    maxSpeed: 1400
}, defaultInertiaOptions);
const defaultBearingInertiaOptions = ref_properties.extend({
    deceleration: 1000,
    maxSpeed: 360
}, defaultInertiaOptions);
const defaultPitchInertiaOptions = ref_properties.extend({
    deceleration: 1000,
    maxSpeed: 90
}, defaultInertiaOptions);
class HandlerInertia {
    constructor(map) {
        this._map = map;
        this.clear();
    }
    clear() {
        this._inertiaBuffer = [];
    }
    record(settings) {
        this._drainInertiaBuffer();
        this._inertiaBuffer.push({
            time: ref_properties.exported.now(),
            settings
        });
    }
    _drainInertiaBuffer() {
        const inertia = this._inertiaBuffer, now = ref_properties.exported.now(), cutoff = 160;
        while (inertia.length > 0 && now - inertia[0].time > cutoff)
            inertia.shift();
    }
    _onMoveEnd(panInertiaOptions) {
        this._drainInertiaBuffer();
        if (this._inertiaBuffer.length < 2) {
            return;
        }
        const deltas = {
            zoom: 0,
            bearing: 0,
            pitch: 0,
            pan: new ref_properties.pointGeometry(0, 0),
            pinchAround: undefined,
            around: undefined
        };
        for (const {settings} of this._inertiaBuffer) {
            deltas.zoom += settings.zoomDelta || 0;
            deltas.bearing += settings.bearingDelta || 0;
            deltas.pitch += settings.pitchDelta || 0;
            if (settings.panDelta)
                deltas.pan._add(settings.panDelta);
            if (settings.around)
                deltas.around = settings.around;
            if (settings.pinchAround)
                deltas.pinchAround = settings.pinchAround;
        }
        const lastEntry = this._inertiaBuffer[this._inertiaBuffer.length - 1];
        const duration = lastEntry.time - this._inertiaBuffer[0].time;
        const easeOptions = {};
        if (deltas.pan.mag()) {
            const result = calculateEasing(deltas.pan.mag(), duration, ref_properties.extend({}, defaultPanInertiaOptions, panInertiaOptions || {}));
            easeOptions.offset = deltas.pan.mult(result.amount / deltas.pan.mag());
            easeOptions.center = this._map.transform.center;
            extendDuration(easeOptions, result);
        }
        if (deltas.zoom) {
            const result = calculateEasing(deltas.zoom, duration, defaultZoomInertiaOptions);
            easeOptions.zoom = this._map.transform.zoom + result.amount;
            extendDuration(easeOptions, result);
        }
        if (deltas.bearing) {
            const result = calculateEasing(deltas.bearing, duration, defaultBearingInertiaOptions);
            easeOptions.bearing = this._map.transform.bearing + ref_properties.clamp(result.amount, -179, 179);
            extendDuration(easeOptions, result);
        }
        if (deltas.pitch) {
            const result = calculateEasing(deltas.pitch, duration, defaultPitchInertiaOptions);
            easeOptions.pitch = this._map.transform.pitch + result.amount;
            extendDuration(easeOptions, result);
        }
        if (easeOptions.zoom || easeOptions.bearing) {
            const last = deltas.pinchAround === undefined ? deltas.around : deltas.pinchAround;
            easeOptions.around = last ? this._map.unproject(last) : this._map.getCenter();
        }
        this.clear();
        return ref_properties.extend(easeOptions, { noMoveStart: true });
    }
}
function extendDuration(easeOptions, result) {
    if (!easeOptions.duration || easeOptions.duration < result.duration) {
        easeOptions.duration = result.duration;
        easeOptions.easing = result.easing;
    }
}
function calculateEasing(amount, inertiaDuration, inertiaOptions) {
    const {maxSpeed, linearity, deceleration} = inertiaOptions;
    const speed = ref_properties.clamp(amount * linearity / (inertiaDuration / 1000), -maxSpeed, maxSpeed);
    const duration = Math.abs(speed) / (deceleration * linearity);
    return {
        easing: inertiaOptions.easing,
        duration: duration * 1000,
        amount: speed * (duration / 2)
    };
}

class MapMouseEvent extends ref_properties.Event {
    preventDefault() {
        this._defaultPrevented = true;
    }
    get defaultPrevented() {
        return this._defaultPrevented;
    }
    constructor(type, map, originalEvent, data = {}) {
        const point = DOM.mousePos(map.getCanvasContainer(), originalEvent);
        const lngLat = map.unproject(point);
        super(type, ref_properties.extend({
            point,
            lngLat,
            originalEvent
        }, data));
        this._defaultPrevented = false;
        this.target = map;
    }
}
class MapTouchEvent extends ref_properties.Event {
    preventDefault() {
        this._defaultPrevented = true;
    }
    get defaultPrevented() {
        return this._defaultPrevented;
    }
    constructor(type, map, originalEvent) {
        const touches = type === 'touchend' ? originalEvent.changedTouches : originalEvent.touches;
        const points = DOM.touchPos(map.getCanvasContainer(), touches);
        const lngLats = points.map(t => map.unproject(t));
        const point = points.reduce((prev, curr, i, arr) => {
            return prev.add(curr.div(arr.length));
        }, new ref_properties.pointGeometry(0, 0));
        const lngLat = map.unproject(point);
        super(type, {
            points,
            point,
            lngLats,
            lngLat,
            originalEvent
        });
        this._defaultPrevented = false;
    }
}
class MapWheelEvent extends ref_properties.Event {
    preventDefault() {
        this._defaultPrevented = true;
    }
    get defaultPrevented() {
        return this._defaultPrevented;
    }
    constructor(type, map, originalEvent) {
        super(type, { originalEvent });
        this._defaultPrevented = false;
    }
}

class MapEventHandler {
    constructor(map, options) {
        this._map = map;
        this._clickTolerance = options.clickTolerance;
    }
    reset() {
        delete this._mousedownPos;
    }
    wheel(e) {
        return this._firePreventable(new MapWheelEvent(e.type, this._map, e));
    }
    mousedown(e, point) {
        this._mousedownPos = point;
        return this._firePreventable(new MapMouseEvent(e.type, this._map, e));
    }
    mouseup(e) {
        this._map.fire(new MapMouseEvent(e.type, this._map, e));
    }
    click(e, point) {
        if (this._mousedownPos && this._mousedownPos.dist(point) >= this._clickTolerance)
            return;
        this._map.fire(new MapMouseEvent(e.type, this._map, e));
    }
    dblclick(e) {
        return this._firePreventable(new MapMouseEvent(e.type, this._map, e));
    }
    mouseover(e) {
        this._map.fire(new MapMouseEvent(e.type, this._map, e));
    }
    mouseout(e) {
        this._map.fire(new MapMouseEvent(e.type, this._map, e));
    }
    touchstart(e) {
        return this._firePreventable(new MapTouchEvent(e.type, this._map, e));
    }
    touchmove(e) {
        this._map.fire(new MapTouchEvent(e.type, this._map, e));
    }
    touchend(e) {
        this._map.fire(new MapTouchEvent(e.type, this._map, e));
    }
    touchcancel(e) {
        this._map.fire(new MapTouchEvent(e.type, this._map, e));
    }
    _firePreventable(mapEvent) {
        this._map.fire(mapEvent);
        if (mapEvent.defaultPrevented) {
            return {};
        }
    }
    isEnabled() {
        return true;
    }
    isActive() {
        return false;
    }
    enable() {
    }
    disable() {
    }
}
class BlockableMapEventHandler {
    constructor(map) {
        this._map = map;
    }
    reset() {
        this._delayContextMenu = false;
        delete this._contextMenuEvent;
    }
    mousemove(e) {
        this._map.fire(new MapMouseEvent(e.type, this._map, e));
    }
    mousedown() {
        this._delayContextMenu = true;
    }
    mouseup() {
        this._delayContextMenu = false;
        if (this._contextMenuEvent) {
            this._map.fire(new MapMouseEvent('contextmenu', this._map, this._contextMenuEvent));
            delete this._contextMenuEvent;
        }
    }
    contextmenu(e) {
        if (this._delayContextMenu) {
            this._contextMenuEvent = e;
        } else {
            this._map.fire(new MapMouseEvent(e.type, this._map, e));
        }
        if (this._map.listens('contextmenu')) {
            e.preventDefault();
        }
    }
    isEnabled() {
        return true;
    }
    isActive() {
        return false;
    }
    enable() {
    }
    disable() {
    }
}

class BoxZoomHandler {
    constructor(map, options) {
        this._map = map;
        this._el = map.getCanvasContainer();
        this._container = map.getContainer();
        this._clickTolerance = options.clickTolerance || 1;
    }
    isEnabled() {
        return !!this._enabled;
    }
    isActive() {
        return !!this._active;
    }
    enable() {
        if (this.isEnabled())
            return;
        this._enabled = true;
    }
    disable() {
        if (!this.isEnabled())
            return;
        this._enabled = false;
    }
    mousedown(e, point) {
        if (!this.isEnabled())
            return;
        if (!(e.shiftKey && e.button === 0))
            return;
        DOM.disableDrag();
        this._startPos = this._lastPos = point;
        this._active = true;
    }
    mousemoveWindow(e, point) {
        if (!this._active)
            return;
        const pos = point;
        if (this._lastPos.equals(pos) || !this._box && pos.dist(this._startPos) < this._clickTolerance) {
            return;
        }
        const p0 = this._startPos;
        this._lastPos = pos;
        if (!this._box) {
            this._box = DOM.create('div', 'mapboxgl-boxzoom', this._container);
            this._container.classList.add('mapboxgl-crosshair');
            this._fireEvent('boxzoomstart', e);
        }
        const minX = Math.min(p0.x, pos.x), maxX = Math.max(p0.x, pos.x), minY = Math.min(p0.y, pos.y), maxY = Math.max(p0.y, pos.y);
        this._map._requestDomTask(() => {
            if (this._box) {
                DOM.setTransform(this._box, `translate(${ minX }px,${ minY }px)`);
                this._box.style.width = `${ maxX - minX }px`;
                this._box.style.height = `${ maxY - minY }px`;
            }
        });
    }
    mouseupWindow(e, point) {
        if (!this._active)
            return;
        if (e.button !== 0)
            return;
        const p0 = this._startPos, p1 = point;
        this.reset();
        DOM.suppressClick();
        if (p0.x === p1.x && p0.y === p1.y) {
            this._fireEvent('boxzoomcancel', e);
        } else {
            this._map.fire(new ref_properties.Event('boxzoomend', { originalEvent: e }));
            return { cameraAnimation: map => map.fitScreenCoordinates(p0, p1, this._map.getBearing(), { linear: false }) };
        }
    }
    keydown(e) {
        if (!this._active)
            return;
        if (e.keyCode === 27) {
            this.reset();
            this._fireEvent('boxzoomcancel', e);
        }
    }
    reset() {
        this._active = false;
        this._container.classList.remove('mapboxgl-crosshair');
        if (this._box) {
            DOM.remove(this._box);
            this._box = null;
        }
        DOM.enableDrag();
        delete this._startPos;
        delete this._lastPos;
    }
    _fireEvent(type, e) {
        return this._map.fire(new ref_properties.Event(type, { originalEvent: e }));
    }
}

function indexTouches(touches, points) {
    const obj = {};
    for (let i = 0; i < touches.length; i++) {
        obj[touches[i].identifier] = points[i];
    }
    return obj;
}

function getCentroid(points) {
    const sum = new ref_properties.pointGeometry(0, 0);
    for (const point of points) {
        sum._add(point);
    }
    return sum.div(points.length);
}
const MAX_TAP_INTERVAL = 500;
const MAX_TOUCH_TIME = 500;
const MAX_DIST = 30;
class SingleTapRecognizer {
    constructor(options) {
        this.reset();
        this.numTouches = options.numTouches;
    }
    reset() {
        delete this.centroid;
        delete this.startTime;
        delete this.touches;
        this.aborted = false;
    }
    touchstart(e, points, mapTouches) {
        if (this.centroid || mapTouches.length > this.numTouches) {
            this.aborted = true;
        }
        if (this.aborted) {
            return;
        }
        if (this.startTime === undefined) {
            this.startTime = e.timeStamp;
        }
        if (mapTouches.length === this.numTouches) {
            this.centroid = getCentroid(points);
            this.touches = indexTouches(mapTouches, points);
        }
    }
    touchmove(e, points, mapTouches) {
        if (this.aborted || !this.centroid)
            return;
        const newTouches = indexTouches(mapTouches, points);
        for (const id in this.touches) {
            const prevPos = this.touches[id];
            const pos = newTouches[id];
            if (!pos || pos.dist(prevPos) > MAX_DIST) {
                this.aborted = true;
            }
        }
    }
    touchend(e, points, mapTouches) {
        if (!this.centroid || e.timeStamp - this.startTime > MAX_TOUCH_TIME) {
            this.aborted = true;
        }
        if (mapTouches.length === 0) {
            const centroid = !this.aborted && this.centroid;
            this.reset();
            if (centroid)
                return centroid;
        }
    }
}
class TapRecognizer {
    constructor(options) {
        this.singleTap = new SingleTapRecognizer(options);
        this.numTaps = options.numTaps;
        this.reset();
    }
    reset() {
        this.lastTime = Infinity;
        delete this.lastTap;
        this.count = 0;
        this.singleTap.reset();
    }
    touchstart(e, points, mapTouches) {
        this.singleTap.touchstart(e, points, mapTouches);
    }
    touchmove(e, points, mapTouches) {
        this.singleTap.touchmove(e, points, mapTouches);
    }
    touchend(e, points, mapTouches) {
        const tap = this.singleTap.touchend(e, points, mapTouches);
        if (tap) {
            const soonEnough = e.timeStamp - this.lastTime < MAX_TAP_INTERVAL;
            const closeEnough = !this.lastTap || this.lastTap.dist(tap) < MAX_DIST;
            if (!soonEnough || !closeEnough) {
                this.reset();
            }
            this.count++;
            this.lastTime = e.timeStamp;
            this.lastTap = tap;
            if (this.count === this.numTaps) {
                this.reset();
                return tap;
            }
        }
    }
}

class TapZoomHandler {
    constructor() {
        this._zoomIn = new TapRecognizer({
            numTouches: 1,
            numTaps: 2
        });
        this._zoomOut = new TapRecognizer({
            numTouches: 2,
            numTaps: 1
        });
        this.reset();
    }
    reset() {
        this._active = false;
        this._zoomIn.reset();
        this._zoomOut.reset();
    }
    touchstart(e, points, mapTouches) {
        this._zoomIn.touchstart(e, points, mapTouches);
        this._zoomOut.touchstart(e, points, mapTouches);
    }
    touchmove(e, points, mapTouches) {
        this._zoomIn.touchmove(e, points, mapTouches);
        this._zoomOut.touchmove(e, points, mapTouches);
    }
    touchend(e, points, mapTouches) {
        const zoomInPoint = this._zoomIn.touchend(e, points, mapTouches);
        const zoomOutPoint = this._zoomOut.touchend(e, points, mapTouches);
        if (zoomInPoint) {
            this._active = true;
            e.preventDefault();
            setTimeout(() => this.reset(), 0);
            return {
                cameraAnimation: map => map.easeTo({
                    duration: 300,
                    zoom: map.getZoom() + 1,
                    around: map.unproject(zoomInPoint)
                }, { originalEvent: e })
            };
        } else if (zoomOutPoint) {
            this._active = true;
            e.preventDefault();
            setTimeout(() => this.reset(), 0);
            return {
                cameraAnimation: map => map.easeTo({
                    duration: 300,
                    zoom: map.getZoom() - 1,
                    around: map.unproject(zoomOutPoint)
                }, { originalEvent: e })
            };
        }
    }
    touchcancel() {
        this.reset();
    }
    enable() {
        this._enabled = true;
    }
    disable() {
        this._enabled = false;
        this.reset();
    }
    isEnabled() {
        return this._enabled;
    }
    isActive() {
        return this._active;
    }
}

const LEFT_BUTTON = 0;
const RIGHT_BUTTON = 2;
const BUTTONS_FLAGS = {
    [LEFT_BUTTON]: 1,
    [RIGHT_BUTTON]: 2
};
function buttonStillPressed(e, button) {
    const flag = BUTTONS_FLAGS[button];
    return e.buttons === undefined || (e.buttons & flag) !== flag;
}
class MouseHandler {
    constructor(options) {
        this.reset();
        this._clickTolerance = options.clickTolerance || 1;
    }
    reset() {
        this._active = false;
        this._moved = false;
        delete this._lastPoint;
        delete this._eventButton;
    }
    _correctButton(e, button) {
        return false;
    }
    _move(lastPoint, point) {
        return {};
    }
    mousedown(e, point) {
        if (this._lastPoint)
            return;
        const eventButton = DOM.mouseButton(e);
        if (!this._correctButton(e, eventButton))
            return;
        this._lastPoint = point;
        this._eventButton = eventButton;
    }
    mousemoveWindow(e, point) {
        const lastPoint = this._lastPoint;
        if (!lastPoint)
            return;
        e.preventDefault();
        if (buttonStillPressed(e, this._eventButton)) {
            this.reset();
            return;
        }
        if (!this._moved && point.dist(lastPoint) < this._clickTolerance)
            return;
        this._moved = true;
        this._lastPoint = point;
        return this._move(lastPoint, point);
    }
    mouseupWindow(e) {
        if (!this._lastPoint)
            return;
        const eventButton = DOM.mouseButton(e);
        if (eventButton !== this._eventButton)
            return;
        if (this._moved)
            DOM.suppressClick();
        this.reset();
    }
    enable() {
        this._enabled = true;
    }
    disable() {
        this._enabled = false;
        this.reset();
    }
    isEnabled() {
        return this._enabled;
    }
    isActive() {
        return this._active;
    }
}
class MousePanHandler extends MouseHandler {
    mousedown(e, point) {
        super.mousedown(e, point);
        if (this._lastPoint)
            this._active = true;
    }
    _correctButton(e, button) {
        return button === LEFT_BUTTON && !e.ctrlKey;
    }
    _move(lastPoint, point) {
        return {
            around: point,
            panDelta: point.sub(lastPoint)
        };
    }
}
class MouseRotateHandler extends MouseHandler {
    _correctButton(e, button) {
        return button === LEFT_BUTTON && e.ctrlKey || button === RIGHT_BUTTON;
    }
    _move(lastPoint, point) {
        const degreesPerPixelMoved = 0.8;
        const bearingDelta = (point.x - lastPoint.x) * degreesPerPixelMoved;
        if (bearingDelta) {
            this._active = true;
            return { bearingDelta };
        }
    }
    contextmenu(e) {
        e.preventDefault();
    }
}
class MousePitchHandler extends MouseHandler {
    _correctButton(e, button) {
        return button === LEFT_BUTTON && e.ctrlKey || button === RIGHT_BUTTON;
    }
    _move(lastPoint, point) {
        const degreesPerPixelMoved = -0.5;
        const pitchDelta = (point.y - lastPoint.y) * degreesPerPixelMoved;
        if (pitchDelta) {
            this._active = true;
            return { pitchDelta };
        }
    }
    contextmenu(e) {
        e.preventDefault();
    }
}

class TouchPanHandler {
    constructor(options) {
        this._minTouches = 1;
        this._clickTolerance = options.clickTolerance || 1;
        this.reset();
    }
    reset() {
        this._active = false;
        this._touches = {};
        this._sum = new ref_properties.pointGeometry(0, 0);
    }
    touchstart(e, points, mapTouches) {
        return this._calculateTransform(e, points, mapTouches);
    }
    touchmove(e, points, mapTouches) {
        if (!this._active || mapTouches.length < this._minTouches)
            return;
        e.preventDefault();
        return this._calculateTransform(e, points, mapTouches);
    }
    touchend(e, points, mapTouches) {
        this._calculateTransform(e, points, mapTouches);
        if (this._active && mapTouches.length < this._minTouches) {
            this.reset();
        }
    }
    touchcancel() {
        this.reset();
    }
    _calculateTransform(e, points, mapTouches) {
        if (mapTouches.length > 0)
            this._active = true;
        const touches = indexTouches(mapTouches, points);
        const touchPointSum = new ref_properties.pointGeometry(0, 0);
        const touchDeltaSum = new ref_properties.pointGeometry(0, 0);
        let touchDeltaCount = 0;
        for (const identifier in touches) {
            const point = touches[identifier];
            const prevPoint = this._touches[identifier];
            if (prevPoint) {
                touchPointSum._add(point);
                touchDeltaSum._add(point.sub(prevPoint));
                touchDeltaCount++;
                touches[identifier] = point;
            }
        }
        this._touches = touches;
        if (touchDeltaCount < this._minTouches || !touchDeltaSum.mag())
            return;
        const panDelta = touchDeltaSum.div(touchDeltaCount);
        this._sum._add(panDelta);
        if (this._sum.mag() < this._clickTolerance)
            return;
        const around = touchPointSum.div(touchDeltaCount);
        return {
            around,
            panDelta
        };
    }
    enable() {
        this._enabled = true;
    }
    disable() {
        this._enabled = false;
        this.reset();
    }
    isEnabled() {
        return this._enabled;
    }
    isActive() {
        return this._active;
    }
}

class TwoTouchHandler {
    constructor() {
        this.reset();
    }
    reset() {
        this._active = false;
        delete this._firstTwoTouches;
    }
    _start(points) {
    }
    _move(points, pinchAround, e) {
        return {};
    }
    touchstart(e, points, mapTouches) {
        if (this._firstTwoTouches || mapTouches.length < 2)
            return;
        this._firstTwoTouches = [
            mapTouches[0].identifier,
            mapTouches[1].identifier
        ];
        this._start([
            points[0],
            points[1]
        ]);
    }
    touchmove(e, points, mapTouches) {
        if (!this._firstTwoTouches)
            return;
        e.preventDefault();
        const [idA, idB] = this._firstTwoTouches;
        const a = getTouchById(mapTouches, points, idA);
        const b = getTouchById(mapTouches, points, idB);
        if (!a || !b)
            return;
        const pinchAround = this._aroundCenter ? null : a.add(b).div(2);
        return this._move([
            a,
            b
        ], pinchAround, e);
    }
    touchend(e, points, mapTouches) {
        if (!this._firstTwoTouches)
            return;
        const [idA, idB] = this._firstTwoTouches;
        const a = getTouchById(mapTouches, points, idA);
        const b = getTouchById(mapTouches, points, idB);
        if (a && b)
            return;
        if (this._active)
            DOM.suppressClick();
        this.reset();
    }
    touchcancel() {
        this.reset();
    }
    enable(options) {
        this._enabled = true;
        this._aroundCenter = !!options && options.around === 'center';
    }
    disable() {
        this._enabled = false;
        this.reset();
    }
    isEnabled() {
        return this._enabled;
    }
    isActive() {
        return this._active;
    }
}
function getTouchById(mapTouches, points, identifier) {
    for (let i = 0; i < mapTouches.length; i++) {
        if (mapTouches[i].identifier === identifier)
            return points[i];
    }
}
const ZOOM_THRESHOLD = 0.1;
function getZoomDelta(distance, lastDistance) {
    return Math.log(distance / lastDistance) / Math.LN2;
}
class TouchZoomHandler extends TwoTouchHandler {
    reset() {
        super.reset();
        delete this._distance;
        delete this._startDistance;
    }
    _start(points) {
        this._startDistance = this._distance = points[0].dist(points[1]);
    }
    _move(points, pinchAround) {
        const lastDistance = this._distance;
        this._distance = points[0].dist(points[1]);
        if (!this._active && Math.abs(getZoomDelta(this._distance, this._startDistance)) < ZOOM_THRESHOLD)
            return;
        this._active = true;
        return {
            zoomDelta: getZoomDelta(this._distance, lastDistance),
            pinchAround
        };
    }
}
const ROTATION_THRESHOLD = 25;
function getBearingDelta(a, b) {
    return a.angleWith(b) * 180 / Math.PI;
}
class TouchRotateHandler extends TwoTouchHandler {
    reset() {
        super.reset();
        delete this._minDiameter;
        delete this._startVector;
        delete this._vector;
    }
    _start(points) {
        this._startVector = this._vector = points[0].sub(points[1]);
        this._minDiameter = points[0].dist(points[1]);
    }
    _move(points, pinchAround) {
        const lastVector = this._vector;
        this._vector = points[0].sub(points[1]);
        if (!this._active && this._isBelowThreshold(this._vector))
            return;
        this._active = true;
        return {
            bearingDelta: getBearingDelta(this._vector, lastVector),
            pinchAround
        };
    }
    _isBelowThreshold(vector) {
        this._minDiameter = Math.min(this._minDiameter, vector.mag());
        const circumference = Math.PI * this._minDiameter;
        const threshold = ROTATION_THRESHOLD / circumference * 360;
        const bearingDeltaSinceStart = getBearingDelta(vector, this._startVector);
        return Math.abs(bearingDeltaSinceStart) < threshold;
    }
}
function isVertical(vector) {
    return Math.abs(vector.y) > Math.abs(vector.x);
}
const ALLOWED_SINGLE_TOUCH_TIME = 100;
class TouchPitchHandler extends TwoTouchHandler {
    reset() {
        super.reset();
        this._valid = undefined;
        delete this._firstMove;
        delete this._lastPoints;
    }
    _start(points) {
        this._lastPoints = points;
        if (isVertical(points[0].sub(points[1]))) {
            this._valid = false;
        }
    }
    _move(points, center, e) {
        const vectorA = points[0].sub(this._lastPoints[0]);
        const vectorB = points[1].sub(this._lastPoints[1]);
        this._valid = this.gestureBeginsVertically(vectorA, vectorB, e.timeStamp);
        if (!this._valid)
            return;
        this._lastPoints = points;
        this._active = true;
        const yDeltaAverage = (vectorA.y + vectorB.y) / 2;
        const degreesPerPixelMoved = -0.5;
        return { pitchDelta: yDeltaAverage * degreesPerPixelMoved };
    }
    gestureBeginsVertically(vectorA, vectorB, timeStamp) {
        if (this._valid !== undefined)
            return this._valid;
        const threshold = 2;
        const movedA = vectorA.mag() >= threshold;
        const movedB = vectorB.mag() >= threshold;
        if (!movedA && !movedB)
            return;
        if (!movedA || !movedB) {
            if (this._firstMove === undefined) {
                this._firstMove = timeStamp;
            }
            if (timeStamp - this._firstMove < ALLOWED_SINGLE_TOUCH_TIME) {
                return undefined;
            } else {
                return false;
            }
        }
        const isSameDirection = vectorA.y > 0 === vectorB.y > 0;
        return isVertical(vectorA) && isVertical(vectorB) && isSameDirection;
    }
}

const defaultOptions$5 = {
    panStep: 100,
    bearingStep: 15,
    pitchStep: 10
};
class KeyboardHandler {
    constructor() {
        const stepOptions = defaultOptions$5;
        this._panStep = stepOptions.panStep;
        this._bearingStep = stepOptions.bearingStep;
        this._pitchStep = stepOptions.pitchStep;
        this._rotationDisabled = false;
    }
    reset() {
        this._active = false;
    }
    keydown(e) {
        if (e.altKey || e.ctrlKey || e.metaKey)
            return;
        let zoomDir = 0;
        let bearingDir = 0;
        let pitchDir = 0;
        let xDir = 0;
        let yDir = 0;
        switch (e.keyCode) {
        case 61:
        case 107:
        case 171:
        case 187:
            zoomDir = 1;
            break;
        case 189:
        case 109:
        case 173:
            zoomDir = -1;
            break;
        case 37:
            if (e.shiftKey) {
                bearingDir = -1;
            } else {
                e.preventDefault();
                xDir = -1;
            }
            break;
        case 39:
            if (e.shiftKey) {
                bearingDir = 1;
            } else {
                e.preventDefault();
                xDir = 1;
            }
            break;
        case 38:
            if (e.shiftKey) {
                pitchDir = 1;
            } else {
                e.preventDefault();
                yDir = -1;
            }
            break;
        case 40:
            if (e.shiftKey) {
                pitchDir = -1;
            } else {
                e.preventDefault();
                yDir = 1;
            }
            break;
        default:
            return;
        }
        if (this._rotationDisabled) {
            bearingDir = 0;
            pitchDir = 0;
        }
        return {
            cameraAnimation: map => {
                const zoom = map.getZoom();
                map.easeTo({
                    duration: 300,
                    easeId: 'keyboardHandler',
                    easing: easeOut,
                    zoom: zoomDir ? Math.round(zoom) + zoomDir * (e.shiftKey ? 2 : 1) : zoom,
                    bearing: map.getBearing() + bearingDir * this._bearingStep,
                    pitch: map.getPitch() + pitchDir * this._pitchStep,
                    offset: [
                        -xDir * this._panStep,
                        -yDir * this._panStep
                    ],
                    center: map.getCenter()
                }, { originalEvent: e });
            }
        };
    }
    enable() {
        this._enabled = true;
    }
    disable() {
        this._enabled = false;
        this.reset();
    }
    isEnabled() {
        return this._enabled;
    }
    isActive() {
        return this._active;
    }
    disableRotation() {
        this._rotationDisabled = true;
    }
    enableRotation() {
        this._rotationDisabled = false;
    }
}
function easeOut(t) {
    return t * (2 - t);
}

const wheelZoomDelta = 4.000244140625;
const defaultZoomRate = 1 / 100;
const wheelZoomRate = 1 / 450;
const maxScalePerFrame = 2;
class ScrollZoomHandler {
    constructor(map, handler) {
        this._map = map;
        this._el = map.getCanvasContainer();
        this._handler = handler;
        this._delta = 0;
        this._defaultZoomRate = defaultZoomRate;
        this._wheelZoomRate = wheelZoomRate;
        ref_properties.bindAll(['_onTimeout'], this);
    }
    setZoomRate(zoomRate) {
        this._defaultZoomRate = zoomRate;
    }
    setWheelZoomRate(wheelZoomRate) {
        this._wheelZoomRate = wheelZoomRate;
    }
    isEnabled() {
        return !!this._enabled;
    }
    isActive() {
        return !!this._active || this._finishTimeout !== undefined;
    }
    isZooming() {
        return !!this._zooming;
    }
    enable(options) {
        if (this.isEnabled())
            return;
        this._enabled = true;
        this._aroundCenter = options && options.around === 'center';
    }
    disable() {
        if (!this.isEnabled())
            return;
        this._enabled = false;
    }
    wheel(e) {
        if (!this.isEnabled())
            return;
        let value = e.deltaMode === ref_properties.window.WheelEvent.DOM_DELTA_LINE ? e.deltaY * 40 : e.deltaY;
        const now = ref_properties.exported.now(), timeDelta = now - (this._lastWheelEventTime || 0);
        this._lastWheelEventTime = now;
        if (value !== 0 && value % wheelZoomDelta === 0) {
            this._type = 'wheel';
        } else if (value !== 0 && Math.abs(value) < 4) {
            this._type = 'trackpad';
        } else if (timeDelta > 400) {
            this._type = null;
            this._lastValue = value;
            this._timeout = setTimeout(this._onTimeout, 40, e);
        } else if (!this._type) {
            this._type = Math.abs(timeDelta * value) < 200 ? 'trackpad' : 'wheel';
            if (this._timeout) {
                clearTimeout(this._timeout);
                this._timeout = null;
                value += this._lastValue;
            }
        }
        if (e.shiftKey && value)
            value = value / 4;
        if (this._type) {
            this._lastWheelEvent = e;
            this._delta -= value;
            if (!this._active) {
                this._start(e);
            }
        }
        e.preventDefault();
    }
    _onTimeout(initialEvent) {
        this._type = 'wheel';
        this._delta -= this._lastValue;
        if (!this._active) {
            this._start(initialEvent);
        }
    }
    _start(e) {
        if (!this._delta)
            return;
        if (this._frameId) {
            this._frameId = null;
        }
        this._active = true;
        if (!this.isZooming()) {
            this._zooming = true;
        }
        if (this._finishTimeout) {
            clearTimeout(this._finishTimeout);
            delete this._finishTimeout;
        }
        const pos = DOM.mousePos(this._el, e);
        this._aroundPoint = this._aroundCenter ? this._map.transform.centerPoint : pos;
        this._aroundCoord = this._map.transform.pointCoordinate3D(this._aroundPoint);
        this._targetZoom = undefined;
        if (!this._frameId) {
            this._frameId = true;
            this._handler._triggerRenderFrame();
        }
    }
    renderFrame() {
        if (!this._frameId)
            return;
        this._frameId = null;
        if (!this.isActive())
            return;
        const tr = this._map.transform;
        const startingZoom = () => {
            return tr._terrainEnabled() ? tr.computeZoomRelativeTo(this._aroundCoord) : tr.zoom;
        };
        if (this._delta !== 0) {
            const zoomRate = this._type === 'wheel' && Math.abs(this._delta) > wheelZoomDelta ? this._wheelZoomRate : this._defaultZoomRate;
            let scale = maxScalePerFrame / (1 + Math.exp(-Math.abs(this._delta * zoomRate)));
            if (this._delta < 0 && scale !== 0) {
                scale = 1 / scale;
            }
            const startZoom = startingZoom();
            const startScale = Math.pow(2, startZoom);
            const fromScale = typeof this._targetZoom === 'number' ? tr.zoomScale(this._targetZoom) : startScale;
            this._targetZoom = Math.min(tr.maxZoom, Math.max(tr.minZoom, tr.scaleZoom(fromScale * scale)));
            if (this._type === 'wheel') {
                this._startZoom = startingZoom();
                this._easing = this._smoothOutEasing(200);
            }
            this._delta = 0;
        }
        const targetZoom = typeof this._targetZoom === 'number' ? this._targetZoom : startingZoom();
        const startZoom = this._startZoom;
        const easing = this._easing;
        let finished = false;
        let zoom;
        if (this._type === 'wheel' && startZoom && easing) {
            const t = Math.min((ref_properties.exported.now() - this._lastWheelEventTime) / 200, 1);
            const k = easing(t);
            zoom = ref_properties.number(startZoom, targetZoom, k);
            if (t < 1) {
                if (!this._frameId) {
                    this._frameId = true;
                }
            } else {
                finished = true;
            }
        } else {
            zoom = targetZoom;
            finished = true;
        }
        this._active = true;
        if (finished) {
            this._active = false;
            this._finishTimeout = setTimeout(() => {
                this._zooming = false;
                this._handler._triggerRenderFrame();
                delete this._targetZoom;
                delete this._finishTimeout;
            }, 200);
        }
        return {
            noInertia: true,
            needsRenderFrame: !finished,
            zoomDelta: zoom - startingZoom(),
            around: this._aroundPoint,
            aroundCoord: this._aroundCoord,
            originalEvent: this._lastWheelEvent
        };
    }
    _smoothOutEasing(duration) {
        let easing = ref_properties.ease;
        if (this._prevEase) {
            const ease = this._prevEase, t = (ref_properties.exported.now() - ease.start) / ease.duration, speed = ease.easing(t + 0.01) - ease.easing(t), x = 0.27 / Math.sqrt(speed * speed + 0.0001) * 0.01, y = Math.sqrt(0.27 * 0.27 - x * x);
            easing = ref_properties.bezier(x, y, 0.25, 1);
        }
        this._prevEase = {
            start: ref_properties.exported.now(),
            duration,
            easing
        };
        return easing;
    }
    reset() {
        this._active = false;
    }
}

class DoubleClickZoomHandler {
    constructor(clickZoom, TapZoom) {
        this._clickZoom = clickZoom;
        this._tapZoom = TapZoom;
    }
    enable() {
        this._clickZoom.enable();
        this._tapZoom.enable();
    }
    disable() {
        this._clickZoom.disable();
        this._tapZoom.disable();
    }
    isEnabled() {
        return this._clickZoom.isEnabled() && this._tapZoom.isEnabled();
    }
    isActive() {
        return this._clickZoom.isActive() || this._tapZoom.isActive();
    }
}

class ClickZoomHandler {
    constructor() {
        this.reset();
    }
    reset() {
        this._active = false;
    }
    dblclick(e, point) {
        e.preventDefault();
        return {
            cameraAnimation: map => {
                map.easeTo({
                    duration: 300,
                    zoom: map.getZoom() + (e.shiftKey ? -1 : 1),
                    around: map.unproject(point)
                }, { originalEvent: e });
            }
        };
    }
    enable() {
        this._enabled = true;
    }
    disable() {
        this._enabled = false;
        this.reset();
    }
    isEnabled() {
        return this._enabled;
    }
    isActive() {
        return this._active;
    }
}

class TapDragZoomHandler {
    constructor() {
        this._tap = new TapRecognizer({
            numTouches: 1,
            numTaps: 1
        });
        this.reset();
    }
    reset() {
        this._active = false;
        delete this._swipePoint;
        delete this._swipeTouch;
        delete this._tapTime;
        this._tap.reset();
    }
    touchstart(e, points, mapTouches) {
        if (this._swipePoint)
            return;
        if (this._tapTime && e.timeStamp - this._tapTime > MAX_TAP_INTERVAL) {
            this.reset();
        }
        if (!this._tapTime) {
            this._tap.touchstart(e, points, mapTouches);
        } else if (mapTouches.length > 0) {
            this._swipePoint = points[0];
            this._swipeTouch = mapTouches[0].identifier;
        }
    }
    touchmove(e, points, mapTouches) {
        if (!this._tapTime) {
            this._tap.touchmove(e, points, mapTouches);
        } else if (this._swipePoint) {
            if (mapTouches[0].identifier !== this._swipeTouch) {
                return;
            }
            const newSwipePoint = points[0];
            const dist = newSwipePoint.y - this._swipePoint.y;
            this._swipePoint = newSwipePoint;
            e.preventDefault();
            this._active = true;
            return { zoomDelta: dist / 128 };
        }
    }
    touchend(e, points, mapTouches) {
        if (!this._tapTime) {
            const point = this._tap.touchend(e, points, mapTouches);
            if (point) {
                this._tapTime = e.timeStamp;
            }
        } else if (this._swipePoint) {
            if (mapTouches.length === 0) {
                this.reset();
            }
        }
    }
    touchcancel() {
        this.reset();
    }
    enable() {
        this._enabled = true;
    }
    disable() {
        this._enabled = false;
        this.reset();
    }
    isEnabled() {
        return this._enabled;
    }
    isActive() {
        return this._active;
    }
}

class DragPanHandler {
    constructor(el, mousePan, touchPan) {
        this._el = el;
        this._mousePan = mousePan;
        this._touchPan = touchPan;
    }
    enable(options) {
        this._inertiaOptions = options || {};
        this._mousePan.enable();
        this._touchPan.enable();
        this._el.classList.add('mapboxgl-touch-drag-pan');
    }
    disable() {
        this._mousePan.disable();
        this._touchPan.disable();
        this._el.classList.remove('mapboxgl-touch-drag-pan');
    }
    isEnabled() {
        return this._mousePan.isEnabled() && this._touchPan.isEnabled();
    }
    isActive() {
        return this._mousePan.isActive() || this._touchPan.isActive();
    }
}

class DragRotateHandler {
    constructor(options, mouseRotate, mousePitch) {
        this._pitchWithRotate = options.pitchWithRotate;
        this._mouseRotate = mouseRotate;
        this._mousePitch = mousePitch;
    }
    enable() {
        this._mouseRotate.enable();
        if (this._pitchWithRotate)
            this._mousePitch.enable();
    }
    disable() {
        this._mouseRotate.disable();
        this._mousePitch.disable();
    }
    isEnabled() {
        return this._mouseRotate.isEnabled() && (!this._pitchWithRotate || this._mousePitch.isEnabled());
    }
    isActive() {
        return this._mouseRotate.isActive() || this._mousePitch.isActive();
    }
}

class TouchZoomRotateHandler {
    constructor(el, touchZoom, touchRotate, tapDragZoom) {
        this._el = el;
        this._touchZoom = touchZoom;
        this._touchRotate = touchRotate;
        this._tapDragZoom = tapDragZoom;
        this._rotationDisabled = false;
        this._enabled = true;
    }
    enable(options) {
        this._touchZoom.enable(options);
        if (!this._rotationDisabled)
            this._touchRotate.enable(options);
        this._tapDragZoom.enable();
        this._el.classList.add('mapboxgl-touch-zoom-rotate');
    }
    disable() {
        this._touchZoom.disable();
        this._touchRotate.disable();
        this._tapDragZoom.disable();
        this._el.classList.remove('mapboxgl-touch-zoom-rotate');
    }
    isEnabled() {
        return this._touchZoom.isEnabled() && (this._rotationDisabled || this._touchRotate.isEnabled()) && this._tapDragZoom.isEnabled();
    }
    isActive() {
        return this._touchZoom.isActive() || this._touchRotate.isActive() || this._tapDragZoom.isActive();
    }
    disableRotation() {
        this._rotationDisabled = true;
        this._touchRotate.disable();
    }
    enableRotation() {
        this._rotationDisabled = false;
        if (this._touchZoom.isEnabled())
            this._touchRotate.enable();
    }
}

const isMoving = p => p.zoom || p.drag || p.pitch || p.rotate;
class RenderFrameEvent extends ref_properties.Event {
}
class TrackingEllipsoid {
    constructor() {
        this.constants = [
            1,
            1,
            0.01
        ];
        this.radius = 0;
    }
    setup(center, pointOnSurface) {
        const centerToSurface = ref_properties.sub([], pointOnSurface, center);
        if (centerToSurface[2] < 0) {
            this.radius = ref_properties.length(ref_properties.div([], centerToSurface, this.constants));
        } else {
            this.radius = ref_properties.length([
                centerToSurface[0],
                centerToSurface[1],
                0
            ]);
        }
    }
    projectRay(dir) {
        ref_properties.div(dir, dir, this.constants);
        ref_properties.normalize(dir, dir);
        ref_properties.mul$2(dir, dir, this.constants);
        const intersection = ref_properties.scale([], dir, this.radius);
        if (intersection[2] > 0) {
            const h = ref_properties.scale([], [
                0,
                0,
                1
            ], ref_properties.dot(intersection, [
                0,
                0,
                1
            ]));
            const r = ref_properties.scale([], ref_properties.normalize([], [
                intersection[0],
                intersection[1],
                0
            ]), this.radius);
            const p = ref_properties.add([], intersection, ref_properties.scale([], ref_properties.sub([], ref_properties.add([], r, h), intersection), 2));
            intersection[0] = p[0];
            intersection[1] = p[1];
        }
        return intersection;
    }
}
function hasChange(result) {
    return result.panDelta && result.panDelta.mag() || result.zoomDelta || result.bearingDelta || result.pitchDelta;
}
class HandlerManager {
    constructor(map, options) {
        this._map = map;
        this._el = this._map.getCanvasContainer();
        this._handlers = [];
        this._handlersById = {};
        this._changes = [];
        this._inertia = new HandlerInertia(map);
        this._bearingSnap = options.bearingSnap;
        this._previousActiveHandlers = {};
        this._trackingEllipsoid = new TrackingEllipsoid();
        this._dragOrigin = null;
        this._eventsInProgress = {};
        this._addDefaultHandlers(options);
        ref_properties.bindAll([
            'handleEvent',
            'handleWindowEvent'
        ], this);
        const el = this._el;
        this._listeners = [
            [
                el,
                'touchstart',
                { passive: true }
            ],
            [
                el,
                'touchmove',
                { passive: false }
            ],
            [
                el,
                'touchend',
                undefined
            ],
            [
                el,
                'touchcancel',
                undefined
            ],
            [
                el,
                'mousedown',
                undefined
            ],
            [
                el,
                'mousemove',
                undefined
            ],
            [
                el,
                'mouseup',
                undefined
            ],
            [
                ref_properties.window.document,
                'mousemove',
                { capture: true }
            ],
            [
                ref_properties.window.document,
                'mouseup',
                undefined
            ],
            [
                el,
                'mouseover',
                undefined
            ],
            [
                el,
                'mouseout',
                undefined
            ],
            [
                el,
                'dblclick',
                undefined
            ],
            [
                el,
                'click',
                undefined
            ],
            [
                el,
                'keydown',
                { capture: false }
            ],
            [
                el,
                'keyup',
                undefined
            ],
            [
                el,
                'wheel',
                { passive: false }
            ],
            [
                el,
                'contextmenu',
                undefined
            ],
            [
                ref_properties.window,
                'blur',
                undefined
            ]
        ];
        for (const [target, type, listenerOptions] of this._listeners) {
            DOM.addEventListener(target, type, target === ref_properties.window.document ? this.handleWindowEvent : this.handleEvent, listenerOptions);
        }
    }
    destroy() {
        for (const [target, type, listenerOptions] of this._listeners) {
            DOM.removeEventListener(target, type, target === ref_properties.window.document ? this.handleWindowEvent : this.handleEvent, listenerOptions);
        }
    }
    _addDefaultHandlers(options) {
        const map = this._map;
        const el = map.getCanvasContainer();
        this._add('mapEvent', new MapEventHandler(map, options));
        const boxZoom = map.boxZoom = new BoxZoomHandler(map, options);
        this._add('boxZoom', boxZoom);
        const tapZoom = new TapZoomHandler();
        const clickZoom = new ClickZoomHandler();
        map.doubleClickZoom = new DoubleClickZoomHandler(clickZoom, tapZoom);
        this._add('tapZoom', tapZoom);
        this._add('clickZoom', clickZoom);
        const tapDragZoom = new TapDragZoomHandler();
        this._add('tapDragZoom', tapDragZoom);
        const touchPitch = map.touchPitch = new TouchPitchHandler();
        this._add('touchPitch', touchPitch);
        const mouseRotate = new MouseRotateHandler(options);
        const mousePitch = new MousePitchHandler(options);
        map.dragRotate = new DragRotateHandler(options, mouseRotate, mousePitch);
        this._add('mouseRotate', mouseRotate, ['mousePitch']);
        this._add('mousePitch', mousePitch, ['mouseRotate']);
        const mousePan = new MousePanHandler(options);
        const touchPan = new TouchPanHandler(options);
        map.dragPan = new DragPanHandler(el, mousePan, touchPan);
        this._add('mousePan', mousePan);
        this._add('touchPan', touchPan, [
            'touchZoom',
            'touchRotate'
        ]);
        const touchRotate = new TouchRotateHandler();
        const touchZoom = new TouchZoomHandler();
        map.touchZoomRotate = new TouchZoomRotateHandler(el, touchZoom, touchRotate, tapDragZoom);
        this._add('touchRotate', touchRotate, [
            'touchPan',
            'touchZoom'
        ]);
        this._add('touchZoom', touchZoom, [
            'touchPan',
            'touchRotate'
        ]);
        this._add('blockableMapEvent', new BlockableMapEventHandler(map));
        const scrollZoom = map.scrollZoom = new ScrollZoomHandler(map, this);
        this._add('scrollZoom', scrollZoom, ['mousePan']);
        const keyboard = map.keyboard = new KeyboardHandler();
        this._add('keyboard', keyboard);
        for (const name of [
                'boxZoom',
                'doubleClickZoom',
                'tapDragZoom',
                'touchPitch',
                'dragRotate',
                'dragPan',
                'touchZoomRotate',
                'scrollZoom',
                'keyboard'
            ]) {
            if (options.interactive && options[name]) {
                map[name].enable(options[name]);
            }
        }
    }
    _add(handlerName, handler, allowed) {
        this._handlers.push({
            handlerName,
            handler,
            allowed
        });
        this._handlersById[handlerName] = handler;
    }
    stop(allowEndAnimation) {
        if (this._updatingCamera)
            return;
        for (const {handler} of this._handlers) {
            handler.reset();
        }
        this._inertia.clear();
        this._fireEvents({}, {}, allowEndAnimation);
        this._changes = [];
    }
    isActive() {
        for (const {handler} of this._handlers) {
            if (handler.isActive())
                return true;
        }
        return false;
    }
    isZooming() {
        return !!this._eventsInProgress.zoom || this._map.scrollZoom.isZooming();
    }
    isRotating() {
        return !!this._eventsInProgress.rotate;
    }
    isMoving() {
        return Boolean(isMoving(this._eventsInProgress)) || this.isZooming();
    }
    _blockedByActive(activeHandlers, allowed, myName) {
        for (const name in activeHandlers) {
            if (name === myName)
                continue;
            if (!allowed || allowed.indexOf(name) < 0) {
                return true;
            }
        }
        return false;
    }
    handleWindowEvent(e) {
        this.handleEvent(e, `${ e.type }Window`);
    }
    _getMapTouches(touches) {
        const mapTouches = [];
        for (const t of touches) {
            const target = t.target;
            if (this._el.contains(target)) {
                mapTouches.push(t);
            }
        }
        return mapTouches;
    }
    handleEvent(e, eventName) {
        if (e.type === 'blur') {
            this.stop(true);
            return;
        }
        this._updatingCamera = true;
        const isRenderFrame = e.type === 'renderFrame';
        const inputEvent = isRenderFrame ? undefined : e;
        const mergedHandlerResult = { needsRenderFrame: false };
        const eventsInProgress = {};
        const activeHandlers = {};
        const mapTouches = e.touches ? this._getMapTouches(e.touches) : undefined;
        const points = mapTouches ? DOM.touchPos(this._el, mapTouches) : isRenderFrame ? undefined : DOM.mousePos(this._el, e);
        for (const {handlerName, handler, allowed} of this._handlers) {
            if (!handler.isEnabled())
                continue;
            let data;
            if (this._blockedByActive(activeHandlers, allowed, handlerName)) {
                handler.reset();
            } else {
                if (handler[eventName || e.type]) {
                    data = handler[eventName || e.type](e, points, mapTouches);
                    this.mergeHandlerResult(mergedHandlerResult, eventsInProgress, data, handlerName, inputEvent);
                    if (data && data.needsRenderFrame) {
                        this._triggerRenderFrame();
                    }
                }
            }
            if (data || handler.isActive()) {
                activeHandlers[handlerName] = handler;
            }
        }
        const deactivatedHandlers = {};
        for (const name in this._previousActiveHandlers) {
            if (!activeHandlers[name]) {
                deactivatedHandlers[name] = inputEvent;
            }
        }
        this._previousActiveHandlers = activeHandlers;
        if (Object.keys(deactivatedHandlers).length || hasChange(mergedHandlerResult)) {
            this._changes.push([
                mergedHandlerResult,
                eventsInProgress,
                deactivatedHandlers
            ]);
            this._triggerRenderFrame();
        }
        if (Object.keys(activeHandlers).length || hasChange(mergedHandlerResult)) {
            this._map._stop(true);
        }
        this._updatingCamera = false;
        const {cameraAnimation} = mergedHandlerResult;
        if (cameraAnimation) {
            this._inertia.clear();
            this._fireEvents({}, {}, true);
            this._changes = [];
            cameraAnimation(this._map);
        }
    }
    mergeHandlerResult(mergedHandlerResult, eventsInProgress, handlerResult, name, e) {
        if (!handlerResult)
            return;
        ref_properties.extend(mergedHandlerResult, handlerResult);
        const eventData = {
            handlerName: name,
            originalEvent: handlerResult.originalEvent || e
        };
        if (handlerResult.zoomDelta !== undefined) {
            eventsInProgress.zoom = eventData;
        }
        if (handlerResult.panDelta !== undefined) {
            eventsInProgress.drag = eventData;
        }
        if (handlerResult.pitchDelta !== undefined) {
            eventsInProgress.pitch = eventData;
        }
        if (handlerResult.bearingDelta !== undefined) {
            eventsInProgress.rotate = eventData;
        }
    }
    _applyChanges() {
        const combined = {};
        const combinedEventsInProgress = {};
        const combinedDeactivatedHandlers = {};
        for (const [change, eventsInProgress, deactivatedHandlers] of this._changes) {
            if (change.panDelta)
                combined.panDelta = (combined.panDelta || new ref_properties.pointGeometry(0, 0))._add(change.panDelta);
            if (change.zoomDelta)
                combined.zoomDelta = (combined.zoomDelta || 0) + change.zoomDelta;
            if (change.bearingDelta)
                combined.bearingDelta = (combined.bearingDelta || 0) + change.bearingDelta;
            if (change.pitchDelta)
                combined.pitchDelta = (combined.pitchDelta || 0) + change.pitchDelta;
            if (change.around !== undefined)
                combined.around = change.around;
            if (change.aroundCoord !== undefined)
                combined.aroundCoord = change.aroundCoord;
            if (change.pinchAround !== undefined)
                combined.pinchAround = change.pinchAround;
            if (change.noInertia)
                combined.noInertia = change.noInertia;
            ref_properties.extend(combinedEventsInProgress, eventsInProgress);
            ref_properties.extend(combinedDeactivatedHandlers, deactivatedHandlers);
        }
        this._updateMapTransform(combined, combinedEventsInProgress, combinedDeactivatedHandlers);
        this._changes = [];
    }
    _updateMapTransform(combinedResult, combinedEventsInProgress, deactivatedHandlers) {
        const map = this._map;
        const tr = map.transform;
        const eventStarted = type => {
            const newEvent = combinedEventsInProgress[type];
            return newEvent && !this._eventsInProgress[type];
        };
        const eventEnded = type => {
            const event = this._eventsInProgress[type];
            return event && !this._handlersById[event.handlerName].isActive();
        };
        const toVec3 = p => [
            p.x,
            p.y,
            p.z
        ];
        if (eventEnded('drag') && !hasChange(combinedResult)) {
            const preZoom = tr.zoom;
            tr.cameraElevationReference = 'sea';
            tr.recenterOnTerrain();
            tr.cameraElevationReference = 'ground';
            if (preZoom !== tr.zoom)
                this._map._update(true);
        }
        if (!hasChange(combinedResult)) {
            return this._fireEvents(combinedEventsInProgress, deactivatedHandlers, true);
        }
        let {panDelta, zoomDelta, bearingDelta, pitchDelta, around, aroundCoord, pinchAround} = combinedResult;
        if (pinchAround !== undefined) {
            around = pinchAround;
        }
        if (eventStarted('drag') && around) {
            this._dragOrigin = toVec3(tr.pointCoordinate3D(around));
            this._trackingEllipsoid.setup(tr._camera.position, this._dragOrigin);
        }
        tr.cameraElevationReference = 'sea';
        map._stop(true);
        around = around || map.transform.centerPoint;
        if (bearingDelta)
            tr.bearing += bearingDelta;
        if (pitchDelta)
            tr.pitch += pitchDelta;
        tr._updateCameraState();
        const panVec = [
            0,
            0,
            0
        ];
        if (panDelta) {
            const startRay = tr.screenPointToMercatorRay(around);
            const endRay = tr.screenPointToMercatorRay(around.sub(panDelta));
            const startPoint = this._trackingEllipsoid.projectRay(startRay.dir);
            const endPoint = this._trackingEllipsoid.projectRay(endRay.dir);
            panVec[0] = endPoint[0] - startPoint[0];
            panVec[1] = endPoint[1] - startPoint[1];
        }
        const originalZoom = tr.zoom;
        const zoomVec = [
            0,
            0,
            0
        ];
        if (zoomDelta) {
            const pickedPosition = aroundCoord ? toVec3(aroundCoord) : toVec3(tr.pointCoordinate3D(around));
            const aroundRay = { dir: ref_properties.normalize([], ref_properties.sub([], pickedPosition, tr._camera.position)) };
            const centerRay = tr.screenPointToMercatorRay(tr.centerPoint);
            if (aroundRay.dir[2] < 0) {
                const pickedAltitude = ref_properties.altitudeFromMercatorZ(pickedPosition[2], pickedPosition[1]);
                const centerOnTargetPlane = tr.rayIntersectionCoordinate(tr.pointRayIntersection(tr.centerPoint, pickedAltitude));
                const movement = tr.zoomDeltaToMovement(toVec3(centerOnTargetPlane), zoomDelta) * (centerRay.dir[2] / aroundRay.dir[2]);
                ref_properties.scale(zoomVec, aroundRay.dir, movement);
            } else if (tr._terrainEnabled()) {
                const movement = tr.zoomDeltaToMovement(pickedPosition, zoomDelta);
                ref_properties.scale(zoomVec, aroundRay.dir, movement);
            }
        }
        const translation = ref_properties.add(panVec, panVec, zoomVec);
        tr._translateCameraConstrained(translation);
        if (zoomDelta && Math.abs(tr.zoom - originalZoom) > 0.0001) {
            tr.recenterOnTerrain();
        }
        tr.cameraElevationReference = 'ground';
        this._map._update();
        if (!combinedResult.noInertia)
            this._inertia.record(combinedResult);
        this._fireEvents(combinedEventsInProgress, deactivatedHandlers, true);
    }
    _fireEvents(newEventsInProgress, deactivatedHandlers, allowEndAnimation) {
        const wasMoving = isMoving(this._eventsInProgress);
        const nowMoving = isMoving(newEventsInProgress);
        const startEvents = {};
        for (const eventName in newEventsInProgress) {
            const {originalEvent} = newEventsInProgress[eventName];
            if (!this._eventsInProgress[eventName]) {
                startEvents[`${ eventName }start`] = originalEvent;
            }
            this._eventsInProgress[eventName] = newEventsInProgress[eventName];
        }
        if (!wasMoving && nowMoving) {
            this._fireEvent('movestart', nowMoving.originalEvent);
        }
        for (const name in startEvents) {
            this._fireEvent(name, startEvents[name]);
        }
        if (nowMoving) {
            this._fireEvent('move', nowMoving.originalEvent);
        }
        for (const eventName in newEventsInProgress) {
            const {originalEvent} = newEventsInProgress[eventName];
            this._fireEvent(eventName, originalEvent);
        }
        const endEvents = {};
        let originalEndEvent;
        for (const eventName in this._eventsInProgress) {
            const {handlerName, originalEvent} = this._eventsInProgress[eventName];
            if (!this._handlersById[handlerName].isActive()) {
                delete this._eventsInProgress[eventName];
                originalEndEvent = deactivatedHandlers[handlerName] || originalEvent;
                endEvents[`${ eventName }end`] = originalEndEvent;
            }
        }
        for (const name in endEvents) {
            this._fireEvent(name, endEvents[name]);
        }
        const stillMoving = isMoving(this._eventsInProgress);
        if (allowEndAnimation && (wasMoving || nowMoving) && !stillMoving) {
            this._updatingCamera = true;
            const inertialEase = this._inertia._onMoveEnd(this._map.dragPan._inertiaOptions);
            const shouldSnapToNorth = bearing => bearing !== 0 && -this._bearingSnap < bearing && bearing < this._bearingSnap;
            if (inertialEase) {
                if (shouldSnapToNorth(inertialEase.bearing || this._map.getBearing())) {
                    inertialEase.bearing = 0;
                }
                this._map.easeTo(inertialEase, { originalEvent: originalEndEvent });
            } else {
                this._map.fire(new ref_properties.Event('moveend', { originalEvent: originalEndEvent }));
                if (shouldSnapToNorth(this._map.getBearing())) {
                    this._map.resetNorth();
                }
            }
            this._updatingCamera = false;
        }
    }
    _fireEvent(type, e) {
        this._map.fire(new ref_properties.Event(type, e ? { originalEvent: e } : {}));
    }
    _requestFrame() {
        this._map.triggerRepaint();
        return this._map._renderTaskQueue.add(timeStamp => {
            delete this._frameId;
            this.handleEvent(new RenderFrameEvent('renderFrame', { timeStamp }));
            this._applyChanges();
        });
    }
    _triggerRenderFrame() {
        if (this._frameId === undefined) {
            this._frameId = this._requestFrame();
        }
    }
}

class Camera extends ref_properties.Evented {
    constructor(transform, options) {
        super();
        this._moving = false;
        this._zooming = false;
        this.transform = transform;
        this._bearingSnap = options.bearingSnap;
        ref_properties.bindAll(['_renderFrameCallback'], this);
    }
    getCenter() {
        return new ref_properties.LngLat(this.transform.center.lng, this.transform.center.lat);
    }
    setCenter(center, eventData) {
        return this.jumpTo({ center }, eventData);
    }
    panBy(offset, options, eventData) {
        offset = ref_properties.pointGeometry.convert(offset).mult(-1);
        return this.panTo(this.transform.center, ref_properties.extend({ offset }, options), eventData);
    }
    panTo(lnglat, options, eventData) {
        return this.easeTo(ref_properties.extend({ center: lnglat }, options), eventData);
    }
    getZoom() {
        return this.transform.zoom;
    }
    setZoom(zoom, eventData) {
        this.jumpTo({ zoom }, eventData);
        return this;
    }
    zoomTo(zoom, options, eventData) {
        return this.easeTo(ref_properties.extend({ zoom }, options), eventData);
    }
    zoomIn(options, eventData) {
        this.zoomTo(this.getZoom() + 1, options, eventData);
        return this;
    }
    zoomOut(options, eventData) {
        this.zoomTo(this.getZoom() - 1, options, eventData);
        return this;
    }
    getBearing() {
        return this.transform.bearing;
    }
    setBearing(bearing, eventData) {
        this.jumpTo({ bearing }, eventData);
        return this;
    }
    getPadding() {
        return this.transform.padding;
    }
    setPadding(padding, eventData) {
        this.jumpTo({ padding }, eventData);
        return this;
    }
    rotateTo(bearing, options, eventData) {
        return this.easeTo(ref_properties.extend({ bearing }, options), eventData);
    }
    resetNorth(options, eventData) {
        this.rotateTo(0, ref_properties.extend({ duration: 1000 }, options), eventData);
        return this;
    }
    resetNorthPitch(options, eventData) {
        this.easeTo(ref_properties.extend({
            bearing: 0,
            pitch: 0,
            duration: 1000
        }, options), eventData);
        return this;
    }
    snapToNorth(options, eventData) {
        if (Math.abs(this.getBearing()) < this._bearingSnap) {
            return this.resetNorth(options, eventData);
        }
        return this;
    }
    getPitch() {
        return this.transform.pitch;
    }
    setPitch(pitch, eventData) {
        this.jumpTo({ pitch }, eventData);
        return this;
    }
    cameraForBounds(bounds, options) {
        bounds = ref_properties.LngLatBounds.convert(bounds);
        const bearing = options && options.bearing || 0;
        return this._cameraForBoxAndBearing(bounds.getNorthWest(), bounds.getSouthEast(), bearing, options);
    }
    _extendCameraOptions(options) {
        const defaultPadding = {
            top: 0,
            bottom: 0,
            right: 0,
            left: 0
        };
        options = ref_properties.extend({
            padding: defaultPadding,
            offset: [
                0,
                0
            ],
            maxZoom: this.transform.maxZoom
        }, options);
        if (typeof options.padding === 'number') {
            const p = options.padding;
            options.padding = {
                top: p,
                bottom: p,
                right: p,
                left: p
            };
        }
        options.padding = ref_properties.extend(defaultPadding, options.padding);
        return options;
    }
    _cameraForBoxAndBearing(p0, p1, bearing, options) {
        const eOptions = this._extendCameraOptions(options);
        const tr = this.transform;
        const edgePadding = tr.padding;
        const p0world = tr.project(ref_properties.LngLat.convert(p0));
        const p1world = tr.project(ref_properties.LngLat.convert(p1));
        const p0rotated = p0world.rotate(-ref_properties.degToRad(bearing));
        const p1rotated = p1world.rotate(-ref_properties.degToRad(bearing));
        const upperRight = new ref_properties.pointGeometry(Math.max(p0rotated.x, p1rotated.x), Math.max(p0rotated.y, p1rotated.y));
        const lowerLeft = new ref_properties.pointGeometry(Math.min(p0rotated.x, p1rotated.x), Math.min(p0rotated.y, p1rotated.y));
        const size = upperRight.sub(lowerLeft);
        const scaleX = (tr.width - (edgePadding.left + edgePadding.right + eOptions.padding.left + eOptions.padding.right)) / size.x;
        const scaleY = (tr.height - (edgePadding.top + edgePadding.bottom + eOptions.padding.top + eOptions.padding.bottom)) / size.y;
        if (scaleY < 0 || scaleX < 0) {
            ref_properties.warnOnce('Map cannot fit within canvas with the given bounds, padding, and/or offset.');
            return;
        }
        const zoom = Math.min(tr.scaleZoom(tr.scale * Math.min(scaleX, scaleY)), eOptions.maxZoom);
        const offset = typeof eOptions.offset.x === 'number' ? new ref_properties.pointGeometry(eOptions.offset.x, eOptions.offset.y) : ref_properties.pointGeometry.convert(eOptions.offset);
        const paddingOffsetX = (eOptions.padding.left - eOptions.padding.right) / 2;
        const paddingOffsetY = (eOptions.padding.top - eOptions.padding.bottom) / 2;
        const paddingOffset = new ref_properties.pointGeometry(paddingOffsetX, paddingOffsetY);
        const rotatedPaddingOffset = paddingOffset.rotate(bearing * Math.PI / 180);
        const offsetAtInitialZoom = offset.add(rotatedPaddingOffset);
        const offsetAtFinalZoom = offsetAtInitialZoom.mult(tr.scale / tr.zoomScale(zoom));
        const center = tr.unproject(p0world.add(p1world).div(2).sub(offsetAtFinalZoom));
        return {
            center,
            zoom,
            bearing
        };
    }
    _cameraForBox(p0, p1, minAltitude, maxAltitude, options) {
        const eOptions = this._extendCameraOptions(options);
        minAltitude = minAltitude || 0;
        maxAltitude = maxAltitude || 0;
        p0 = ref_properties.LngLat.convert(p0);
        p1 = ref_properties.LngLat.convert(p1);
        const tr = this.transform.clone();
        tr.padding = eOptions.padding;
        const camera = this.getFreeCameraOptions();
        const focus = new ref_properties.LngLat((p0.lng + p1.lng) * 0.5, (p0.lat + p1.lat) * 0.5);
        const focusAltitude = (minAltitude + maxAltitude) * 0.5;
        if (tr._camera.position[2] < ref_properties.mercatorZfromAltitude(focusAltitude, focus.lat)) {
            ref_properties.warnOnce('Map cannot fit within canvas with the given bounds, padding, and/or offset.');
            return;
        }
        camera.lookAtPoint(focus);
        tr.setFreeCameraOptions(camera);
        const coord0 = ref_properties.MercatorCoordinate.fromLngLat(p0);
        const coord1 = ref_properties.MercatorCoordinate.fromLngLat(p1);
        const toVec3 = p => [
            p.x,
            p.y,
            p.z
        ];
        const centerIntersectionPoint = tr.pointRayIntersection(tr.centerPoint, focusAltitude);
        const centerIntersectionCoord = toVec3(tr.rayIntersectionCoordinate(centerIntersectionPoint));
        const centerMercatorRay = tr.screenPointToMercatorRay(tr.centerPoint);
        const maxMarchingSteps = 10;
        let steps = 0;
        let halfDistanceToGround;
        do {
            const z = Math.floor(tr.zoom);
            const z2 = 1 << z;
            const minX = Math.min(z2 * coord0.x, z2 * coord1.x);
            const minY = Math.min(z2 * coord0.y, z2 * coord1.y);
            const maxX = Math.max(z2 * coord0.x, z2 * coord1.x);
            const maxY = Math.max(z2 * coord0.y, z2 * coord1.y);
            const aabb = new Aabb([
                minX,
                minY,
                minAltitude
            ], [
                maxX,
                maxY,
                maxAltitude
            ]);
            const frustum = Frustum.fromInvProjectionMatrix(tr.invProjMatrix, tr.worldSize, z);
            if (aabb.intersects(frustum) !== 2) {
                if (halfDistanceToGround) {
                    tr._camera.position = ref_properties.scaleAndAdd([], tr._camera.position, centerMercatorRay.dir, -halfDistanceToGround);
                    tr._updateStateFromCamera();
                }
                break;
            }
            const cameraPositionToGround = ref_properties.sub([], tr._camera.position, centerIntersectionCoord);
            halfDistanceToGround = 0.5 * ref_properties.length(cameraPositionToGround);
            tr._camera.position = ref_properties.scaleAndAdd([], tr._camera.position, centerMercatorRay.dir, halfDistanceToGround);
            try {
                tr._updateStateFromCamera();
            } catch (e) {
                ref_properties.warnOnce('Map cannot fit within canvas with the given bounds, padding, and/or offset.');
                return;
            }
        } while (++steps < maxMarchingSteps);
        return {
            center: tr.center,
            zoom: tr.zoom,
            bearing: tr.bearing,
            pitch: tr.pitch
        };
    }
    fitBounds(bounds, options, eventData) {
        return this._fitInternal(this.cameraForBounds(bounds, options), options, eventData);
    }
    _raycastElevationBox(point0, point1) {
        const elevation = this.transform.elevation;
        if (!elevation)
            return;
        const point2 = new ref_properties.pointGeometry(point0.x, point1.y);
        const point3 = new ref_properties.pointGeometry(point1.x, point0.y);
        const r0 = elevation.pointCoordinate(point0);
        if (!r0)
            return;
        const r1 = elevation.pointCoordinate(point1);
        if (!r1)
            return;
        const r2 = elevation.pointCoordinate(point2);
        if (!r2)
            return;
        const r3 = elevation.pointCoordinate(point3);
        if (!r3)
            return;
        const m0 = new ref_properties.MercatorCoordinate(r0[0], r0[1]).toLngLat();
        const m1 = new ref_properties.MercatorCoordinate(r1[0], r1[1]).toLngLat();
        const m2 = new ref_properties.MercatorCoordinate(r2[0], r2[1]).toLngLat();
        const m3 = new ref_properties.MercatorCoordinate(r3[0], r3[1]).toLngLat();
        const minLng = Math.min(m0.lng, Math.min(m1.lng, Math.min(m2.lng, m3.lng)));
        const minLat = Math.min(m0.lat, Math.min(m1.lat, Math.min(m2.lat, m3.lat)));
        const maxLng = Math.max(m0.lng, Math.max(m1.lng, Math.max(m2.lng, m3.lng)));
        const maxLat = Math.max(m0.lat, Math.max(m1.lat, Math.max(m2.lat, m3.lat)));
        const minAltitude = Math.min(r0[3], Math.min(r1[3], Math.min(r2[3], r3[3])));
        const maxAltitude = Math.max(r0[3], Math.max(r1[3], Math.max(r2[3], r3[3])));
        const minLngLat = new ref_properties.LngLat(minLng, minLat);
        const maxLngLat = new ref_properties.LngLat(maxLng, maxLat);
        return {
            minLngLat,
            maxLngLat,
            minAltitude,
            maxAltitude
        };
    }
    fitScreenCoordinates(p0, p1, bearing, options, eventData) {
        let lngLat0, lngLat1, minAltitude, maxAltitude;
        const point0 = ref_properties.pointGeometry.convert(p0);
        const point1 = ref_properties.pointGeometry.convert(p1);
        const raycast = this._raycastElevationBox(point0, point1);
        if (!raycast) {
            if (this.transform.isHorizonVisibleForPoints(point0, point1)) {
                return this;
            }
            lngLat0 = this.transform.pointLocation(point0);
            lngLat1 = this.transform.pointLocation(point1);
        } else {
            lngLat0 = raycast.minLngLat;
            lngLat1 = raycast.maxLngLat;
            minAltitude = raycast.minAltitude;
            maxAltitude = raycast.maxAltitude;
        }
        if (this.transform.pitch === 0) {
            return this._fitInternal(this._cameraForBoxAndBearing(this.transform.pointLocation(ref_properties.pointGeometry.convert(p0)), this.transform.pointLocation(ref_properties.pointGeometry.convert(p1)), bearing, options), options, eventData);
        }
        return this._fitInternal(this._cameraForBox(lngLat0, lngLat1, minAltitude, maxAltitude, options), options, eventData);
    }
    _fitInternal(calculatedOptions, options, eventData) {
        if (!calculatedOptions)
            return this;
        options = ref_properties.extend(calculatedOptions, options);
        delete options.padding;
        return options.linear ? this.easeTo(options, eventData) : this.flyTo(options, eventData);
    }
    jumpTo(options, eventData) {
        this.stop();
        const tr = this.transform;
        let zoomChanged = false, bearingChanged = false, pitchChanged = false;
        if ('zoom' in options && tr.zoom !== +options.zoom) {
            zoomChanged = true;
            tr.zoom = +options.zoom;
        }
        if (options.center !== undefined) {
            tr.center = ref_properties.LngLat.convert(options.center);
        }
        if ('bearing' in options && tr.bearing !== +options.bearing) {
            bearingChanged = true;
            tr.bearing = +options.bearing;
        }
        if ('pitch' in options && tr.pitch !== +options.pitch) {
            pitchChanged = true;
            tr.pitch = +options.pitch;
        }
        if (options.padding != null && !tr.isPaddingEqual(options.padding)) {
            tr.padding = options.padding;
        }
        this.fire(new ref_properties.Event('movestart', eventData)).fire(new ref_properties.Event('move', eventData));
        if (zoomChanged) {
            this.fire(new ref_properties.Event('zoomstart', eventData)).fire(new ref_properties.Event('zoom', eventData)).fire(new ref_properties.Event('zoomend', eventData));
        }
        if (bearingChanged) {
            this.fire(new ref_properties.Event('rotatestart', eventData)).fire(new ref_properties.Event('rotate', eventData)).fire(new ref_properties.Event('rotateend', eventData));
        }
        if (pitchChanged) {
            this.fire(new ref_properties.Event('pitchstart', eventData)).fire(new ref_properties.Event('pitch', eventData)).fire(new ref_properties.Event('pitchend', eventData));
        }
        return this.fire(new ref_properties.Event('moveend', eventData));
    }
    getFreeCameraOptions() {
        return this.transform.getFreeCameraOptions();
    }
    setFreeCameraOptions(options, eventData) {
        this.stop();
        const tr = this.transform;
        const prevZoom = tr.zoom;
        const prevPitch = tr.pitch;
        const prevBearing = tr.bearing;
        tr.setFreeCameraOptions(options);
        const zoomChanged = prevZoom !== tr.zoom;
        const pitchChanged = prevPitch !== tr.pitch;
        const bearingChanged = prevBearing !== tr.bearing;
        this.fire(new ref_properties.Event('movestart', eventData)).fire(new ref_properties.Event('move', eventData));
        if (zoomChanged) {
            this.fire(new ref_properties.Event('zoomstart', eventData)).fire(new ref_properties.Event('zoom', eventData)).fire(new ref_properties.Event('zoomend', eventData));
        }
        if (bearingChanged) {
            this.fire(new ref_properties.Event('rotatestart', eventData)).fire(new ref_properties.Event('rotate', eventData)).fire(new ref_properties.Event('rotateend', eventData));
        }
        if (pitchChanged) {
            this.fire(new ref_properties.Event('pitchstart', eventData)).fire(new ref_properties.Event('pitch', eventData)).fire(new ref_properties.Event('pitchend', eventData));
        }
        this.fire(new ref_properties.Event('moveend', eventData));
        return this;
    }
    easeTo(options, eventData) {
        this._stop(false, options.easeId);
        options = ref_properties.extend({
            offset: [
                0,
                0
            ],
            duration: 500,
            easing: ref_properties.ease
        }, options);
        if (options.animate === false || !options.essential && ref_properties.exported.prefersReducedMotion)
            options.duration = 0;
        const tr = this.transform, startZoom = this.getZoom(), startBearing = this.getBearing(), startPitch = this.getPitch(), startPadding = this.getPadding(), zoom = 'zoom' in options ? +options.zoom : startZoom, bearing = 'bearing' in options ? this._normalizeBearing(options.bearing, startBearing) : startBearing, pitch = 'pitch' in options ? +options.pitch : startPitch, padding = 'padding' in options ? options.padding : tr.padding;
        const offsetAsPoint = ref_properties.pointGeometry.convert(options.offset);
        let pointAtOffset = tr.centerPoint.add(offsetAsPoint);
        const locationAtOffset = tr.pointLocation(pointAtOffset);
        const center = ref_properties.LngLat.convert(options.center || locationAtOffset);
        this._normalizeCenter(center);
        const from = tr.project(locationAtOffset);
        const delta = tr.project(center).sub(from);
        const finalScale = tr.zoomScale(zoom - startZoom);
        let around, aroundPoint;
        if (options.around) {
            around = ref_properties.LngLat.convert(options.around);
            aroundPoint = tr.locationPoint(around);
        }
        const currently = {
            moving: this._moving,
            zooming: this._zooming,
            rotating: this._rotating,
            pitching: this._pitching
        };
        this._zooming = this._zooming || zoom !== startZoom;
        this._rotating = this._rotating || startBearing !== bearing;
        this._pitching = this._pitching || pitch !== startPitch;
        this._padding = !tr.isPaddingEqual(padding);
        this._easeId = options.easeId;
        this._prepareEase(eventData, options.noMoveStart, currently);
        this._ease(k => {
            if (this._zooming) {
                tr.zoom = ref_properties.number(startZoom, zoom, k);
            }
            if (this._rotating) {
                tr.bearing = ref_properties.number(startBearing, bearing, k);
            }
            if (this._pitching) {
                tr.pitch = ref_properties.number(startPitch, pitch, k);
            }
            if (this._padding) {
                tr.interpolatePadding(startPadding, padding, k);
                pointAtOffset = tr.centerPoint.add(offsetAsPoint);
            }
            if (around) {
                tr.setLocationAtPoint(around, aroundPoint);
            } else {
                const scale = tr.zoomScale(tr.zoom - startZoom);
                const base = zoom > startZoom ? Math.min(2, finalScale) : Math.max(0.5, finalScale);
                const speedup = Math.pow(base, 1 - k);
                const newCenter = tr.unproject(from.add(delta.mult(k * speedup)).mult(scale));
                tr.setLocationAtPoint(tr.renderWorldCopies ? newCenter.wrap() : newCenter, pointAtOffset);
            }
            this._fireMoveEvents(eventData);
        }, interruptingEaseId => {
            tr.recenterOnTerrain();
            this._afterEase(eventData, interruptingEaseId);
        }, options);
        return this;
    }
    _prepareEase(eventData, noMoveStart, currently = {}) {
        this._moving = true;
        this.transform.cameraElevationReference = 'sea';
        if (!noMoveStart && !currently.moving) {
            this.fire(new ref_properties.Event('movestart', eventData));
        }
        if (this._zooming && !currently.zooming) {
            this.fire(new ref_properties.Event('zoomstart', eventData));
        }
        if (this._rotating && !currently.rotating) {
            this.fire(new ref_properties.Event('rotatestart', eventData));
        }
        if (this._pitching && !currently.pitching) {
            this.fire(new ref_properties.Event('pitchstart', eventData));
        }
    }
    _fireMoveEvents(eventData) {
        this.fire(new ref_properties.Event('move', eventData));
        if (this._zooming) {
            this.fire(new ref_properties.Event('zoom', eventData));
        }
        if (this._rotating) {
            this.fire(new ref_properties.Event('rotate', eventData));
        }
        if (this._pitching) {
            this.fire(new ref_properties.Event('pitch', eventData));
        }
    }
    _afterEase(eventData, easeId) {
        if (this._easeId && easeId && this._easeId === easeId) {
            return;
        }
        delete this._easeId;
        this.transform.cameraElevationReference = 'ground';
        const wasZooming = this._zooming;
        const wasRotating = this._rotating;
        const wasPitching = this._pitching;
        this._moving = false;
        this._zooming = false;
        this._rotating = false;
        this._pitching = false;
        this._padding = false;
        if (wasZooming) {
            this.fire(new ref_properties.Event('zoomend', eventData));
        }
        if (wasRotating) {
            this.fire(new ref_properties.Event('rotateend', eventData));
        }
        if (wasPitching) {
            this.fire(new ref_properties.Event('pitchend', eventData));
        }
        this.fire(new ref_properties.Event('moveend', eventData));
    }
    flyTo(options, eventData) {
        if (!options.essential && ref_properties.exported.prefersReducedMotion) {
            const coercedOptions = ref_properties.pick(options, [
                'center',
                'zoom',
                'bearing',
                'pitch',
                'around'
            ]);
            return this.jumpTo(coercedOptions, eventData);
        }
        this.stop();
        options = ref_properties.extend({
            offset: [
                0,
                0
            ],
            speed: 1.2,
            curve: 1.42,
            easing: ref_properties.ease
        }, options);
        const tr = this.transform, startZoom = this.getZoom(), startBearing = this.getBearing(), startPitch = this.getPitch(), startPadding = this.getPadding();
        const zoom = 'zoom' in options ? ref_properties.clamp(+options.zoom, tr.minZoom, tr.maxZoom) : startZoom;
        const bearing = 'bearing' in options ? this._normalizeBearing(options.bearing, startBearing) : startBearing;
        const pitch = 'pitch' in options ? +options.pitch : startPitch;
        const padding = 'padding' in options ? options.padding : tr.padding;
        const scale = tr.zoomScale(zoom - startZoom);
        const offsetAsPoint = ref_properties.pointGeometry.convert(options.offset);
        let pointAtOffset = tr.centerPoint.add(offsetAsPoint);
        const locationAtOffset = tr.pointLocation(pointAtOffset);
        const center = ref_properties.LngLat.convert(options.center || locationAtOffset);
        this._normalizeCenter(center);
        const from = tr.project(locationAtOffset);
        const delta = tr.project(center).sub(from);
        let rho = options.curve;
        const w0 = Math.max(tr.width, tr.height), w1 = w0 / scale, u1 = delta.mag();
        if ('minZoom' in options) {
            const minZoom = ref_properties.clamp(Math.min(options.minZoom, startZoom, zoom), tr.minZoom, tr.maxZoom);
            const wMax = w0 / tr.zoomScale(minZoom - startZoom);
            rho = Math.sqrt(wMax / u1 * 2);
        }
        const rho2 = rho * rho;
        function r(i) {
            const b = (w1 * w1 - w0 * w0 + (i ? -1 : 1) * rho2 * rho2 * u1 * u1) / (2 * (i ? w1 : w0) * rho2 * u1);
            return Math.log(Math.sqrt(b * b + 1) - b);
        }
        function sinh(n) {
            return (Math.exp(n) - Math.exp(-n)) / 2;
        }
        function cosh(n) {
            return (Math.exp(n) + Math.exp(-n)) / 2;
        }
        function tanh(n) {
            return sinh(n) / cosh(n);
        }
        const r0 = r(0);
        let w = function (s) {
            return cosh(r0) / cosh(r0 + rho * s);
        };
        let u = function (s) {
            return w0 * ((cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2) / u1;
        };
        let S = (r(1) - r0) / rho;
        if (Math.abs(u1) < 0.000001 || !isFinite(S)) {
            if (Math.abs(w0 - w1) < 0.000001)
                return this.easeTo(options, eventData);
            const k = w1 < w0 ? -1 : 1;
            S = Math.abs(Math.log(w1 / w0)) / rho;
            u = function () {
                return 0;
            };
            w = function (s) {
                return Math.exp(k * rho * s);
            };
        }
        if ('duration' in options) {
            options.duration = +options.duration;
        } else {
            const V = 'screenSpeed' in options ? +options.screenSpeed / rho : +options.speed;
            options.duration = 1000 * S / V;
        }
        if (options.maxDuration && options.duration > options.maxDuration) {
            options.duration = 0;
        }
        this._zooming = true;
        this._rotating = startBearing !== bearing;
        this._pitching = pitch !== startPitch;
        this._padding = !tr.isPaddingEqual(padding);
        this._prepareEase(eventData, false);
        this._ease(k => {
            const s = k * S;
            const scale = 1 / w(s);
            tr.zoom = k === 1 ? zoom : startZoom + tr.scaleZoom(scale);
            if (this._rotating) {
                tr.bearing = ref_properties.number(startBearing, bearing, k);
            }
            if (this._pitching) {
                tr.pitch = ref_properties.number(startPitch, pitch, k);
            }
            if (this._padding) {
                tr.interpolatePadding(startPadding, padding, k);
                pointAtOffset = tr.centerPoint.add(offsetAsPoint);
            }
            const newCenter = k === 1 ? center : tr.unproject(from.add(delta.mult(u(s))).mult(scale));
            tr.setLocationAtPoint(tr.renderWorldCopies ? newCenter.wrap() : newCenter, pointAtOffset);
            tr._updateCenterElevation();
            this._fireMoveEvents(eventData);
        }, () => this._afterEase(eventData), options);
        return this;
    }
    isEasing() {
        return !!this._easeFrameId;
    }
    stop() {
        return this._stop();
    }
    _stop(allowGestures, easeId) {
        if (this._easeFrameId) {
            this._cancelRenderFrame(this._easeFrameId);
            delete this._easeFrameId;
            delete this._onEaseFrame;
        }
        if (this._onEaseEnd) {
            const onEaseEnd = this._onEaseEnd;
            delete this._onEaseEnd;
            onEaseEnd.call(this, easeId);
        }
        if (!allowGestures) {
            const handlers = this.handlers;
            if (handlers)
                handlers.stop(false);
        }
        return this;
    }
    _ease(frame, finish, options) {
        if (options.animate === false || options.duration === 0) {
            frame(1);
            finish();
        } else {
            this._easeStart = ref_properties.exported.now();
            this._easeOptions = options;
            this._onEaseFrame = frame;
            this._onEaseEnd = finish;
            this._easeFrameId = this._requestRenderFrame(this._renderFrameCallback);
        }
    }
    _renderFrameCallback() {
        const t = Math.min((ref_properties.exported.now() - this._easeStart) / this._easeOptions.duration, 1);
        this._onEaseFrame(this._easeOptions.easing(t));
        if (t < 1) {
            this._easeFrameId = this._requestRenderFrame(this._renderFrameCallback);
        } else {
            this.stop();
        }
    }
    _normalizeBearing(bearing, currentBearing) {
        bearing = ref_properties.wrap(bearing, -180, 180);
        const diff = Math.abs(bearing - currentBearing);
        if (Math.abs(bearing - 360 - currentBearing) < diff)
            bearing -= 360;
        if (Math.abs(bearing + 360 - currentBearing) < diff)
            bearing += 360;
        return bearing;
    }
    _normalizeCenter(center) {
        const tr = this.transform;
        if (!tr.renderWorldCopies || tr.lngRange)
            return;
        const delta = center.lng - tr.center.lng;
        center.lng += delta > 180 ? -360 : delta < -180 ? 360 : 0;
    }
}

class AttributionControl {
    constructor(options = {}) {
        this.options = options;
        ref_properties.bindAll([
            '_toggleAttribution',
            '_updateEditLink',
            '_updateData',
            '_updateCompact'
        ], this);
    }
    getDefaultPosition() {
        return 'bottom-right';
    }
    onAdd(map) {
        const compact = this.options && this.options.compact;
        this._map = map;
        this._container = DOM.create('div', 'mapboxgl-ctrl mapboxgl-ctrl-attrib');
        this._compactButton = DOM.create('button', 'mapboxgl-ctrl-attrib-button', this._container);
        this._compactButton.type = 'button';
        this._compactButton.addEventListener('click', this._toggleAttribution);
        this._setElementTitle(this._compactButton, 'ToggleAttribution');
        this._innerContainer = DOM.create('div', 'mapboxgl-ctrl-attrib-inner', this._container);
        this._innerContainer.setAttribute('role', 'list');
        if (compact) {
            this._container.classList.add('mapboxgl-compact');
        }
        this._updateAttributions();
        this._updateEditLink();
        this._map.on('styledata', this._updateData);
        this._map.on('sourcedata', this._updateData);
        this._map.on('moveend', this._updateEditLink);
        if (compact === undefined) {
            this._map.on('resize', this._updateCompact);
            this._updateCompact();
        }
        return this._container;
    }
    onRemove() {
        DOM.remove(this._container);
        this._map.off('styledata', this._updateData);
        this._map.off('sourcedata', this._updateData);
        this._map.off('moveend', this._updateEditLink);
        this._map.off('resize', this._updateCompact);
        this._map = undefined;
        this._attribHTML = undefined;
    }
    _setElementTitle(element, title) {
        const str = this._map._getUIString(`AttributionControl.${ title }`);
        element.title = str;
        element.setAttribute('aria-label', str);
    }
    _toggleAttribution() {
        if (this._container.classList.contains('mapboxgl-compact-show')) {
            this._container.classList.remove('mapboxgl-compact-show');
            this._compactButton.setAttribute('aria-pressed', 'false');
        } else {
            this._container.classList.add('mapboxgl-compact-show');
            this._compactButton.setAttribute('aria-pressed', 'true');
        }
    }
    _updateEditLink() {
        let editLink = this._editLink;
        if (!editLink) {
            editLink = this._editLink = this._container.querySelector('.mapbox-improve-map');
        }
        const params = [
            {
                key: 'owner',
                value: this.styleOwner
            },
            {
                key: 'id',
                value: this.styleId
            },
            {
                key: 'access_token',
                value: this._map._requestManager._customAccessToken || ref_properties.config.ACCESS_TOKEN
            }
        ];
        if (editLink) {
            const paramString = params.reduce((acc, next, i) => {
                if (next.value) {
                    acc += `${ next.key }=${ next.value }${ i < params.length - 1 ? '&' : '' }`;
                }
                return acc;
            }, `?`);
            editLink.href = `${ ref_properties.config.FEEDBACK_URL }/${ paramString }${ this._map._hash ? this._map._hash.getHashString(true) : '' }`;
            editLink.rel = 'noopener nofollow';
            this._setElementTitle(editLink, 'MapFeedback');
        }
    }
    _updateData(e) {
        if (e && (e.sourceDataType === 'metadata' || e.sourceDataType === 'visibility' || e.dataType === 'style')) {
            this._updateAttributions();
            this._updateEditLink();
        }
    }
    _updateAttributions() {
        if (!this._map.style)
            return;
        let attributions = [];
        if (this.options.customAttribution) {
            if (Array.isArray(this.options.customAttribution)) {
                attributions = attributions.concat(this.options.customAttribution.map(attribution => {
                    if (typeof attribution !== 'string')
                        return '';
                    return attribution;
                }));
            } else if (typeof this.options.customAttribution === 'string') {
                attributions.push(this.options.customAttribution);
            }
        }
        if (this._map.style.stylesheet) {
            const stylesheet = this._map.style.stylesheet;
            this.styleOwner = stylesheet.owner;
            this.styleId = stylesheet.id;
        }
        const sourceCaches = this._map.style._sourceCaches;
        for (const id in sourceCaches) {
            const sourceCache = sourceCaches[id];
            if (sourceCache.used) {
                const source = sourceCache.getSource();
                if (source.attribution && attributions.indexOf(source.attribution) < 0) {
                    attributions.push(source.attribution);
                }
            }
        }
        attributions.sort((a, b) => a.length - b.length);
        attributions = attributions.filter((attrib, i) => {
            for (let j = i + 1; j < attributions.length; j++) {
                if (attributions[j].indexOf(attrib) >= 0) {
                    return false;
                }
            }
            return true;
        });
        const attribHTML = attributions.join(' | ');
        if (attribHTML === this._attribHTML)
            return;
        this._attribHTML = attribHTML;
        if (attributions.length) {
            this._innerContainer.innerHTML = attribHTML;
            this._container.classList.remove('mapboxgl-attrib-empty');
        } else {
            this._container.classList.add('mapboxgl-attrib-empty');
        }
        this._editLink = null;
    }
    _updateCompact() {
        if (this._map.getCanvasContainer().offsetWidth <= 640) {
            this._container.classList.add('mapboxgl-compact');
        } else {
            this._container.classList.remove('mapboxgl-compact', 'mapboxgl-compact-show');
        }
    }
}

class LogoControl {
    constructor() {
        ref_properties.bindAll(['_updateLogo'], this);
        ref_properties.bindAll(['_updateCompact'], this);
    }
    onAdd(map) {
        this._map = map;
        this._container = DOM.create('div', 'mapboxgl-ctrl');
        const anchor = DOM.create('a', 'mapboxgl-ctrl-logo');
        anchor.target = '_blank';
        anchor.rel = 'noopener nofollow';
        anchor.href = 'https://www.mapbox.com/';
        anchor.setAttribute('aria-label', this._map._getUIString('LogoControl.Title'));
        anchor.setAttribute('rel', 'noopener nofollow');
        this._container.appendChild(anchor);
        this._container.style.display = 'none';
        this._map.on('sourcedata', this._updateLogo);
        this._updateLogo();
        this._map.on('resize', this._updateCompact);
        this._updateCompact();
        return this._container;
    }
    onRemove() {
        DOM.remove(this._container);
        this._map.off('sourcedata', this._updateLogo);
        this._map.off('resize', this._updateCompact);
    }
    getDefaultPosition() {
        return 'bottom-left';
    }
    _updateLogo(e) {
        if (!e || e.sourceDataType === 'metadata') {
            this._container.style.display = this._logoRequired() ? 'block' : 'none';
        }
    }
    _logoRequired() {
        if (!this._map.style)
            return true;
        const sourceCaches = this._map.style._sourceCaches;
        if (Object.entries(sourceCaches).length === 0)
            return true;
        for (const id in sourceCaches) {
            const source = sourceCaches[id].getSource();
            if (source.hasOwnProperty('mapbox_logo') && !source.mapbox_logo) {
                return false;
            }
        }
        return true;
    }
    _updateCompact() {
        const containerChildren = this._container.children;
        if (containerChildren.length) {
            const anchor = containerChildren[0];
            if (this._map.getCanvasContainer().offsetWidth < 250) {
                anchor.classList.add('mapboxgl-compact');
            } else {
                anchor.classList.remove('mapboxgl-compact');
            }
        }
    }
}

class TaskQueue {
    constructor() {
        this._queue = [];
        this._id = 0;
        this._cleared = false;
        this._currentlyRunning = false;
    }
    add(callback) {
        const id = ++this._id;
        const queue = this._queue;
        queue.push({
            callback,
            id,
            cancelled: false
        });
        return id;
    }
    remove(id) {
        const running = this._currentlyRunning;
        const queue = running ? this._queue.concat(running) : this._queue;
        for (const task of queue) {
            if (task.id === id) {
                task.cancelled = true;
                return;
            }
        }
    }
    run(timeStamp = 0) {
        const queue = this._currentlyRunning = this._queue;
        this._queue = [];
        for (const task of queue) {
            if (task.cancelled)
                continue;
            task.callback(timeStamp);
            if (this._cleared)
                break;
        }
        this._cleared = false;
        this._currentlyRunning = false;
    }
    clear() {
        if (this._currentlyRunning) {
            this._cleared = true;
        }
        this._queue = [];
    }
}

function smartWrap (lngLat, priorPos, transform) {
    lngLat = new ref_properties.LngLat(lngLat.lng, lngLat.lat);
    if (priorPos) {
        const left = new ref_properties.LngLat(lngLat.lng - 360, lngLat.lat);
        const right = new ref_properties.LngLat(lngLat.lng + 360, lngLat.lat);
        const withinWrap = Math.ceil(Math.abs(lngLat.lng - transform.center.lng) / 360) * 360;
        const delta = transform.locationPoint(lngLat).distSqr(priorPos);
        const offscreen = priorPos.x < 0 || priorPos.y < 0 || priorPos.x > transform.width || priorPos.y > transform.height;
        if (transform.locationPoint(left).distSqr(priorPos) < delta && (offscreen || Math.abs(left.lng - transform.center.lng) < withinWrap)) {
            lngLat = left;
        } else if (transform.locationPoint(right).distSqr(priorPos) < delta && (offscreen || Math.abs(right.lng - transform.center.lng) < withinWrap)) {
            lngLat = right;
        }
    }
    while (Math.abs(lngLat.lng - transform.center.lng) > 180) {
        const pos = transform.locationPoint(lngLat);
        if (pos.x >= 0 && pos.y >= 0 && pos.x <= transform.width && pos.y <= transform.height) {
            break;
        }
        if (lngLat.lng > transform.center.lng) {
            lngLat.lng -= 360;
        } else {
            lngLat.lng += 360;
        }
    }
    return lngLat;
}

const anchorTranslate = {
    'center': 'translate(-50%,-50%)',
    'top': 'translate(-50%,0)',
    'top-left': 'translate(0,0)',
    'top-right': 'translate(-100%,0)',
    'bottom': 'translate(-50%,-100%)',
    'bottom-left': 'translate(0,-100%)',
    'bottom-right': 'translate(-100%,-100%)',
    'left': 'translate(0,-50%)',
    'right': 'translate(-100%,-50%)'
};
function applyAnchorClass(element, anchor, prefix) {
    const classList = element.classList;
    for (const key in anchorTranslate) {
        classList.remove(`mapboxgl-${ prefix }-anchor-${ key }`);
    }
    classList.add(`mapboxgl-${ prefix }-anchor-${ anchor }`);
}

const TERRAIN_OCCLUDED_OPACITY = 0.2;
class Marker extends ref_properties.Evented {
    constructor(options, legacyOptions) {
        super();
        if (options instanceof ref_properties.window.HTMLElement || legacyOptions) {
            options = ref_properties.extend({ element: options }, legacyOptions);
        }
        ref_properties.bindAll([
            '_update',
            '_onMove',
            '_onUp',
            '_addDragHandler',
            '_onMapClick',
            '_onKeyPress',
            '_clearFadeTimer'
        ], this);
        this._anchor = options && options.anchor || 'center';
        this._color = options && options.color || '#3FB1CE';
        this._scale = options && options.scale || 1;
        this._draggable = options && options.draggable || false;
        this._clickTolerance = options && options.clickTolerance || 0;
        this._isDragging = false;
        this._state = 'inactive';
        this._rotation = options && options.rotation || 0;
        this._rotationAlignment = options && options.rotationAlignment || 'auto';
        this._pitchAlignment = options && options.pitchAlignment && options.pitchAlignment !== 'auto' ? options.pitchAlignment : this._rotationAlignment;
        if (!options || !options.element) {
            this._defaultMarker = true;
            this._element = DOM.create('div');
            this._element.setAttribute('aria-label', 'Map marker');
            const svg = DOM.createNS('http://www.w3.org/2000/svg', 'svg');
            const defaultHeight = 41;
            const defaultWidth = 27;
            svg.setAttributeNS(null, 'display', 'block');
            svg.setAttributeNS(null, 'height', `${ defaultHeight }px`);
            svg.setAttributeNS(null, 'width', `${ defaultWidth }px`);
            svg.setAttributeNS(null, 'viewBox', `0 0 ${ defaultWidth } ${ defaultHeight }`);
            const markerLarge = DOM.createNS('http://www.w3.org/2000/svg', 'g');
            markerLarge.setAttributeNS(null, 'stroke', 'none');
            markerLarge.setAttributeNS(null, 'stroke-width', '1');
            markerLarge.setAttributeNS(null, 'fill', 'none');
            markerLarge.setAttributeNS(null, 'fill-rule', 'evenodd');
            const page1 = DOM.createNS('http://www.w3.org/2000/svg', 'g');
            page1.setAttributeNS(null, 'fill-rule', 'nonzero');
            const shadow = DOM.createNS('http://www.w3.org/2000/svg', 'g');
            shadow.setAttributeNS(null, 'transform', 'translate(3.0, 29.0)');
            shadow.setAttributeNS(null, 'fill', '#000000');
            const ellipses = [
                {
                    'rx': '10.5',
                    'ry': '5.25002273'
                },
                {
                    'rx': '10.5',
                    'ry': '5.25002273'
                },
                {
                    'rx': '9.5',
                    'ry': '4.77275007'
                },
                {
                    'rx': '8.5',
                    'ry': '4.29549936'
                },
                {
                    'rx': '7.5',
                    'ry': '3.81822308'
                },
                {
                    'rx': '6.5',
                    'ry': '3.34094679'
                },
                {
                    'rx': '5.5',
                    'ry': '2.86367051'
                },
                {
                    'rx': '4.5',
                    'ry': '2.38636864'
                }
            ];
            for (const data of ellipses) {
                const ellipse = DOM.createNS('http://www.w3.org/2000/svg', 'ellipse');
                ellipse.setAttributeNS(null, 'opacity', '0.04');
                ellipse.setAttributeNS(null, 'cx', '10.5');
                ellipse.setAttributeNS(null, 'cy', '5.80029008');
                ellipse.setAttributeNS(null, 'rx', data['rx']);
                ellipse.setAttributeNS(null, 'ry', data['ry']);
                shadow.appendChild(ellipse);
            }
            const background = DOM.createNS('http://www.w3.org/2000/svg', 'g');
            background.setAttributeNS(null, 'fill', this._color);
            const bgPath = DOM.createNS('http://www.w3.org/2000/svg', 'path');
            bgPath.setAttributeNS(null, 'd', 'M27,13.5 C27,19.074644 20.250001,27.000002 14.75,34.500002 C14.016665,35.500004 12.983335,35.500004 12.25,34.500002 C6.7499993,27.000002 0,19.222562 0,13.5 C0,6.0441559 6.0441559,0 13.5,0 C20.955844,0 27,6.0441559 27,13.5 Z');
            background.appendChild(bgPath);
            const border = DOM.createNS('http://www.w3.org/2000/svg', 'g');
            border.setAttributeNS(null, 'opacity', '0.25');
            border.setAttributeNS(null, 'fill', '#000000');
            const borderPath = DOM.createNS('http://www.w3.org/2000/svg', 'path');
            borderPath.setAttributeNS(null, 'd', 'M13.5,0 C6.0441559,0 0,6.0441559 0,13.5 C0,19.222562 6.7499993,27 12.25,34.5 C13,35.522727 14.016664,35.500004 14.75,34.5 C20.250001,27 27,19.074644 27,13.5 C27,6.0441559 20.955844,0 13.5,0 Z M13.5,1 C20.415404,1 26,6.584596 26,13.5 C26,15.898657 24.495584,19.181431 22.220703,22.738281 C19.945823,26.295132 16.705119,30.142167 13.943359,33.908203 C13.743445,34.180814 13.612715,34.322738 13.5,34.441406 C13.387285,34.322738 13.256555,34.180814 13.056641,33.908203 C10.284481,30.127985 7.4148684,26.314159 5.015625,22.773438 C2.6163816,19.232715 1,15.953538 1,13.5 C1,6.584596 6.584596,1 13.5,1 Z');
            border.appendChild(borderPath);
            const maki = DOM.createNS('http://www.w3.org/2000/svg', 'g');
            maki.setAttributeNS(null, 'transform', 'translate(6.0, 7.0)');
            maki.setAttributeNS(null, 'fill', '#FFFFFF');
            const circleContainer = DOM.createNS('http://www.w3.org/2000/svg', 'g');
            circleContainer.setAttributeNS(null, 'transform', 'translate(8.0, 8.0)');
            const circle1 = DOM.createNS('http://www.w3.org/2000/svg', 'circle');
            circle1.setAttributeNS(null, 'fill', '#000000');
            circle1.setAttributeNS(null, 'opacity', '0.25');
            circle1.setAttributeNS(null, 'cx', '5.5');
            circle1.setAttributeNS(null, 'cy', '5.5');
            circle1.setAttributeNS(null, 'r', '5.4999962');
            const circle2 = DOM.createNS('http://www.w3.org/2000/svg', 'circle');
            circle2.setAttributeNS(null, 'fill', '#FFFFFF');
            circle2.setAttributeNS(null, 'cx', '5.5');
            circle2.setAttributeNS(null, 'cy', '5.5');
            circle2.setAttributeNS(null, 'r', '5.4999962');
            circleContainer.appendChild(circle1);
            circleContainer.appendChild(circle2);
            page1.appendChild(shadow);
            page1.appendChild(background);
            page1.appendChild(border);
            page1.appendChild(maki);
            page1.appendChild(circleContainer);
            svg.appendChild(page1);
            svg.setAttributeNS(null, 'height', `${ defaultHeight * this._scale }px`);
            svg.setAttributeNS(null, 'width', `${ defaultWidth * this._scale }px`);
            this._element.appendChild(svg);
            this._offset = ref_properties.pointGeometry.convert(options && options.offset || [
                0,
                -14
            ]);
        } else {
            this._element = options.element;
            this._offset = ref_properties.pointGeometry.convert(options && options.offset || [
                0,
                0
            ]);
        }
        this._element.classList.add('mapboxgl-marker');
        this._element.addEventListener('dragstart', e => {
            e.preventDefault();
        });
        this._element.addEventListener('mousedown', e => {
            e.preventDefault();
        });
        applyAnchorClass(this._element, this._anchor, 'marker');
        this._popup = null;
    }
    addTo(map) {
        this.remove();
        this._map = map;
        map.getCanvasContainer().appendChild(this._element);
        map.on('move', this._update);
        map.on('moveend', this._update);
        map.on('remove', this._clearFadeTimer);
        map._addMarker(this);
        this.setDraggable(this._draggable);
        this._update();
        this._map.on('click', this._onMapClick);
        return this;
    }
    remove() {
        if (this._map) {
            this._map.off('click', this._onMapClick);
            this._map.off('move', this._update);
            this._map.off('moveend', this._update);
            this._map.off('mousedown', this._addDragHandler);
            this._map.off('touchstart', this._addDragHandler);
            this._map.off('mouseup', this._onUp);
            this._map.off('touchend', this._onUp);
            this._map.off('mousemove', this._onMove);
            this._map.off('touchmove', this._onMove);
            this._map.off('remove', this._clearFadeTimer);
            this._map._removeMarker(this);
            delete this._map;
        }
        this._clearFadeTimer();
        DOM.remove(this._element);
        if (this._popup)
            this._popup.remove();
        return this;
    }
    getLngLat() {
        return this._lngLat;
    }
    setLngLat(lnglat) {
        this._lngLat = ref_properties.LngLat.convert(lnglat);
        this._pos = null;
        if (this._popup)
            this._popup.setLngLat(this._lngLat);
        this._update();
        return this;
    }
    getElement() {
        return this._element;
    }
    setPopup(popup) {
        if (this._popup) {
            this._popup.remove();
            this._popup = null;
            this._element.removeEventListener('keypress', this._onKeyPress);
            if (!this._originalTabIndex) {
                this._element.removeAttribute('tabindex');
            }
        }
        if (popup) {
            if (!('offset' in popup.options)) {
                const markerHeight = 41 - 5.8 / 2;
                const markerRadius = 13.5;
                const linearOffset = Math.sqrt(Math.pow(markerRadius, 2) / 2);
                popup.options.offset = this._defaultMarker ? {
                    'top': [
                        0,
                        0
                    ],
                    'top-left': [
                        0,
                        0
                    ],
                    'top-right': [
                        0,
                        0
                    ],
                    'bottom': [
                        0,
                        -markerHeight
                    ],
                    'bottom-left': [
                        linearOffset,
                        (markerHeight - markerRadius + linearOffset) * -1
                    ],
                    'bottom-right': [
                        -linearOffset,
                        (markerHeight - markerRadius + linearOffset) * -1
                    ],
                    'left': [
                        markerRadius,
                        (markerHeight - markerRadius) * -1
                    ],
                    'right': [
                        -markerRadius,
                        (markerHeight - markerRadius) * -1
                    ]
                } : this._offset;
            }
            this._popup = popup;
            if (this._lngLat)
                this._popup.setLngLat(this._lngLat);
            this._originalTabIndex = this._element.getAttribute('tabindex');
            if (!this._originalTabIndex) {
                this._element.setAttribute('tabindex', '0');
            }
            this._element.addEventListener('keypress', this._onKeyPress);
        }
        return this;
    }
    _onKeyPress(e) {
        const code = e.code;
        const legacyCode = e.charCode || e.keyCode;
        if (code === 'Space' || code === 'Enter' || legacyCode === 32 || legacyCode === 13) {
            this.togglePopup();
        }
    }
    _onMapClick(e) {
        const targetElement = e.originalEvent.target;
        const element = this._element;
        if (this._popup && (targetElement === element || element.contains(targetElement))) {
            this.togglePopup();
        }
    }
    getPopup() {
        return this._popup;
    }
    togglePopup() {
        const popup = this._popup;
        if (!popup)
            return this;
        else if (popup.isOpen())
            popup.remove();
        else
            popup.addTo(this._map);
        return this;
    }
    _evaluateOpacity() {
        const position = this._pos ? this._pos.sub(this._transformedOffset()) : null;
        if (!this._withinScreenBounds(position)) {
            this._clearFadeTimer();
            return;
        }
        const mapLocation = this._map.unproject(position);
        let terrainOccluded = false;
        if (this._map.getTerrain()) {
            const camera = this._map.getFreeCameraOptions();
            if (camera.position) {
                const cameraPos = camera.position.toLngLat();
                const offsetDistance = cameraPos.distanceTo(mapLocation);
                const distance = cameraPos.distanceTo(this._lngLat);
                terrainOccluded = offsetDistance < distance * 0.9;
            }
        }
        const fogOpacity = this._map._queryFogOpacity(mapLocation);
        const opacity = (1 - fogOpacity) * (terrainOccluded ? TERRAIN_OCCLUDED_OPACITY : 1);
        this._element.style.opacity = `${ opacity }`;
        if (this._popup)
            this._popup._setOpacity(`${ opacity }`);
        this._fadeTimer = null;
    }
    _clearFadeTimer() {
        if (this._fadeTimer) {
            clearTimeout(this._fadeTimer);
            this._fadeTimer = null;
        }
    }
    _withinScreenBounds(position) {
        const tr = this._map.transform;
        return !!position && position.x >= 0 && position.x < tr.width && position.y >= 0 && position.y < tr.height;
    }
    _update(e) {
        if (!this._map)
            return;
        if (this._map.transform.renderWorldCopies) {
            this._lngLat = smartWrap(this._lngLat, this._pos, this._map.transform);
        }
        this._pos = this._map.project(this._lngLat)._add(this._transformedOffset());
        let rotation = '';
        if (this._rotationAlignment === 'viewport' || this._rotationAlignment === 'auto') {
            rotation = `rotateZ(${ this._rotation }deg)`;
        } else if (this._rotationAlignment === 'map') {
            rotation = `rotateZ(${ this._rotation - this._map.getBearing() }deg)`;
        }
        let pitch = '';
        if (this._pitchAlignment === 'viewport' || this._pitchAlignment === 'auto') {
            pitch = 'rotateX(0deg)';
        } else if (this._pitchAlignment === 'map') {
            pitch = `rotateX(${ this._map.getPitch() }deg)`;
        }
        if (!e || e.type === 'moveend') {
            this._pos = this._pos.round();
        }
        this._map._requestDomTask(() => {
            if (!this._map)
                return;
            if (this._element && this._pos && this._anchor) {
                DOM.setTransform(this._element, `${ anchorTranslate[this._anchor] } translate(${ this._pos.x }px, ${ this._pos.y }px) ${ pitch } ${ rotation }`);
            }
            if ((this._map.getTerrain() || this._map.getFog()) && !this._fadeTimer) {
                this._fadeTimer = setTimeout(this._evaluateOpacity.bind(this), 60);
            }
        });
    }
    _transformedOffset() {
        if (!this._defaultMarker)
            return this._offset;
        const tr = this._map.transform;
        const offset = this._offset.mult(this._scale);
        if (this._rotationAlignment === 'map')
            offset._rotate(tr.angle);
        if (this._pitchAlignment === 'map')
            offset.y *= Math.cos(tr._pitch);
        return offset;
    }
    getOffset() {
        return this._offset;
    }
    setOffset(offset) {
        this._offset = ref_properties.pointGeometry.convert(offset);
        this._update();
        return this;
    }
    _onMove(e) {
        if (!this._isDragging) {
            const clickTolerance = this._clickTolerance || this._map._clickTolerance;
            this._isDragging = e.point.dist(this._pointerdownPos) >= clickTolerance;
        }
        if (!this._isDragging)
            return;
        this._pos = e.point.sub(this._positionDelta);
        this._lngLat = this._map.unproject(this._pos);
        this.setLngLat(this._lngLat);
        this._element.style.pointerEvents = 'none';
        if (this._state === 'pending') {
            this._state = 'active';
            this.fire(new ref_properties.Event('dragstart'));
        }
        this.fire(new ref_properties.Event('drag'));
    }
    _onUp() {
        this._element.style.pointerEvents = 'auto';
        this._positionDelta = null;
        this._pointerdownPos = null;
        this._isDragging = false;
        this._map.off('mousemove', this._onMove);
        this._map.off('touchmove', this._onMove);
        if (this._state === 'active') {
            this.fire(new ref_properties.Event('dragend'));
        }
        this._state = 'inactive';
    }
    _addDragHandler(e) {
        if (this._element.contains(e.originalEvent.target)) {
            e.preventDefault();
            this._positionDelta = e.point.sub(this._pos).add(this._transformedOffset());
            this._pointerdownPos = e.point;
            this._state = 'pending';
            this._map.on('mousemove', this._onMove);
            this._map.on('touchmove', this._onMove);
            this._map.once('mouseup', this._onUp);
            this._map.once('touchend', this._onUp);
        }
    }
    setDraggable(shouldBeDraggable) {
        this._draggable = !!shouldBeDraggable;
        if (this._map) {
            if (shouldBeDraggable) {
                this._map.on('mousedown', this._addDragHandler);
                this._map.on('touchstart', this._addDragHandler);
            } else {
                this._map.off('mousedown', this._addDragHandler);
                this._map.off('touchstart', this._addDragHandler);
            }
        }
        return this;
    }
    isDraggable() {
        return this._draggable;
    }
    setRotation(rotation) {
        this._rotation = rotation || 0;
        this._update();
        return this;
    }
    getRotation() {
        return this._rotation;
    }
    setRotationAlignment(alignment) {
        this._rotationAlignment = alignment || 'auto';
        this._update();
        return this;
    }
    getRotationAlignment() {
        return this._rotationAlignment;
    }
    setPitchAlignment(alignment) {
        this._pitchAlignment = alignment && alignment !== 'auto' ? alignment : this._rotationAlignment;
        this._update();
        return this;
    }
    getPitchAlignment() {
        return this._pitchAlignment;
    }
}

class EasedVariable {
    constructor(initialValue) {
        this.jumpTo(initialValue);
    }
    getValue(timeStamp) {
        if (timeStamp <= this._startTime)
            return this._start;
        if (timeStamp >= this._endTime)
            return this._end;
        const t = ref_properties.easeCubicInOut((timeStamp - this._startTime) / (this._endTime - this._startTime));
        return this._start * (1 - t) + this._end * t;
    }
    isEasing(timeStamp) {
        return timeStamp >= this._startTime && timeStamp <= this._endTime;
    }
    jumpTo(value) {
        this._startTime = -Infinity;
        this._endTime = -Infinity;
        this._start = value;
        this._end = value;
    }
    easeTo(value, timeStamp, duration) {
        this._start = this.getValue(timeStamp);
        this._end = value;
        this._startTime = timeStamp;
        this._endTime = timeStamp + duration;
    }
}

const defaultLocale = {
    'AttributionControl.ToggleAttribution': 'Toggle attribution',
    'AttributionControl.MapFeedback': 'Map feedback',
    'FullscreenControl.Enter': 'Enter fullscreen',
    'FullscreenControl.Exit': 'Exit fullscreen',
    'GeolocateControl.FindMyLocation': 'Find my location',
    'GeolocateControl.LocationNotAvailable': 'Location not available',
    'LogoControl.Title': 'Mapbox logo',
    'NavigationControl.ResetBearing': 'Reset bearing to north',
    'NavigationControl.ZoomIn': 'Zoom in',
    'NavigationControl.ZoomOut': 'Zoom out',
    'ScaleControl.Feet': 'ft',
    'ScaleControl.Meters': 'm',
    'ScaleControl.Kilometers': 'km',
    'ScaleControl.Miles': 'mi',
    'ScaleControl.NauticalMiles': 'nm'
};

const {HTMLImageElement, HTMLElement, ImageBitmap} = ref_properties.window;
const AVERAGE_ELEVATION_SAMPLING_INTERVAL = 500;
const AVERAGE_ELEVATION_EASE_TIME = 300;
const AVERAGE_ELEVATION_EASE_THRESHOLD = 1;
const AVERAGE_ELEVATION_CHANGE_THRESHOLD = 0.0001;
const defaultMinZoom = -2;
const defaultMaxZoom = 22;
const defaultMinPitch = 0;
const defaultMaxPitch = 85;
const defaultOptions$4 = {
    center: [
        0,
        0
    ],
    zoom: 0,
    bearing: 0,
    pitch: 0,
    minZoom: defaultMinZoom,
    maxZoom: defaultMaxZoom,
    minPitch: defaultMinPitch,
    maxPitch: defaultMaxPitch,
    interactive: true,
    scrollZoom: true,
    boxZoom: true,
    dragRotate: true,
    dragPan: true,
    keyboard: true,
    doubleClickZoom: true,
    touchZoomRotate: true,
    touchPitch: true,
    bearingSnap: 7,
    clickTolerance: 3,
    pitchWithRotate: true,
    hash: false,
    attributionControl: true,
    failIfMajorPerformanceCaveat: false,
    preserveDrawingBuffer: false,
    trackResize: true,
    optimizeForTerrain: true,
    renderWorldCopies: true,
    refreshExpiredTiles: true,
    maxTileCacheSize: null,
    localIdeographFontFamily: 'sans-serif',
    localFontFamily: null,
    transformRequest: null,
    accessToken: null,
    fadeDuration: 300,
    crossSourceCollisions: true
};
class Map extends Camera {
    constructor(options) {
        options = ref_properties.extend({}, defaultOptions$4, options);
        if (options.minZoom != null && options.maxZoom != null && options.minZoom > options.maxZoom) {
            throw new Error(`maxZoom must be greater than or equal to minZoom`);
        }
        if (options.minPitch != null && options.maxPitch != null && options.minPitch > options.maxPitch) {
            throw new Error(`maxPitch must be greater than or equal to minPitch`);
        }
        if (options.minPitch != null && options.minPitch < defaultMinPitch) {
            throw new Error(`minPitch must be greater than or equal to ${ defaultMinPitch }`);
        }
        if (options.maxPitch != null && options.maxPitch > defaultMaxPitch) {
            throw new Error(`maxPitch must be less than or equal to ${ defaultMaxPitch }`);
        }
        const transform = new Transform(options.minZoom, options.maxZoom, options.minPitch, options.maxPitch, options.renderWorldCopies);
        super(transform, options);
        this._interactive = options.interactive;
        this._maxTileCacheSize = options.maxTileCacheSize;
        this._failIfMajorPerformanceCaveat = options.failIfMajorPerformanceCaveat;
        this._preserveDrawingBuffer = options.preserveDrawingBuffer;
        this._antialias = options.antialias;
        this._trackResize = options.trackResize;
        this._bearingSnap = options.bearingSnap;
        this._refreshExpiredTiles = options.refreshExpiredTiles;
        this._fadeDuration = options.fadeDuration;
        this._isInitialLoad = true;
        this._crossSourceCollisions = options.crossSourceCollisions;
        this._crossFadingFactor = 1;
        this._collectResourceTiming = options.collectResourceTiming;
        this._optimizeForTerrain = options.optimizeForTerrain;
        this._renderTaskQueue = new TaskQueue();
        this._domRenderTaskQueue = new TaskQueue();
        this._controls = [];
        this._markers = [];
        this._mapId = ref_properties.uniqueId();
        this._locale = ref_properties.extend({}, defaultLocale, options.locale);
        this._clickTolerance = options.clickTolerance;
        this._averageElevationLastSampledAt = -Infinity;
        this._averageElevation = new EasedVariable(0);
        this._requestManager = new ref_properties.RequestManager(options.transformRequest, options.accessToken, options.testMode);
        this._silenceAuthErrors = !!options.testMode;
        if (typeof options.container === 'string') {
            this._container = ref_properties.window.document.getElementById(options.container);
            if (!this._container) {
                throw new Error(`Container '${ options.container }' not found.`);
            }
        } else if (options.container instanceof HTMLElement) {
            this._container = options.container;
        } else {
            throw new Error(`Invalid type: 'container' must be a String or HTMLElement.`);
        }
        if (options.maxBounds) {
            this.setMaxBounds(options.maxBounds);
        }
        ref_properties.bindAll([
            '_onWindowOnline',
            '_onWindowResize',
            '_onMapScroll',
            '_contextLost',
            '_contextRestored'
        ], this);
        this._setupContainer();
        this._setupPainter();
        if (this.painter === undefined) {
            throw new Error(`Failed to initialize WebGL.`);
        }
        this.on('move', () => this._update(false));
        this.on('moveend', () => this._update(false));
        this.on('zoom', () => this._update(true));
        if (typeof ref_properties.window !== 'undefined') {
            ref_properties.window.addEventListener('online', this._onWindowOnline, false);
            ref_properties.window.addEventListener('resize', this._onWindowResize, false);
            ref_properties.window.addEventListener('orientationchange', this._onWindowResize, false);
        }
        this.handlers = new HandlerManager(this, options);
        const hashName = typeof options.hash === 'string' && options.hash || undefined;
        this._hash = options.hash && new Hash(hashName).addTo(this);
        if (!this._hash || !this._hash._onHashChange()) {
            this.jumpTo({
                center: options.center,
                zoom: options.zoom,
                bearing: options.bearing,
                pitch: options.pitch
            });
            if (options.bounds) {
                this.resize();
                this.fitBounds(options.bounds, ref_properties.extend({}, options.fitBoundsOptions, { duration: 0 }));
            }
        }
        this.resize();
        this._localFontFamily = options.localFontFamily;
        this._localIdeographFontFamily = options.localIdeographFontFamily;
        if (options.style)
            this.setStyle(options.style, {
                localFontFamily: this._localFontFamily,
                localIdeographFontFamily: this._localIdeographFontFamily
            });
        if (options.attributionControl)
            this.addControl(new AttributionControl({ customAttribution: options.customAttribution }));
        this._logoControl = new LogoControl();
        this.addControl(this._logoControl, options.logoPosition);
        this.on('style.load', () => {
            if (this.transform.unmodified) {
                this.jumpTo(this.style.stylesheet);
            }
        });
        this.on('data', event => {
            this._update(event.dataType === 'style');
            this.fire(new ref_properties.Event(`${ event.dataType }data`, event));
        });
        this.on('dataloading', event => {
            this.fire(new ref_properties.Event(`${ event.dataType }dataloading`, event));
        });
    }
    _getMapId() {
        return this._mapId;
    }
    addControl(control, position) {
        if (position === undefined) {
            if (control.getDefaultPosition) {
                position = control.getDefaultPosition();
            } else {
                position = 'top-right';
            }
        }
        if (!control || !control.onAdd) {
            return this.fire(new ref_properties.ErrorEvent(new Error('Invalid argument to map.addControl(). Argument must be a control with onAdd and onRemove methods.')));
        }
        const controlElement = control.onAdd(this);
        this._controls.push(control);
        const positionContainer = this._controlPositions[position];
        if (position.indexOf('bottom') !== -1) {
            positionContainer.insertBefore(controlElement, positionContainer.firstChild);
        } else {
            positionContainer.appendChild(controlElement);
        }
        return this;
    }
    removeControl(control) {
        if (!control || !control.onRemove) {
            return this.fire(new ref_properties.ErrorEvent(new Error('Invalid argument to map.removeControl(). Argument must be a control with onAdd and onRemove methods.')));
        }
        const ci = this._controls.indexOf(control);
        if (ci > -1)
            this._controls.splice(ci, 1);
        control.onRemove(this);
        return this;
    }
    hasControl(control) {
        return this._controls.indexOf(control) > -1;
    }
    resize(eventData) {
        const dimensions = this._containerDimensions();
        const width = dimensions[0];
        const height = dimensions[1];
        this._resizeCanvas(width, height);
        this.transform.resize(width, height);
        this.painter.resize(width, height);
        const fireMoving = !this._moving;
        if (fireMoving) {
            this.stop();
            this.fire(new ref_properties.Event('movestart', eventData)).fire(new ref_properties.Event('move', eventData));
        }
        this.fire(new ref_properties.Event('resize', eventData));
        if (fireMoving)
            this.fire(new ref_properties.Event('moveend', eventData));
        return this;
    }
    getBounds() {
        return this.transform.getBounds();
    }
    getMaxBounds() {
        return this.transform.getMaxBounds();
    }
    setMaxBounds(bounds) {
        this.transform.setMaxBounds(ref_properties.LngLatBounds.convert(bounds));
        return this._update();
    }
    setMinZoom(minZoom) {
        minZoom = minZoom === null || minZoom === undefined ? defaultMinZoom : minZoom;
        if (minZoom >= defaultMinZoom && minZoom <= this.transform.maxZoom) {
            this.transform.minZoom = minZoom;
            this._update();
            if (this.getZoom() < minZoom)
                this.setZoom(minZoom);
            return this;
        } else
            throw new Error(`minZoom must be between ${ defaultMinZoom } and the current maxZoom, inclusive`);
    }
    getMinZoom() {
        return this.transform.minZoom;
    }
    setMaxZoom(maxZoom) {
        maxZoom = maxZoom === null || maxZoom === undefined ? defaultMaxZoom : maxZoom;
        if (maxZoom >= this.transform.minZoom) {
            this.transform.maxZoom = maxZoom;
            this._update();
            if (this.getZoom() > maxZoom)
                this.setZoom(maxZoom);
            return this;
        } else
            throw new Error(`maxZoom must be greater than the current minZoom`);
    }
    getMaxZoom() {
        return this.transform.maxZoom;
    }
    setMinPitch(minPitch) {
        minPitch = minPitch === null || minPitch === undefined ? defaultMinPitch : minPitch;
        if (minPitch < defaultMinPitch) {
            throw new Error(`minPitch must be greater than or equal to ${ defaultMinPitch }`);
        }
        if (minPitch >= defaultMinPitch && minPitch <= this.transform.maxPitch) {
            this.transform.minPitch = minPitch;
            this._update();
            if (this.getPitch() < minPitch)
                this.setPitch(minPitch);
            return this;
        } else
            throw new Error(`minPitch must be between ${ defaultMinPitch } and the current maxPitch, inclusive`);
    }
    getMinPitch() {
        return this.transform.minPitch;
    }
    setMaxPitch(maxPitch) {
        maxPitch = maxPitch === null || maxPitch === undefined ? defaultMaxPitch : maxPitch;
        if (maxPitch > defaultMaxPitch) {
            throw new Error(`maxPitch must be less than or equal to ${ defaultMaxPitch }`);
        }
        if (maxPitch >= this.transform.minPitch) {
            this.transform.maxPitch = maxPitch;
            this._update();
            if (this.getPitch() > maxPitch)
                this.setPitch(maxPitch);
            return this;
        } else
            throw new Error(`maxPitch must be greater than the current minPitch`);
    }
    getMaxPitch() {
        return this.transform.maxPitch;
    }
    getRenderWorldCopies() {
        return this.transform.renderWorldCopies;
    }
    setRenderWorldCopies(renderWorldCopies) {
        this.transform.renderWorldCopies = renderWorldCopies;
        return this._update();
    }
    project(lnglat) {
        return this.transform.locationPoint3D(ref_properties.LngLat.convert(lnglat));
    }
    unproject(point) {
        return this.transform.pointLocation3D(ref_properties.pointGeometry.convert(point));
    }
    isMoving() {
        return this._moving || this.handlers && this.handlers.isMoving();
    }
    isZooming() {
        return this._zooming || this.handlers && this.handlers.isZooming();
    }
    isRotating() {
        return this._rotating || this.handlers && this.handlers.isRotating();
    }
    _createDelegatedListener(type, layerId, listener) {
        if (type === 'mouseenter' || type === 'mouseover') {
            let mousein = false;
            const mousemove = e => {
                const features = this.getLayer(layerId) ? this.queryRenderedFeatures(e.point, { layers: [layerId] }) : [];
                if (!features.length) {
                    mousein = false;
                } else if (!mousein) {
                    mousein = true;
                    listener.call(this, new MapMouseEvent(type, this, e.originalEvent, { features }));
                }
            };
            const mouseout = () => {
                mousein = false;
            };
            return {
                layer: layerId,
                listener,
                delegates: {
                    mousemove,
                    mouseout
                }
            };
        } else if (type === 'mouseleave' || type === 'mouseout') {
            let mousein = false;
            const mousemove = e => {
                const features = this.getLayer(layerId) ? this.queryRenderedFeatures(e.point, { layers: [layerId] }) : [];
                if (features.length) {
                    mousein = true;
                } else if (mousein) {
                    mousein = false;
                    listener.call(this, new MapMouseEvent(type, this, e.originalEvent));
                }
            };
            const mouseout = e => {
                if (mousein) {
                    mousein = false;
                    listener.call(this, new MapMouseEvent(type, this, e.originalEvent));
                }
            };
            return {
                layer: layerId,
                listener,
                delegates: {
                    mousemove,
                    mouseout
                }
            };
        } else {
            const delegate = e => {
                const features = this.getLayer(layerId) ? this.queryRenderedFeatures(e.point, { layers: [layerId] }) : [];
                if (features.length) {
                    e.features = features;
                    listener.call(this, e);
                    delete e.features;
                }
            };
            return {
                layer: layerId,
                listener,
                delegates: { [type]: delegate }
            };
        }
    }
    on(type, layerId, listener) {
        if (listener === undefined) {
            return super.on(type, layerId);
        }
        const delegatedListener = this._createDelegatedListener(type, layerId, listener);
        this._delegatedListeners = this._delegatedListeners || {};
        this._delegatedListeners[type] = this._delegatedListeners[type] || [];
        this._delegatedListeners[type].push(delegatedListener);
        for (const event in delegatedListener.delegates) {
            this.on(event, delegatedListener.delegates[event]);
        }
        return this;
    }
    once(type, layerId, listener) {
        if (listener === undefined) {
            return super.once(type, layerId);
        }
        const delegatedListener = this._createDelegatedListener(type, layerId, listener);
        for (const event in delegatedListener.delegates) {
            this.once(event, delegatedListener.delegates[event]);
        }
        return this;
    }
    off(type, layerId, listener) {
        if (listener === undefined) {
            return super.off(type, layerId);
        }
        const removeDelegatedListener = delegatedListeners => {
            const listeners = delegatedListeners[type];
            for (let i = 0; i < listeners.length; i++) {
                const delegatedListener = listeners[i];
                if (delegatedListener.layer === layerId && delegatedListener.listener === listener) {
                    for (const event in delegatedListener.delegates) {
                        this.off(event, delegatedListener.delegates[event]);
                    }
                    listeners.splice(i, 1);
                    return this;
                }
            }
        };
        if (this._delegatedListeners && this._delegatedListeners[type]) {
            removeDelegatedListener(this._delegatedListeners);
        }
        return this;
    }
    queryRenderedFeatures(geometry, options) {
        if (!this.style) {
            return [];
        }
        if (options === undefined && geometry !== undefined && !(geometry instanceof ref_properties.pointGeometry) && !Array.isArray(geometry)) {
            options = geometry;
            geometry = undefined;
        }
        options = options || {};
        geometry = geometry || [
            [
                0,
                0
            ],
            [
                this.transform.width,
                this.transform.height
            ]
        ];
        return this.style.queryRenderedFeatures(geometry, options, this.transform);
    }
    querySourceFeatures(sourceId, parameters) {
        return this.style.querySourceFeatures(sourceId, parameters);
    }
    setStyle(style, options) {
        options = ref_properties.extend({}, {
            localIdeographFontFamily: this._localIdeographFontFamily,
            localFontFamily: this._localFontFamily
        }, options);
        if (options.diff !== false && options.localIdeographFontFamily === this._localIdeographFontFamily && options.localFontFamily === this._localFontFamily && this.style && style) {
            this._diffStyle(style, options);
            return this;
        } else {
            this._localIdeographFontFamily = options.localIdeographFontFamily;
            this._localFontFamily = options.localFontFamily;
            return this._updateStyle(style, options);
        }
    }
    _getUIString(key) {
        const str = this._locale[key];
        if (str == null) {
            throw new Error(`Missing UI string '${ key }'`);
        }
        return str;
    }
    _updateStyle(style, options) {
        if (this.style) {
            this.style.setEventedParent(null);
            this.style._remove();
            delete this.style;
        }
        if (style) {
            this.style = new Style(this, options || {});
            this.style.setEventedParent(this, { style: this.style });
            if (typeof style === 'string') {
                this.style.loadURL(style);
            } else {
                this.style.loadJSON(style);
            }
        }
        this._updateTerrain();
        return this;
    }
    _lazyInitEmptyStyle() {
        if (!this.style) {
            this.style = new Style(this, {});
            this.style.setEventedParent(this, { style: this.style });
            this.style.loadEmpty();
        }
    }
    _diffStyle(style, options) {
        if (typeof style === 'string') {
            const url = this._requestManager.normalizeStyleURL(style);
            const request = this._requestManager.transformRequest(url, ref_properties.ResourceType.Style);
            ref_properties.getJSON(request, (error, json) => {
                if (error) {
                    this.fire(new ref_properties.ErrorEvent(error));
                } else if (json) {
                    this._updateDiff(json, options);
                }
            });
        } else if (typeof style === 'object') {
            this._updateDiff(style, options);
        }
    }
    _updateDiff(style, options) {
        try {
            if (this.style.setState(style)) {
                this._update(true);
            }
        } catch (e) {
            ref_properties.warnOnce(`Unable to perform style diff: ${ e.message || e.error || e }.  Rebuilding the style from scratch.`);
            this._updateStyle(style, options);
        }
    }
    getStyle() {
        if (this.style) {
            return this.style.serialize();
        }
    }
    isStyleLoaded() {
        if (!this.style)
            return ref_properties.warnOnce('There is no style added to the map.');
        return this.style.loaded();
    }
    addSource(id, source) {
        this._lazyInitEmptyStyle();
        this.style.addSource(id, source);
        return this._update(true);
    }
    isSourceLoaded(id) {
        const sourceCaches = this.style && this.style._getSourceCaches(id);
        if (sourceCaches.length === 0) {
            this.fire(new ref_properties.ErrorEvent(new Error(`There is no source with ID '${ id }'`)));
            return;
        }
        return sourceCaches.every(sc => sc.loaded());
    }
    areTilesLoaded() {
        const sources = this.style && this.style._sourceCaches;
        for (const id in sources) {
            const source = sources[id];
            const tiles = source._tiles;
            for (const t in tiles) {
                const tile = tiles[t];
                if (!(tile.state === 'loaded' || tile.state === 'errored'))
                    return false;
            }
        }
        return true;
    }
    addSourceType(name, SourceType, callback) {
        this._lazyInitEmptyStyle();
        return this.style.addSourceType(name, SourceType, callback);
    }
    removeSource(id) {
        this.style.removeSource(id);
        this._updateTerrain();
        return this._update(true);
    }
    getSource(id) {
        return this.style.getSource(id);
    }
    addImage(id, image, {pixelRatio = 1, sdf = false, stretchX, stretchY, content} = {}) {
        this._lazyInitEmptyStyle();
        const version = 0;
        if (image instanceof HTMLImageElement || ImageBitmap && image instanceof ImageBitmap) {
            const {width, height, data} = ref_properties.exported.getImageData(image);
            this.style.addImage(id, {
                data: new ref_properties.RGBAImage({
                    width,
                    height
                }, data),
                pixelRatio,
                stretchX,
                stretchY,
                content,
                sdf,
                version
            });
        } else if (image.width === undefined || image.height === undefined) {
            return this.fire(new ref_properties.ErrorEvent(new Error('Invalid arguments to map.addImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, ' + 'or object with `width`, `height`, and `data` properties with the same format as `ImageData`')));
        } else {
            const {width, height, data} = image;
            const userImage = image;
            this.style.addImage(id, {
                data: new ref_properties.RGBAImage({
                    width,
                    height
                }, new Uint8Array(data)),
                pixelRatio,
                stretchX,
                stretchY,
                content,
                sdf,
                version,
                userImage
            });
            if (userImage.onAdd) {
                userImage.onAdd(this, id);
            }
        }
    }
    updateImage(id, image) {
        const existingImage = this.style.getImage(id);
        if (!existingImage) {
            return this.fire(new ref_properties.ErrorEvent(new Error('The map has no image with that id. If you are adding a new image use `map.addImage(...)` instead.')));
        }
        const imageData = image instanceof HTMLImageElement || ImageBitmap && image instanceof ImageBitmap ? ref_properties.exported.getImageData(image) : image;
        const {width, height, data} = imageData;
        if (width === undefined || height === undefined) {
            return this.fire(new ref_properties.ErrorEvent(new Error('Invalid arguments to map.updateImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, ' + 'or object with `width`, `height`, and `data` properties with the same format as `ImageData`')));
        }
        if (width !== existingImage.data.width || height !== existingImage.data.height) {
            return this.fire(new ref_properties.ErrorEvent(new Error('The width and height of the updated image must be that same as the previous version of the image')));
        }
        const copy = !(image instanceof HTMLImageElement || ImageBitmap && image instanceof ImageBitmap);
        existingImage.data.replace(data, copy);
        this.style.updateImage(id, existingImage);
    }
    hasImage(id) {
        if (!id) {
            this.fire(new ref_properties.ErrorEvent(new Error('Missing required image id')));
            return false;
        }
        return !!this.style.getImage(id);
    }
    removeImage(id) {
        this.style.removeImage(id);
    }
    loadImage(url, callback) {
        ref_properties.getImage(this._requestManager.transformRequest(url, ref_properties.ResourceType.Image), (err, img) => {
            callback(err, img instanceof HTMLImageElement ? ref_properties.exported.getImageData(img) : img);
        });
    }
    listImages() {
        return this.style.listImages();
    }
    addLayer(layer, beforeId) {
        this._lazyInitEmptyStyle();
        this.style.addLayer(layer, beforeId);
        return this._update(true);
    }
    moveLayer(id, beforeId) {
        this.style.moveLayer(id, beforeId);
        return this._update(true);
    }
    removeLayer(id) {
        this.style.removeLayer(id);
        return this._update(true);
    }
    getLayer(id) {
        return this.style.getLayer(id);
    }
    setLayerZoomRange(layerId, minzoom, maxzoom) {
        this.style.setLayerZoomRange(layerId, minzoom, maxzoom);
        return this._update(true);
    }
    setFilter(layerId, filter, options = {}) {
        this.style.setFilter(layerId, filter, options);
        return this._update(true);
    }
    getFilter(layerId) {
        return this.style.getFilter(layerId);
    }
    setPaintProperty(layerId, name, value, options = {}) {
        this.style.setPaintProperty(layerId, name, value, options);
        return this._update(true);
    }
    getPaintProperty(layerId, name) {
        return this.style.getPaintProperty(layerId, name);
    }
    setLayoutProperty(layerId, name, value, options = {}) {
        this.style.setLayoutProperty(layerId, name, value, options);
        return this._update(true);
    }
    getLayoutProperty(layerId, name) {
        return this.style.getLayoutProperty(layerId, name);
    }
    setLight(light, options = {}) {
        this._lazyInitEmptyStyle();
        this.style.setLight(light, options);
        return this._update(true);
    }
    getLight() {
        return this.style.getLight();
    }
    setTerrain(terrain) {
        this._lazyInitEmptyStyle();
        this.style.setTerrain(terrain);
        this._averageElevationLastSampledAt = -Infinity;
        return this._update(true);
    }
    getTerrain() {
        return this.style ? this.style.getTerrain() : null;
    }
    setFog(fog) {
        this._lazyInitEmptyStyle();
        this.style.setFog(fog);
        return this._update(true);
    }
    getFog() {
        return this.style ? this.style.getFog() : null;
    }
    _queryFogOpacity(lnglat) {
        if (!this.style || !this.style.fog)
            return 0;
        return this.style.fog.getOpacityAtLatLng(ref_properties.LngLat.convert(lnglat), this.transform);
    }
    queryTerrainElevation(lnglat, options) {
        const elevation = this.transform.elevation;
        if (elevation) {
            options = ref_properties.extend({}, { exaggerated: true }, options);
            return elevation.getAtPoint(ref_properties.MercatorCoordinate.fromLngLat(lnglat), null, options.exaggerated);
        }
        return null;
    }
    setFeatureState(feature, state) {
        this.style.setFeatureState(feature, state);
        return this._update();
    }
    removeFeatureState(feature, key) {
        this.style.removeFeatureState(feature, key);
        return this._update();
    }
    getFeatureState(feature) {
        return this.style.getFeatureState(feature);
    }
    getContainer() {
        return this._container;
    }
    getCanvasContainer() {
        return this._canvasContainer;
    }
    getCanvas() {
        return this._canvas;
    }
    _containerDimensions() {
        let width = 0;
        let height = 0;
        if (this._container) {
            width = this._container.clientWidth || 400;
            height = this._container.clientHeight || 300;
        }
        return [
            width,
            height
        ];
    }
    _detectMissingCSS() {
        const computedColor = ref_properties.window.getComputedStyle(this._missingCSSCanary).getPropertyValue('background-color');
        if (computedColor !== 'rgb(250, 128, 114)') {
            ref_properties.warnOnce('This page appears to be missing CSS declarations for ' + 'Mapbox GL JS, which may cause the map to display incorrectly. ' + 'Please ensure your page includes mapbox-gl.css, as described ' + 'in https://www.mapbox.com/mapbox-gl-js/api/.');
        }
    }
    _setupContainer() {
        const container = this._container;
        container.classList.add('mapboxgl-map');
        const missingCSSCanary = this._missingCSSCanary = DOM.create('div', 'mapboxgl-canary', container);
        missingCSSCanary.style.visibility = 'hidden';
        this._detectMissingCSS();
        const canvasContainer = this._canvasContainer = DOM.create('div', 'mapboxgl-canvas-container', container);
        if (this._interactive) {
            canvasContainer.classList.add('mapboxgl-interactive');
        }
        this._canvas = DOM.create('canvas', 'mapboxgl-canvas', canvasContainer);
        this._canvas.addEventListener('webglcontextlost', this._contextLost, false);
        this._canvas.addEventListener('webglcontextrestored', this._contextRestored, false);
        this._canvas.setAttribute('tabindex', '0');
        this._canvas.setAttribute('aria-label', 'Map');
        this._canvas.setAttribute('role', 'region');
        const dimensions = this._containerDimensions();
        this._resizeCanvas(dimensions[0], dimensions[1]);
        const controlContainer = this._controlContainer = DOM.create('div', 'mapboxgl-control-container', container);
        const positions = this._controlPositions = {};
        [
            'top-left',
            'top-right',
            'bottom-left',
            'bottom-right'
        ].forEach(positionName => {
            positions[positionName] = DOM.create('div', `mapboxgl-ctrl-${ positionName }`, controlContainer);
        });
        this._container.addEventListener('scroll', this._onMapScroll, false);
    }
    _resizeCanvas(width, height) {
        const pixelRatio = ref_properties.exported.devicePixelRatio || 1;
        this._canvas.width = pixelRatio * width;
        this._canvas.height = pixelRatio * height;
        this._canvas.style.width = `${ width }px`;
        this._canvas.style.height = `${ height }px`;
    }
    _addMarker(marker) {
        this._markers.push(marker);
    }
    _removeMarker(marker) {
        const index = this._markers.indexOf(marker);
        if (index !== -1) {
            this._markers.splice(index, 1);
        }
    }
    _setupPainter() {
        const attributes = ref_properties.extend({}, supported.webGLContextAttributes, {
            failIfMajorPerformanceCaveat: this._failIfMajorPerformanceCaveat,
            preserveDrawingBuffer: this._preserveDrawingBuffer,
            antialias: this._antialias || false
        });
        const gl = this._canvas.getContext('webgl', attributes) || this._canvas.getContext('experimental-webgl', attributes);
        if (!gl) {
            this.fire(new ref_properties.ErrorEvent(new Error('Failed to initialize WebGL')));
            return;
        }
        ref_properties.storeAuthState(gl, true);
        this.painter = new Painter(gl, this.transform);
        this.on('data', event => {
            if (event.dataType === 'source') {
                this.painter.setTileLoadedFlag(true);
            }
        });
        ref_properties.exported$1.testSupport(gl);
    }
    _contextLost(event) {
        event.preventDefault();
        if (this._frame) {
            this._frame.cancel();
            this._frame = null;
        }
        this.fire(new ref_properties.Event('webglcontextlost', { originalEvent: event }));
    }
    _contextRestored(event) {
        this._setupPainter();
        this.resize();
        this._update();
        this.fire(new ref_properties.Event('webglcontextrestored', { originalEvent: event }));
    }
    _onMapScroll(event) {
        if (event.target !== this._container)
            return;
        this._container.scrollTop = 0;
        this._container.scrollLeft = 0;
        return false;
    }
    loaded() {
        return !this._styleDirty && !this._sourcesDirty && !!this.style && this.style.loaded();
    }
    _update(updateStyle) {
        if (!this.style)
            return this;
        this._styleDirty = this._styleDirty || updateStyle;
        this._sourcesDirty = true;
        this.triggerRepaint();
        return this;
    }
    _requestRenderFrame(callback) {
        this._update();
        return this._renderTaskQueue.add(callback);
    }
    _cancelRenderFrame(id) {
        this._renderTaskQueue.remove(id);
    }
    _requestDomTask(callback) {
        if (!this.isMoving() && this.loaded()) {
            callback();
        } else {
            this._domRenderTaskQueue.add(callback);
        }
    }
    _render(paintStartTimeStamp) {
        let gpuTimer;
        const extTimerQuery = this.painter.context.extTimerQuery;
        const frameStartTime = ref_properties.exported.now();
        if (this.listens('gpu-timing-frame')) {
            gpuTimer = extTimerQuery.createQueryEXT();
            extTimerQuery.beginQueryEXT(extTimerQuery.TIME_ELAPSED_EXT, gpuTimer);
        }
        let averageElevationChanged = this._updateAverageElevation(frameStartTime);
        this.painter.context.setDirty();
        this.painter.setBaseState();
        this._renderTaskQueue.run(paintStartTimeStamp);
        this._domRenderTaskQueue.run(paintStartTimeStamp);
        if (this._removed)
            return;
        let crossFading = false;
        const fadeDuration = this._isInitialLoad ? 0 : this._fadeDuration;
        if (this.style && this._styleDirty) {
            this._styleDirty = false;
            const zoom = this.transform.zoom;
            const now = ref_properties.exported.now();
            this.style.zoomHistory.update(zoom, now);
            const parameters = new ref_properties.EvaluationParameters(zoom, {
                now,
                fadeDuration,
                zoomHistory: this.style.zoomHistory,
                transition: this.style.getTransition()
            });
            const factor = parameters.crossFadingFactor();
            if (factor !== 1 || factor !== this._crossFadingFactor) {
                crossFading = true;
                this._crossFadingFactor = factor;
            }
            this.style.update(parameters);
        }
        const fogIsTransitioning = this.style && this.style.fog && this.style.fog.hasTransition();
        if (fogIsTransitioning) {
            this.style._markersNeedUpdate = true;
            this._sourcesDirty = true;
        }
        if (this.style && this._sourcesDirty) {
            this._sourcesDirty = false;
            this.painter._updateFog(this.style);
            this._updateTerrain();
            this.style._updateSources(this.transform);
        }
        this._placementDirty = this.style && this.style._updatePlacement(this.painter.transform, this.showCollisionBoxes, fadeDuration, this._crossSourceCollisions);
        this.painter.render(this.style, {
            showTileBoundaries: this.showTileBoundaries,
            showTerrainWireframe: this.showTerrainWireframe,
            showOverdrawInspector: this._showOverdrawInspector,
            showQueryGeometry: !!this._showQueryGeometry,
            rotating: this.isRotating(),
            zooming: this.isZooming(),
            moving: this.isMoving(),
            fadeDuration,
            isInitialLoad: this._isInitialLoad,
            showPadding: this.showPadding,
            gpuTiming: !!this.listens('gpu-timing-layer'),
            speedIndexTiming: this.speedIndexTiming
        });
        this.fire(new ref_properties.Event('render'));
        if (this.loaded() && !this._loaded) {
            this._loaded = true;
            this.fire(new ref_properties.Event('load'));
        }
        if (this.style && (this.style.hasTransitions() || crossFading)) {
            this._styleDirty = true;
        }
        if (this.style && !this._placementDirty) {
            this.style._releaseSymbolFadeTiles();
        }
        if (this.listens('gpu-timing-frame')) {
            const renderCPUTime = ref_properties.exported.now() - frameStartTime;
            extTimerQuery.endQueryEXT(extTimerQuery.TIME_ELAPSED_EXT, gpuTimer);
            setTimeout(() => {
                const renderGPUTime = extTimerQuery.getQueryObjectEXT(gpuTimer, extTimerQuery.QUERY_RESULT_EXT) / (1000 * 1000);
                extTimerQuery.deleteQueryEXT(gpuTimer);
                this.fire(new ref_properties.Event('gpu-timing-frame', {
                    cpuTime: renderCPUTime,
                    gpuTime: renderGPUTime
                }));
            }, 50);
        }
        if (this.listens('gpu-timing-layer')) {
            const frameLayerQueries = this.painter.collectGpuTimers();
            setTimeout(() => {
                const renderedLayerTimes = this.painter.queryGpuTimers(frameLayerQueries);
                this.fire(new ref_properties.Event('gpu-timing-layer', { layerTimes: renderedLayerTimes }));
            }, 50);
        }
        const somethingDirty = this._sourcesDirty || this._styleDirty || this._placementDirty || averageElevationChanged;
        if (somethingDirty || this._repaint) {
            this.triggerRepaint();
        } else {
            const willIdle = !this.isMoving() && this.loaded();
            if (willIdle) {
                averageElevationChanged = this._updateAverageElevation(frameStartTime, true);
            }
            if (averageElevationChanged) {
                this.triggerRepaint();
            } else {
                this._triggerFrame(false);
                if (willIdle) {
                    this.fire(new ref_properties.Event('idle'));
                    this._isInitialLoad = false;
                    if (this.speedIndexTiming) {
                        const speedIndexNumber = this._calculateSpeedIndex();
                        this.fire(new ref_properties.Event('speedindexcompleted', { speedIndex: speedIndexNumber }));
                        this.speedIndexTiming = false;
                    }
                }
            }
        }
        if (this._loaded && !this._fullyLoaded && !somethingDirty) {
            this._fullyLoaded = true;
        }
        return this;
    }
    _updateAverageElevation(timeStamp, ignoreTimeout = false) {
        const applyUpdate = value => {
            this.transform.averageElevation = value;
            this._update(false);
            return true;
        };
        if (!this.painter.averageElevationNeedsEasing()) {
            if (this.transform.averageElevation !== 0)
                return applyUpdate(0);
            return false;
        }
        const timeoutElapsed = ignoreTimeout || timeStamp - this._averageElevationLastSampledAt > AVERAGE_ELEVATION_SAMPLING_INTERVAL;
        if (timeoutElapsed && !this._averageElevation.isEasing(timeStamp)) {
            const currentElevation = this.transform.averageElevation;
            let newElevation = this.transform.sampleAverageElevation();
            if (isNaN(newElevation)) {
                newElevation = 0;
            } else {
                this._averageElevationLastSampledAt = timeStamp;
            }
            const elevationChange = Math.abs(currentElevation - newElevation);
            if (elevationChange > AVERAGE_ELEVATION_EASE_THRESHOLD) {
                this._averageElevation.easeTo(newElevation, timeStamp, AVERAGE_ELEVATION_EASE_TIME);
            } else if (elevationChange > AVERAGE_ELEVATION_CHANGE_THRESHOLD) {
                this._averageElevation.jumpTo(newElevation);
                return applyUpdate(newElevation);
            }
        }
        if (this._averageElevation.isEasing(timeStamp)) {
            return applyUpdate(this._averageElevation.getValue(timeStamp));
        }
        return false;
    }
    _updateTerrain() {
        this.painter.updateTerrain(this.style, this.isMoving() || this.isRotating() || this.isZooming());
    }
    _calculateSpeedIndex() {
        const finalFrame = this.painter.canvasCopy();
        const canvasCopyInstances = this.painter.getCanvasCopiesAndTimestamps();
        canvasCopyInstances.timeStamps.push(performance.now());
        const gl = this.painter.context.gl;
        const framebuffer = gl.createFramebuffer();
        gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
        function read(texture) {
            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
            const pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4);
            gl.readPixels(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
            return pixels;
        }
        return this._canvasPixelComparison(read(finalFrame), canvasCopyInstances.canvasCopies.map(read), canvasCopyInstances.timeStamps);
    }
    _canvasPixelComparison(finalFrame, allFrames, timeStamps) {
        let finalScore = timeStamps[1] - timeStamps[0];
        const numPixels = finalFrame.length / 4;
        for (let i = 0; i < allFrames.length; i++) {
            const frame = allFrames[i];
            let cnt = 0;
            for (let j = 0; j < frame.length; j += 4) {
                if (frame[j] === finalFrame[j] && frame[j + 1] === finalFrame[j + 1] && frame[j + 2] === finalFrame[j + 2] && frame[j + 3] === finalFrame[j + 3]) {
                    cnt = cnt + 1;
                }
            }
            const interval = timeStamps[i + 2] - timeStamps[i + 1];
            const visualCompletness = cnt / numPixels;
            finalScore += interval * (1 - visualCompletness);
        }
        return finalScore;
    }
    remove() {
        if (this._hash)
            this._hash.remove();
        for (const control of this._controls)
            control.onRemove(this);
        this._controls = [];
        if (this._frame) {
            this._frame.cancel();
            this._frame = null;
        }
        this._renderTaskQueue.clear();
        this._domRenderTaskQueue.clear();
        this.painter.destroy();
        this.handlers.destroy();
        delete this.handlers;
        this.setStyle(null);
        if (typeof ref_properties.window !== 'undefined') {
            ref_properties.window.removeEventListener('resize', this._onWindowResize, false);
            ref_properties.window.removeEventListener('orientationchange', this._onWindowResize, false);
            ref_properties.window.removeEventListener('online', this._onWindowOnline, false);
        }
        const extension = this.painter.context.gl.getExtension('WEBGL_lose_context');
        if (extension)
            extension.loseContext();
        removeNode(this._canvasContainer);
        removeNode(this._controlContainer);
        removeNode(this._missingCSSCanary);
        this._container.classList.remove('mapboxgl-map');
        ref_properties.removeAuthState(this.painter.context.gl);
        this._removed = true;
        this.fire(new ref_properties.Event('remove'));
    }
    triggerRepaint() {
        this._triggerFrame(true);
    }
    _triggerFrame(render) {
        this._renderNextFrame = this._renderNextFrame || render;
        if (this.style && !this._frame) {
            this._frame = ref_properties.exported.frame(paintStartTimeStamp => {
                const isRenderFrame = !!this._renderNextFrame;
                this._frame = null;
                this._renderNextFrame = null;
                if (isRenderFrame) {
                    this._render(paintStartTimeStamp);
                }
            });
        }
    }
    _onWindowOnline() {
        this._update();
    }
    _onWindowResize(event) {
        if (this._trackResize) {
            this.resize({ originalEvent: event })._update();
        }
    }
    get showTileBoundaries() {
        return !!this._showTileBoundaries;
    }
    set showTileBoundaries(value) {
        if (this._showTileBoundaries === value)
            return;
        this._showTileBoundaries = value;
        this._update();
    }
    get showTerrainWireframe() {
        return !!this._showTerrainWireframe;
    }
    set showTerrainWireframe(value) {
        if (this._showTerrainWireframe === value)
            return;
        this._showTerrainWireframe = value;
        this._update();
    }
    get speedIndexTiming() {
        return !!this._speedIndexTiming;
    }
    set speedIndexTiming(value) {
        if (this._speedIndexTiming === value)
            return;
        this._speedIndexTiming = value;
        this._update();
    }
    get showPadding() {
        return !!this._showPadding;
    }
    set showPadding(value) {
        if (this._showPadding === value)
            return;
        this._showPadding = value;
        this._update();
    }
    get showCollisionBoxes() {
        return !!this._showCollisionBoxes;
    }
    set showCollisionBoxes(value) {
        if (this._showCollisionBoxes === value)
            return;
        this._showCollisionBoxes = value;
        if (value) {
            this.style._generateCollisionBoxes();
        } else {
            this._update();
        }
    }
    get showOverdrawInspector() {
        return !!this._showOverdrawInspector;
    }
    set showOverdrawInspector(value) {
        if (this._showOverdrawInspector === value)
            return;
        this._showOverdrawInspector = value;
        this._update();
    }
    get repaint() {
        return !!this._repaint;
    }
    set repaint(value) {
        if (this._repaint !== value) {
            this._repaint = value;
            this.triggerRepaint();
        }
    }
    get vertices() {
        return !!this._vertices;
    }
    set vertices(value) {
        this._vertices = value;
        this._update();
    }
    _setCacheLimits(limit, checkThreshold) {
        ref_properties.setCacheLimits(limit, checkThreshold);
    }
    get version() {
        return ref_properties.version;
    }
}
function removeNode(node) {
    if (node.parentNode) {
        node.parentNode.removeChild(node);
    }
}

const defaultOptions$3 = {
    showCompass: true,
    showZoom: true,
    visualizePitch: false
};
class NavigationControl {
    constructor(options) {
        this.options = ref_properties.extend({}, defaultOptions$3, options);
        this._container = DOM.create('div', 'mapboxgl-ctrl mapboxgl-ctrl-group');
        this._container.addEventListener('contextmenu', e => e.preventDefault());
        if (this.options.showZoom) {
            ref_properties.bindAll([
                '_setButtonTitle',
                '_updateZoomButtons'
            ], this);
            this._zoomInButton = this._createButton('mapboxgl-ctrl-zoom-in', e => this._map.zoomIn({}, { originalEvent: e }));
            DOM.create('span', `mapboxgl-ctrl-icon`, this._zoomInButton).setAttribute('aria-hidden', true);
            this._zoomOutButton = this._createButton('mapboxgl-ctrl-zoom-out', e => this._map.zoomOut({}, { originalEvent: e }));
            DOM.create('span', `mapboxgl-ctrl-icon`, this._zoomOutButton).setAttribute('aria-hidden', true);
        }
        if (this.options.showCompass) {
            ref_properties.bindAll(['_rotateCompassArrow'], this);
            this._compass = this._createButton('mapboxgl-ctrl-compass', e => {
                if (this.options.visualizePitch) {
                    this._map.resetNorthPitch({}, { originalEvent: e });
                } else {
                    this._map.resetNorth({}, { originalEvent: e });
                }
            });
            this._compassIcon = DOM.create('span', 'mapboxgl-ctrl-icon', this._compass);
            this._compassIcon.setAttribute('aria-hidden', true);
        }
    }
    _updateZoomButtons() {
        const zoom = this._map.getZoom();
        const isMax = zoom === this._map.getMaxZoom();
        const isMin = zoom === this._map.getMinZoom();
        this._zoomInButton.disabled = isMax;
        this._zoomOutButton.disabled = isMin;
        this._zoomInButton.setAttribute('aria-disabled', isMax.toString());
        this._zoomOutButton.setAttribute('aria-disabled', isMin.toString());
    }
    _rotateCompassArrow() {
        const rotate = this.options.visualizePitch ? `scale(${ 1 / Math.pow(Math.cos(this._map.transform.pitch * (Math.PI / 180)), 0.5) }) rotateX(${ this._map.transform.pitch }deg) rotateZ(${ this._map.transform.angle * (180 / Math.PI) }deg)` : `rotate(${ this._map.transform.angle * (180 / Math.PI) }deg)`;
        this._map._requestDomTask(() => {
            if (this._compassIcon) {
                this._compassIcon.style.transform = rotate;
            }
        });
    }
    onAdd(map) {
        this._map = map;
        if (this.options.showZoom) {
            this._setButtonTitle(this._zoomInButton, 'ZoomIn');
            this._setButtonTitle(this._zoomOutButton, 'ZoomOut');
            this._map.on('zoom', this._updateZoomButtons);
            this._updateZoomButtons();
        }
        if (this.options.showCompass) {
            this._setButtonTitle(this._compass, 'ResetBearing');
            if (this.options.visualizePitch) {
                this._map.on('pitch', this._rotateCompassArrow);
            }
            this._map.on('rotate', this._rotateCompassArrow);
            this._rotateCompassArrow();
            this._handler = new MouseRotateWrapper(this._map, this._compass, this.options.visualizePitch);
        }
        return this._container;
    }
    onRemove() {
        DOM.remove(this._container);
        if (this.options.showZoom) {
            this._map.off('zoom', this._updateZoomButtons);
        }
        if (this.options.showCompass) {
            if (this.options.visualizePitch) {
                this._map.off('pitch', this._rotateCompassArrow);
            }
            this._map.off('rotate', this._rotateCompassArrow);
            this._handler.off();
            delete this._handler;
        }
        delete this._map;
    }
    _createButton(className, fn) {
        const a = DOM.create('button', className, this._container);
        a.type = 'button';
        a.addEventListener('click', fn);
        return a;
    }
    _setButtonTitle(button, title) {
        const str = this._map._getUIString(`NavigationControl.${ title }`);
        button.title = str;
        button.setAttribute('aria-label', str);
    }
}
class MouseRotateWrapper {
    constructor(map, element, pitch = false) {
        this._clickTolerance = 10;
        this.element = element;
        this.mouseRotate = new MouseRotateHandler({ clickTolerance: map.dragRotate._mouseRotate._clickTolerance });
        this.map = map;
        if (pitch)
            this.mousePitch = new MousePitchHandler({ clickTolerance: map.dragRotate._mousePitch._clickTolerance });
        ref_properties.bindAll([
            'mousedown',
            'mousemove',
            'mouseup',
            'touchstart',
            'touchmove',
            'touchend',
            'reset'
        ], this);
        DOM.addEventListener(element, 'mousedown', this.mousedown);
        DOM.addEventListener(element, 'touchstart', this.touchstart, { passive: false });
        DOM.addEventListener(element, 'touchmove', this.touchmove);
        DOM.addEventListener(element, 'touchend', this.touchend);
        DOM.addEventListener(element, 'touchcancel', this.reset);
    }
    down(e, point) {
        this.mouseRotate.mousedown(e, point);
        if (this.mousePitch)
            this.mousePitch.mousedown(e, point);
        DOM.disableDrag();
    }
    move(e, point) {
        const map = this.map;
        const r = this.mouseRotate.mousemoveWindow(e, point);
        if (r && r.bearingDelta)
            map.setBearing(map.getBearing() + r.bearingDelta);
        if (this.mousePitch) {
            const p = this.mousePitch.mousemoveWindow(e, point);
            if (p && p.pitchDelta)
                map.setPitch(map.getPitch() + p.pitchDelta);
        }
    }
    off() {
        const element = this.element;
        DOM.removeEventListener(element, 'mousedown', this.mousedown);
        DOM.removeEventListener(element, 'touchstart', this.touchstart, { passive: false });
        DOM.removeEventListener(element, 'touchmove', this.touchmove);
        DOM.removeEventListener(element, 'touchend', this.touchend);
        DOM.removeEventListener(element, 'touchcancel', this.reset);
        this.offTemp();
    }
    offTemp() {
        DOM.enableDrag();
        DOM.removeEventListener(ref_properties.window, 'mousemove', this.mousemove);
        DOM.removeEventListener(ref_properties.window, 'mouseup', this.mouseup);
    }
    mousedown(e) {
        this.down(ref_properties.extend({}, e, {
            ctrlKey: true,
            preventDefault: () => e.preventDefault()
        }), DOM.mousePos(this.element, e));
        DOM.addEventListener(ref_properties.window, 'mousemove', this.mousemove);
        DOM.addEventListener(ref_properties.window, 'mouseup', this.mouseup);
    }
    mousemove(e) {
        this.move(e, DOM.mousePos(this.element, e));
    }
    mouseup(e) {
        this.mouseRotate.mouseupWindow(e);
        if (this.mousePitch)
            this.mousePitch.mouseupWindow(e);
        this.offTemp();
    }
    touchstart(e) {
        if (e.targetTouches.length !== 1) {
            this.reset();
        } else {
            this._startPos = this._lastPos = DOM.touchPos(this.element, e.targetTouches)[0];
            this.down({
                type: 'mousedown',
                button: 0,
                ctrlKey: true,
                preventDefault: () => e.preventDefault()
            }, this._startPos);
        }
    }
    touchmove(e) {
        if (e.targetTouches.length !== 1) {
            this.reset();
        } else {
            this._lastPos = DOM.touchPos(this.element, e.targetTouches)[0];
            this.move({ preventDefault: () => e.preventDefault() }, this._lastPos);
        }
    }
    touchend(e) {
        if (e.targetTouches.length === 0 && this._startPos && this._lastPos && this._startPos.dist(this._lastPos) < this._clickTolerance) {
            this.element.click();
        }
        this.reset();
    }
    reset() {
        this.mouseRotate.reset();
        if (this.mousePitch)
            this.mousePitch.reset();
        delete this._startPos;
        delete this._lastPos;
        this.offTemp();
    }
}

const defaultOptions$2 = {
    positionOptions: {
        enableHighAccuracy: false,
        maximumAge: 0,
        timeout: 6000
    },
    fitBoundsOptions: { maxZoom: 15 },
    trackUserLocation: false,
    showAccuracyCircle: true,
    showUserLocation: true
};
let supportsGeolocation;
function checkGeolocationSupport(callback) {
    if (supportsGeolocation !== undefined) {
        callback(supportsGeolocation);
    } else if (ref_properties.window.navigator.permissions !== undefined) {
        ref_properties.window.navigator.permissions.query({ name: 'geolocation' }).then(p => {
            supportsGeolocation = p.state !== 'denied';
            callback(supportsGeolocation);
        });
    } else {
        supportsGeolocation = !!ref_properties.window.navigator.geolocation;
        callback(supportsGeolocation);
    }
}
let numberOfWatches = 0;
let noTimeout = false;
class GeolocateControl extends ref_properties.Evented {
    constructor(options) {
        super();
        this.options = ref_properties.extend({}, defaultOptions$2, options);
        ref_properties.bindAll([
            '_onSuccess',
            '_onError',
            '_onZoom',
            '_finish',
            '_setupUI',
            '_updateCamera',
            '_updateMarker'
        ], this);
    }
    onAdd(map) {
        this._map = map;
        this._container = DOM.create('div', `mapboxgl-ctrl mapboxgl-ctrl-group`);
        checkGeolocationSupport(this._setupUI);
        return this._container;
    }
    onRemove() {
        if (this._geolocationWatchID !== undefined) {
            ref_properties.window.navigator.geolocation.clearWatch(this._geolocationWatchID);
            this._geolocationWatchID = undefined;
        }
        if (this.options.showUserLocation && this._userLocationDotMarker) {
            this._userLocationDotMarker.remove();
        }
        if (this.options.showAccuracyCircle && this._accuracyCircleMarker) {
            this._accuracyCircleMarker.remove();
        }
        DOM.remove(this._container);
        this._map.off('zoom', this._onZoom);
        this._map = undefined;
        numberOfWatches = 0;
        noTimeout = false;
    }
    _isOutOfMapMaxBounds(position) {
        const bounds = this._map.getMaxBounds();
        const coordinates = position.coords;
        return bounds && (coordinates.longitude < bounds.getWest() || coordinates.longitude > bounds.getEast() || coordinates.latitude < bounds.getSouth() || coordinates.latitude > bounds.getNorth());
    }
    _setErrorState() {
        switch (this._watchState) {
        case 'WAITING_ACTIVE':
            this._watchState = 'ACTIVE_ERROR';
            this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-active');
            this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-active-error');
            break;
        case 'ACTIVE_LOCK':
            this._watchState = 'ACTIVE_ERROR';
            this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-active');
            this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-active-error');
            this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-waiting');
            break;
        case 'BACKGROUND':
            this._watchState = 'BACKGROUND_ERROR';
            this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-background');
            this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-background-error');
            this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-waiting');
            break;
        }
    }
    _onSuccess(position) {
        if (!this._map) {
            return;
        }
        if (this._isOutOfMapMaxBounds(position)) {
            this._setErrorState();
            this.fire(new ref_properties.Event('outofmaxbounds', position));
            this._updateMarker();
            this._finish();
            return;
        }
        if (this.options.trackUserLocation) {
            this._lastKnownPosition = position;
            switch (this._watchState) {
            case 'WAITING_ACTIVE':
            case 'ACTIVE_LOCK':
            case 'ACTIVE_ERROR':
                this._watchState = 'ACTIVE_LOCK';
                this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-waiting');
                this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-active-error');
                this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-active');
                break;
            case 'BACKGROUND':
            case 'BACKGROUND_ERROR':
                this._watchState = 'BACKGROUND';
                this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-waiting');
                this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-background-error');
                this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-background');
                break;
            }
        }
        if (this.options.showUserLocation && this._watchState !== 'OFF') {
            this._updateMarker(position);
        }
        if (!this.options.trackUserLocation || this._watchState === 'ACTIVE_LOCK') {
            this._updateCamera(position);
        }
        if (this.options.showUserLocation) {
            this._dotElement.classList.remove('mapboxgl-user-location-dot-stale');
        }
        this.fire(new ref_properties.Event('geolocate', position));
        this._finish();
    }
    _updateCamera(position) {
        const center = new ref_properties.LngLat(position.coords.longitude, position.coords.latitude);
        const radius = position.coords.accuracy;
        const bearing = this._map.getBearing();
        const options = ref_properties.extend({ bearing }, this.options.fitBoundsOptions);
        this._map.fitBounds(center.toBounds(radius), options, { geolocateSource: true });
    }
    _updateMarker(position) {
        if (position) {
            const center = new ref_properties.LngLat(position.coords.longitude, position.coords.latitude);
            this._accuracyCircleMarker.setLngLat(center).addTo(this._map);
            this._userLocationDotMarker.setLngLat(center).addTo(this._map);
            this._accuracy = position.coords.accuracy;
            if (this.options.showUserLocation && this.options.showAccuracyCircle) {
                this._updateCircleRadius();
            }
        } else {
            this._userLocationDotMarker.remove();
            this._accuracyCircleMarker.remove();
        }
    }
    _updateCircleRadius() {
        const y = this._map._container.clientHeight / 2;
        const a = this._map.unproject([
            0,
            y
        ]);
        const b = this._map.unproject([
            100,
            y
        ]);
        const metersPerPixel = a.distanceTo(b) / 100;
        const circleDiameter = Math.ceil(2 * this._accuracy / metersPerPixel);
        this._circleElement.style.width = `${ circleDiameter }px`;
        this._circleElement.style.height = `${ circleDiameter }px`;
    }
    _onZoom() {
        if (this.options.showUserLocation && this.options.showAccuracyCircle) {
            this._updateCircleRadius();
        }
    }
    _onError(error) {
        if (!this._map) {
            return;
        }
        if (this.options.trackUserLocation) {
            if (error.code === 1) {
                this._watchState = 'OFF';
                this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-waiting');
                this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-active');
                this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-active-error');
                this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-background');
                this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-background-error');
                this._geolocateButton.disabled = true;
                const title = this._map._getUIString('GeolocateControl.LocationNotAvailable');
                this._geolocateButton.title = title;
                this._geolocateButton.setAttribute('aria-label', title);
                if (this._geolocationWatchID !== undefined) {
                    this._clearWatch();
                }
            } else if (error.code === 3 && noTimeout) {
                return;
            } else {
                this._setErrorState();
            }
        }
        if (this._watchState !== 'OFF' && this.options.showUserLocation) {
            this._dotElement.classList.add('mapboxgl-user-location-dot-stale');
        }
        this.fire(new ref_properties.Event('error', error));
        this._finish();
    }
    _finish() {
        if (this._timeoutId) {
            clearTimeout(this._timeoutId);
        }
        this._timeoutId = undefined;
    }
    _setupUI(supported) {
        this._container.addEventListener('contextmenu', e => e.preventDefault());
        this._geolocateButton = DOM.create('button', `mapboxgl-ctrl-geolocate`, this._container);
        DOM.create('span', `mapboxgl-ctrl-icon`, this._geolocateButton).setAttribute('aria-hidden', true);
        this._geolocateButton.type = 'button';
        if (supported === false) {
            ref_properties.warnOnce('Geolocation support is not available so the GeolocateControl will be disabled.');
            const title = this._map._getUIString('GeolocateControl.LocationNotAvailable');
            this._geolocateButton.disabled = true;
            this._geolocateButton.title = title;
            this._geolocateButton.setAttribute('aria-label', title);
        } else {
            const title = this._map._getUIString('GeolocateControl.FindMyLocation');
            this._geolocateButton.title = title;
            this._geolocateButton.setAttribute('aria-label', title);
        }
        if (this.options.trackUserLocation) {
            this._geolocateButton.setAttribute('aria-pressed', 'false');
            this._watchState = 'OFF';
        }
        if (this.options.showUserLocation) {
            this._dotElement = DOM.create('div', 'mapboxgl-user-location-dot');
            this._userLocationDotMarker = new Marker(this._dotElement);
            this._circleElement = DOM.create('div', 'mapboxgl-user-location-accuracy-circle');
            this._accuracyCircleMarker = new Marker({
                element: this._circleElement,
                pitchAlignment: 'map'
            });
            if (this.options.trackUserLocation)
                this._watchState = 'OFF';
            this._map.on('zoom', this._onZoom);
        }
        this._geolocateButton.addEventListener('click', this.trigger.bind(this));
        this._setup = true;
        if (this.options.trackUserLocation) {
            this._map.on('movestart', event => {
                const fromResize = event.originalEvent && event.originalEvent.type === 'resize';
                if (!event.geolocateSource && this._watchState === 'ACTIVE_LOCK' && !fromResize) {
                    this._watchState = 'BACKGROUND';
                    this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-background');
                    this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-active');
                    this.fire(new ref_properties.Event('trackuserlocationend'));
                }
            });
        }
    }
    trigger() {
        if (!this._setup) {
            ref_properties.warnOnce('Geolocate control triggered before added to a map');
            return false;
        }
        if (this.options.trackUserLocation) {
            switch (this._watchState) {
            case 'OFF':
                this._watchState = 'WAITING_ACTIVE';
                this.fire(new ref_properties.Event('trackuserlocationstart'));
                break;
            case 'WAITING_ACTIVE':
            case 'ACTIVE_LOCK':
            case 'ACTIVE_ERROR':
            case 'BACKGROUND_ERROR':
                numberOfWatches--;
                noTimeout = false;
                this._watchState = 'OFF';
                this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-waiting');
                this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-active');
                this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-active-error');
                this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-background');
                this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-background-error');
                this.fire(new ref_properties.Event('trackuserlocationend'));
                break;
            case 'BACKGROUND':
                this._watchState = 'ACTIVE_LOCK';
                this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-background');
                if (this._lastKnownPosition)
                    this._updateCamera(this._lastKnownPosition);
                this.fire(new ref_properties.Event('trackuserlocationstart'));
                break;
            }
            switch (this._watchState) {
            case 'WAITING_ACTIVE':
                this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-waiting');
                this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-active');
                break;
            case 'ACTIVE_LOCK':
                this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-active');
                break;
            case 'ACTIVE_ERROR':
                this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-waiting');
                this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-active-error');
                break;
            case 'BACKGROUND':
                this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-background');
                break;
            case 'BACKGROUND_ERROR':
                this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-waiting');
                this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-background-error');
                break;
            }
            if (this._watchState === 'OFF' && this._geolocationWatchID !== undefined) {
                this._clearWatch();
            } else if (this._geolocationWatchID === undefined) {
                this._geolocateButton.classList.add('mapboxgl-ctrl-geolocate-waiting');
                this._geolocateButton.setAttribute('aria-pressed', 'true');
                numberOfWatches++;
                let positionOptions;
                if (numberOfWatches > 1) {
                    positionOptions = {
                        maximumAge: 600000,
                        timeout: 0
                    };
                    noTimeout = true;
                } else {
                    positionOptions = this.options.positionOptions;
                    noTimeout = false;
                }
                this._geolocationWatchID = ref_properties.window.navigator.geolocation.watchPosition(this._onSuccess, this._onError, positionOptions);
            }
        } else {
            ref_properties.window.navigator.geolocation.getCurrentPosition(this._onSuccess, this._onError, this.options.positionOptions);
            this._timeoutId = setTimeout(this._finish, 10000);
        }
        return true;
    }
    _clearWatch() {
        ref_properties.window.navigator.geolocation.clearWatch(this._geolocationWatchID);
        this._geolocationWatchID = undefined;
        this._geolocateButton.classList.remove('mapboxgl-ctrl-geolocate-waiting');
        this._geolocateButton.setAttribute('aria-pressed', 'false');
        if (this.options.showUserLocation) {
            this._updateMarker(null);
        }
    }
}

const defaultOptions$1 = {
    maxWidth: 100,
    unit: 'metric'
};
class ScaleControl {
    constructor(options) {
        this.options = ref_properties.extend({}, defaultOptions$1, options);
        ref_properties.bindAll([
            '_onMove',
            'setUnit'
        ], this);
    }
    getDefaultPosition() {
        return 'bottom-left';
    }
    _onMove() {
        updateScale(this._map, this._container, this.options);
    }
    onAdd(map) {
        this._map = map;
        this._container = DOM.create('div', 'mapboxgl-ctrl mapboxgl-ctrl-scale', map.getContainer());
        this._map.on('move', this._onMove);
        this._onMove();
        return this._container;
    }
    onRemove() {
        DOM.remove(this._container);
        this._map.off('move', this._onMove);
        this._map = undefined;
    }
    setUnit(unit) {
        this.options.unit = unit;
        updateScale(this._map, this._container, this.options);
    }
}
function updateScale(map, container, options) {
    const maxWidth = options && options.maxWidth || 100;
    const y = map._container.clientHeight / 2;
    const left = map.unproject([
        0,
        y
    ]);
    const right = map.unproject([
        maxWidth,
        y
    ]);
    const maxMeters = left.distanceTo(right);
    if (options && options.unit === 'imperial') {
        const maxFeet = 3.2808 * maxMeters;
        if (maxFeet > 5280) {
            const maxMiles = maxFeet / 5280;
            setScale(container, maxWidth, maxMiles, map._getUIString('ScaleControl.Miles'), map);
        } else {
            setScale(container, maxWidth, maxFeet, map._getUIString('ScaleControl.Feet'), map);
        }
    } else if (options && options.unit === 'nautical') {
        const maxNauticals = maxMeters / 1852;
        setScale(container, maxWidth, maxNauticals, map._getUIString('ScaleControl.NauticalMiles'), map);
    } else if (maxMeters >= 1000) {
        setScale(container, maxWidth, maxMeters / 1000, map._getUIString('ScaleControl.Kilometers'), map);
    } else {
        setScale(container, maxWidth, maxMeters, map._getUIString('ScaleControl.Meters'), map);
    }
}
function setScale(container, maxWidth, maxDistance, unit, map) {
    const distance = getRoundNum(maxDistance);
    const ratio = distance / maxDistance;
    map._requestDomTask(() => {
        container.style.width = `${ maxWidth * ratio }px`;
        container.innerHTML = `${ distance }&nbsp;${ unit }`;
    });
}
function getDecimalRoundNum(d) {
    const multiplier = Math.pow(10, Math.ceil(-Math.log(d) / Math.LN10));
    return Math.round(d * multiplier) / multiplier;
}
function getRoundNum(num) {
    const pow10 = Math.pow(10, `${ Math.floor(num) }`.length - 1);
    let d = num / pow10;
    d = d >= 10 ? 10 : d >= 5 ? 5 : d >= 3 ? 3 : d >= 2 ? 2 : d >= 1 ? 1 : getDecimalRoundNum(d);
    return pow10 * d;
}

class FullscreenControl {
    constructor(options) {
        this._fullscreen = false;
        if (options && options.container) {
            if (options.container instanceof ref_properties.window.HTMLElement) {
                this._container = options.container;
            } else {
                ref_properties.warnOnce('Full screen control \'container\' must be a DOM element.');
            }
        }
        ref_properties.bindAll([
            '_onClickFullscreen',
            '_changeIcon'
        ], this);
        if ('onfullscreenchange' in ref_properties.window.document) {
            this._fullscreenchange = 'fullscreenchange';
        } else if ('onwebkitfullscreenchange' in ref_properties.window.document) {
            this._fullscreenchange = 'webkitfullscreenchange';
        }
    }
    onAdd(map) {
        this._map = map;
        if (!this._container)
            this._container = this._map.getContainer();
        this._controlContainer = DOM.create('div', `mapboxgl-ctrl mapboxgl-ctrl-group`);
        if (this._checkFullscreenSupport()) {
            this._setupUI();
        } else {
            this._controlContainer.style.display = 'none';
            ref_properties.warnOnce('This device does not support fullscreen mode.');
        }
        return this._controlContainer;
    }
    onRemove() {
        DOM.remove(this._controlContainer);
        this._map = null;
        ref_properties.window.document.removeEventListener(this._fullscreenchange, this._changeIcon);
    }
    _checkFullscreenSupport() {
        return !!(ref_properties.window.document.fullscreenEnabled || ref_properties.window.document.webkitFullscreenEnabled);
    }
    _setupUI() {
        const button = this._fullscreenButton = DOM.create('button', `mapboxgl-ctrl-fullscreen`, this._controlContainer);
        DOM.create('span', `mapboxgl-ctrl-icon`, button).setAttribute('aria-hidden', true);
        button.type = 'button';
        this._updateTitle();
        this._fullscreenButton.addEventListener('click', this._onClickFullscreen);
        ref_properties.window.document.addEventListener(this._fullscreenchange, this._changeIcon);
    }
    _updateTitle() {
        const title = this._getTitle();
        this._fullscreenButton.setAttribute('aria-label', title);
        this._fullscreenButton.title = title;
    }
    _getTitle() {
        return this._map._getUIString(this._isFullscreen() ? 'FullscreenControl.Exit' : 'FullscreenControl.Enter');
    }
    _isFullscreen() {
        return this._fullscreen;
    }
    _changeIcon() {
        const fullscreenElement = ref_properties.window.document.fullscreenElement || ref_properties.window.document.webkitFullscreenElement;
        if (fullscreenElement === this._container !== this._fullscreen) {
            this._fullscreen = !this._fullscreen;
            this._fullscreenButton.classList.toggle(`mapboxgl-ctrl-shrink`);
            this._fullscreenButton.classList.toggle(`mapboxgl-ctrl-fullscreen`);
            this._updateTitle();
        }
    }
    _onClickFullscreen() {
        if (this._isFullscreen()) {
            if (ref_properties.window.document.exitFullscreen) {
                ref_properties.window.document.exitFullscreen();
            } else if (ref_properties.window.document.webkitCancelFullScreen) {
                ref_properties.window.document.webkitCancelFullScreen();
            }
        } else if (this._container.requestFullscreen) {
            this._container.requestFullscreen();
        } else if (this._container.webkitRequestFullscreen) {
            this._container.webkitRequestFullscreen();
        }
    }
}

const defaultOptions = {
    closeButton: true,
    closeOnClick: true,
    focusAfterOpen: true,
    className: '',
    maxWidth: '240px'
};
const focusQuerySelector = [
    'a[href]',
    '[tabindex]:not([tabindex=\'-1\'])',
    '[contenteditable]:not([contenteditable=\'false\'])',
    'button:not([disabled])',
    'input:not([disabled])',
    'select:not([disabled])',
    'textarea:not([disabled])'
].join(', ');
class Popup extends ref_properties.Evented {
    constructor(options) {
        super();
        this.options = ref_properties.extend(Object.create(defaultOptions), options);
        ref_properties.bindAll([
            '_update',
            '_onClose',
            'remove',
            '_onMouseMove',
            '_onMouseUp',
            '_onDrag'
        ], this);
    }
    addTo(map) {
        if (this._map)
            this.remove();
        this._map = map;
        if (this.options.closeOnClick) {
            this._map.on('click', this._onClose);
        }
        if (this.options.closeOnMove) {
            this._map.on('move', this._onClose);
        }
        this._map.on('remove', this.remove);
        this._update();
        this._focusFirstElement();
        if (this._trackPointer) {
            this._map.on('mousemove', this._onMouseMove);
            this._map.on('mouseup', this._onMouseUp);
            if (this._container) {
                this._container.classList.add('mapboxgl-popup-track-pointer');
            }
            this._map._canvasContainer.classList.add('mapboxgl-track-pointer');
        } else {
            this._map.on('move', this._update);
        }
        this.fire(new ref_properties.Event('open'));
        return this;
    }
    isOpen() {
        return !!this._map;
    }
    remove() {
        if (this._content) {
            DOM.remove(this._content);
        }
        if (this._container) {
            DOM.remove(this._container);
            delete this._container;
        }
        if (this._map) {
            this._map.off('move', this._update);
            this._map.off('move', this._onClose);
            this._map.off('click', this._onClose);
            this._map.off('remove', this.remove);
            this._map.off('mousemove', this._onMouseMove);
            this._map.off('mouseup', this._onMouseUp);
            this._map.off('drag', this._onDrag);
            delete this._map;
        }
        this.fire(new ref_properties.Event('close'));
        return this;
    }
    getLngLat() {
        return this._lngLat;
    }
    setLngLat(lnglat) {
        this._lngLat = ref_properties.LngLat.convert(lnglat);
        this._pos = null;
        this._trackPointer = false;
        this._update();
        if (this._map) {
            this._map.on('move', this._update);
            this._map.off('mousemove', this._onMouseMove);
            if (this._container) {
                this._container.classList.remove('mapboxgl-popup-track-pointer');
            }
            this._map._canvasContainer.classList.remove('mapboxgl-track-pointer');
        }
        return this;
    }
    trackPointer() {
        this._trackPointer = true;
        this._pos = null;
        this._update();
        if (this._map) {
            this._map.off('move', this._update);
            this._map.on('mousemove', this._onMouseMove);
            this._map.on('drag', this._onDrag);
            if (this._container) {
                this._container.classList.add('mapboxgl-popup-track-pointer');
            }
            this._map._canvasContainer.classList.add('mapboxgl-track-pointer');
        }
        return this;
    }
    getElement() {
        return this._container;
    }
    setText(text) {
        return this.setDOMContent(ref_properties.window.document.createTextNode(text));
    }
    setHTML(html) {
        const frag = ref_properties.window.document.createDocumentFragment();
        const temp = ref_properties.window.document.createElement('body');
        let child;
        temp.innerHTML = html;
        while (true) {
            child = temp.firstChild;
            if (!child)
                break;
            frag.appendChild(child);
        }
        return this.setDOMContent(frag);
    }
    getMaxWidth() {
        return this._container && this._container.style.maxWidth;
    }
    setMaxWidth(maxWidth) {
        this.options.maxWidth = maxWidth;
        this._update();
        return this;
    }
    setDOMContent(htmlNode) {
        if (this._content) {
            while (this._content.hasChildNodes()) {
                if (this._content.firstChild) {
                    this._content.removeChild(this._content.firstChild);
                }
            }
        } else {
            this._content = DOM.create('div', 'mapboxgl-popup-content', this._container);
        }
        this._content.appendChild(htmlNode);
        this._createCloseButton();
        this._update();
        this._focusFirstElement();
        return this;
    }
    addClassName(className) {
        if (this._container) {
            this._container.classList.add(className);
        }
    }
    removeClassName(className) {
        if (this._container) {
            this._container.classList.remove(className);
        }
    }
    setOffset(offset) {
        this.options.offset = offset;
        this._update();
        return this;
    }
    toggleClassName(className) {
        if (this._container) {
            return this._container.classList.toggle(className);
        }
    }
    _createCloseButton() {
        if (this.options.closeButton) {
            this._closeButton = DOM.create('button', 'mapboxgl-popup-close-button', this._content);
            this._closeButton.type = 'button';
            this._closeButton.setAttribute('aria-label', 'Close popup');
            this._closeButton.innerHTML = '&#215;';
            this._closeButton.addEventListener('click', this._onClose);
        }
    }
    _onMouseUp(event) {
        this._update(event.point);
    }
    _onMouseMove(event) {
        this._update(event.point);
    }
    _onDrag(event) {
        this._update(event.point);
    }
    _update(cursor) {
        const hasPosition = this._lngLat || this._trackPointer;
        if (!this._map || !hasPosition || !this._content) {
            return;
        }
        if (!this._container) {
            this._container = DOM.create('div', 'mapboxgl-popup', this._map.getContainer());
            this._tip = DOM.create('div', 'mapboxgl-popup-tip', this._container);
            this._container.appendChild(this._content);
            if (this.options.className) {
                this.options.className.split(' ').forEach(name => this._container.classList.add(name));
            }
            if (this._trackPointer) {
                this._container.classList.add('mapboxgl-popup-track-pointer');
            }
        }
        if (this.options.maxWidth && this._container.style.maxWidth !== this.options.maxWidth) {
            this._container.style.maxWidth = this.options.maxWidth;
        }
        if (this._map.transform.renderWorldCopies && !this._trackPointer) {
            this._lngLat = smartWrap(this._lngLat, this._pos, this._map.transform);
        }
        if (this._trackPointer && !cursor)
            return;
        const pos = this._pos = this._trackPointer && cursor ? cursor : this._map.project(this._lngLat);
        let anchor = this.options.anchor;
        const offset = normalizeOffset(this.options.offset);
        if (!anchor) {
            const width = this._container.offsetWidth;
            const height = this._container.offsetHeight;
            let anchorComponents;
            if (pos.y + offset.bottom.y < height) {
                anchorComponents = ['top'];
            } else if (pos.y > this._map.transform.height - height) {
                anchorComponents = ['bottom'];
            } else {
                anchorComponents = [];
            }
            if (pos.x < width / 2) {
                anchorComponents.push('left');
            } else if (pos.x > this._map.transform.width - width / 2) {
                anchorComponents.push('right');
            }
            if (anchorComponents.length === 0) {
                anchor = 'bottom';
            } else {
                anchor = anchorComponents.join('-');
            }
        }
        const offsetedPos = pos.add(offset[anchor]).round();
        this._map._requestDomTask(() => {
            if (this._container && anchor) {
                DOM.setTransform(this._container, `${ anchorTranslate[anchor] } translate(${ offsetedPos.x }px,${ offsetedPos.y }px)`);
                applyAnchorClass(this._container, anchor, 'popup');
            }
        });
    }
    _focusFirstElement() {
        if (!this.options.focusAfterOpen || !this._container)
            return;
        const firstFocusable = this._container.querySelector(focusQuerySelector);
        if (firstFocusable)
            firstFocusable.focus();
    }
    _onClose() {
        this.remove();
    }
    _setOpacity(opacity) {
        if (this._content)
            this._content.style.opacity = opacity;
        if (this._tip)
            this._tip.style.opacity = opacity;
    }
}
function normalizeOffset(offset) {
    if (!offset) {
        return normalizeOffset(new ref_properties.pointGeometry(0, 0));
    } else if (typeof offset === 'number') {
        const cornerOffset = Math.round(Math.sqrt(0.5 * Math.pow(offset, 2)));
        return {
            'center': new ref_properties.pointGeometry(0, 0),
            'top': new ref_properties.pointGeometry(0, offset),
            'top-left': new ref_properties.pointGeometry(cornerOffset, cornerOffset),
            'top-right': new ref_properties.pointGeometry(-cornerOffset, cornerOffset),
            'bottom': new ref_properties.pointGeometry(0, -offset),
            'bottom-left': new ref_properties.pointGeometry(cornerOffset, -cornerOffset),
            'bottom-right': new ref_properties.pointGeometry(-cornerOffset, -cornerOffset),
            'left': new ref_properties.pointGeometry(offset, 0),
            'right': new ref_properties.pointGeometry(-offset, 0)
        };
    } else if (offset instanceof ref_properties.pointGeometry || Array.isArray(offset)) {
        const convertedOffset = ref_properties.pointGeometry.convert(offset);
        return {
            'center': convertedOffset,
            'top': convertedOffset,
            'top-left': convertedOffset,
            'top-right': convertedOffset,
            'bottom': convertedOffset,
            'bottom-left': convertedOffset,
            'bottom-right': convertedOffset,
            'left': convertedOffset,
            'right': convertedOffset
        };
    } else {
        return {
            'center': ref_properties.pointGeometry.convert(offset['center'] || [
                0,
                0
            ]),
            'top': ref_properties.pointGeometry.convert(offset['top'] || [
                0,
                0
            ]),
            'top-left': ref_properties.pointGeometry.convert(offset['top-left'] || [
                0,
                0
            ]),
            'top-right': ref_properties.pointGeometry.convert(offset['top-right'] || [
                0,
                0
            ]),
            'bottom': ref_properties.pointGeometry.convert(offset['bottom'] || [
                0,
                0
            ]),
            'bottom-left': ref_properties.pointGeometry.convert(offset['bottom-left'] || [
                0,
                0
            ]),
            'bottom-right': ref_properties.pointGeometry.convert(offset['bottom-right'] || [
                0,
                0
            ]),
            'left': ref_properties.pointGeometry.convert(offset['left'] || [
                0,
                0
            ]),
            'right': ref_properties.pointGeometry.convert(offset['right'] || [
                0,
                0
            ])
        };
    }
}

ref_properties.window.performance;

const exported = {
    version: ref_properties.version,
    supported,
    setRTLTextPlugin: ref_properties.setRTLTextPlugin,
    getRTLTextPluginStatus: ref_properties.getRTLTextPluginStatus,
    Map,
    NavigationControl,
    GeolocateControl,
    AttributionControl,
    ScaleControl,
    FullscreenControl,
    Popup,
    Marker,
    Style,
    LngLat: ref_properties.LngLat,
    LngLatBounds: ref_properties.LngLatBounds,
    Point: ref_properties.pointGeometry,
    MercatorCoordinate: ref_properties.MercatorCoordinate,
    FreeCameraOptions,
    Evented: ref_properties.Evented,
    config: ref_properties.config,
    prewarm,
    clearPrewarmedResources,
    get accessToken() {
        return ref_properties.config.ACCESS_TOKEN;
    },
    set accessToken(token) {
        ref_properties.config.ACCESS_TOKEN = token;
    },
    get baseApiUrl() {
        return ref_properties.config.API_URL;
    },
    set baseApiUrl(url) {
        ref_properties.config.API_URL = url;
    },
    get workerCount() {
        return WorkerPool.workerCount;
    },
    set workerCount(count) {
        WorkerPool.workerCount = count;
    },
    get maxParallelImageRequests() {
        return ref_properties.config.MAX_PARALLEL_IMAGE_REQUESTS;
    },
    set maxParallelImageRequests(numRequests) {
        ref_properties.config.MAX_PARALLEL_IMAGE_REQUESTS = numRequests;
    },
    clearStorage(callback) {
        ref_properties.clearTileCache(callback);
    },
    workerUrl: '',
    workerClass: null,
    setNow: ref_properties.exported.setNow,
    restoreNow: ref_properties.exported.restoreNow
};

return exported;

}));

//

var mapboxgl$1 = mapboxgl;

return mapboxgl$1;

}));
//# sourceMappingURL=geouni-mapbox.js.map
