var ol_qrcdr = function() {
    "use strict";
    class BaseEvent {
        constructor(type) {
            this.propagationStopped;
            this.defaultPrevented;
            this.type = type;
            this.target = null;
        }
        preventDefault() {
            this.defaultPrevented = true;
        }
        stopPropagation() {
            this.propagationStopped = true;
        }
    }
    var ObjectEventType = {
        PROPERTYCHANGE: "propertychange"
    };
    class Disposable {
        constructor() {
            this.disposed = false;
        }
        dispose() {
            if (!this.disposed) {
                this.disposed = true;
                this.disposeInternal();
            }
        }
        disposeInternal() {}
    }
    function binarySearch(haystack, needle, comparator) {
        let mid, cmp;
        comparator = comparator || ascending;
        let low = 0;
        let high = haystack.length;
        let found = false;
        while (low < high) {
            mid = low + (high - low >> 1);
            cmp = +comparator(haystack[mid], needle);
            if (cmp < 0) {
                low = mid + 1;
            } else {
                high = mid;
                found = !cmp;
            }
        }
        return found ? low : ~low;
    }
    function ascending(a, b) {
        return a > b ? 1 : a < b ? -1 : 0;
    }
    function linearFindNearest(arr, target, direction) {
        if (arr[0] <= target) {
            return 0;
        }
        const n = arr.length;
        if (target <= arr[n - 1]) {
            return n - 1;
        }
        if (typeof direction === "function") {
            for (let i = 1; i < n; ++i) {
                const candidate = arr[i];
                if (candidate === target) {
                    return i;
                }
                if (candidate < target) {
                    if (direction(target, arr[i - 1], candidate) > 0) {
                        return i - 1;
                    }
                    return i;
                }
            }
            return n - 1;
        }
        if (direction > 0) {
            for (let i = 1; i < n; ++i) {
                if (arr[i] < target) {
                    return i - 1;
                }
            }
            return n - 1;
        }
        if (direction < 0) {
            for (let i = 1; i < n; ++i) {
                if (arr[i] <= target) {
                    return i;
                }
            }
            return n - 1;
        }
        for (let i = 1; i < n; ++i) {
            if (arr[i] == target) {
                return i;
            }
            if (arr[i] < target) {
                if (arr[i - 1] - target < target - arr[i]) {
                    return i - 1;
                }
                return i;
            }
        }
        return n - 1;
    }
    function reverseSubArray(arr, begin, end) {
        while (begin < end) {
            const tmp = arr[begin];
            arr[begin] = arr[end];
            arr[end] = tmp;
            ++begin;
            --end;
        }
    }
    function extend$2(arr, data) {
        const extension = Array.isArray(data) ? data : [ data ];
        const length = extension.length;
        for (let i = 0; i < length; i++) {
            arr[arr.length] = extension[i];
        }
    }
    function equals$2(arr1, arr2) {
        const len1 = arr1.length;
        if (len1 !== arr2.length) {
            return false;
        }
        for (let i = 0; i < len1; i++) {
            if (arr1[i] !== arr2[i]) {
                return false;
            }
        }
        return true;
    }
    function isSorted(arr, func, strict) {
        const compare = func || ascending;
        return arr.every(function(currentVal, index) {
            if (index === 0) {
                return true;
            }
            const res = compare(arr[index - 1], currentVal);
            return !(res > 0 || res === 0);
        });
    }
    function TRUE() {
        return true;
    }
    function FALSE() {
        return false;
    }
    function VOID() {}
    function memoizeOne(fn) {
        let called = false;
        let lastResult;
        let lastArgs;
        let lastThis;
        return function() {
            const nextArgs = Array.prototype.slice.call(arguments);
            if (!called || this !== lastThis || !equals$2(nextArgs, lastArgs)) {
                called = true;
                lastThis = this;
                lastArgs = nextArgs;
                lastResult = fn.apply(this, arguments);
            }
            return lastResult;
        };
    }
    function toPromise(getter) {
        function promiseGetter() {
            let value;
            try {
                value = getter();
            } catch (err) {
                return Promise.reject(err);
            }
            if (value instanceof Promise) {
                return value;
            }
            return Promise.resolve(value);
        }
        return promiseGetter();
    }
    function clear(object) {
        for (const property in object) {
            delete object[property];
        }
    }
    function isEmpty$1(object) {
        let property;
        for (property in object) {
            return false;
        }
        return !property;
    }
    class Target extends Disposable {
        constructor(target) {
            super();
            this.eventTarget_ = target;
            this.pendingRemovals_ = null;
            this.dispatching_ = null;
            this.listeners_ = null;
        }
        addEventListener(type, listener) {
            if (!type || !listener) {
                return;
            }
            const listeners = this.listeners_ || (this.listeners_ = {});
            const listenersForType = listeners[type] || (listeners[type] = []);
            if (!listenersForType.includes(listener)) {
                listenersForType.push(listener);
            }
        }
        dispatchEvent(event) {
            const isString = typeof event === "string";
            const type = isString ? event : event.type;
            const listeners = this.listeners_ && this.listeners_[type];
            if (!listeners) {
                return;
            }
            const evt = isString ? new BaseEvent(event) : event;
            if (!evt.target) {
                evt.target = this.eventTarget_ || this;
            }
            const dispatching = this.dispatching_ || (this.dispatching_ = {});
            const pendingRemovals = this.pendingRemovals_ || (this.pendingRemovals_ = {});
            if (!(type in dispatching)) {
                dispatching[type] = 0;
                pendingRemovals[type] = 0;
            }
            ++dispatching[type];
            let propagate;
            for (let i = 0, ii = listeners.length; i < ii; ++i) {
                if ("handleEvent" in listeners[i]) {
                    propagate = listeners[i].handleEvent(evt);
                } else {
                    propagate = listeners[i].call(this, evt);
                }
                if (propagate === false || evt.propagationStopped) {
                    propagate = false;
                    break;
                }
            }
            if (--dispatching[type] === 0) {
                let pr = pendingRemovals[type];
                delete pendingRemovals[type];
                while (pr--) {
                    this.removeEventListener(type, VOID);
                }
                delete dispatching[type];
            }
            return propagate;
        }
        disposeInternal() {
            this.listeners_ && clear(this.listeners_);
        }
        getListeners(type) {
            return this.listeners_ && this.listeners_[type] || undefined;
        }
        hasListener(type) {
            if (!this.listeners_) {
                return false;
            }
            return type ? type in this.listeners_ : Object.keys(this.listeners_).length > 0;
        }
        removeEventListener(type, listener) {
            if (!this.listeners_) {
                return;
            }
            const listeners = this.listeners_[type];
            if (!listeners) {
                return;
            }
            const index = listeners.indexOf(listener);
            if (index !== -1) {
                if (this.pendingRemovals_ && type in this.pendingRemovals_) {
                    listeners[index] = VOID;
                    ++this.pendingRemovals_[type];
                } else {
                    listeners.splice(index, 1);
                    if (listeners.length === 0) {
                        delete this.listeners_[type];
                    }
                }
            }
        }
    }
    var EventType = {
        CHANGE: "change",
        ERROR: "error",
        CONTEXTMENU: "contextmenu",
        CLICK: "click",
        DBLCLICK: "dblclick",
        KEYDOWN: "keydown",
        KEYPRESS: "keypress",
        LOAD: "load",
        TOUCHMOVE: "touchmove",
        WHEEL: "wheel"
    };
    function listen(target, type, listener, thisArg, once) {
        if (thisArg && thisArg !== target) {
            listener = listener.bind(thisArg);
        }
        if (once) {
            const originalListener = listener;
            listener = function() {
                target.removeEventListener(type, listener);
                originalListener.apply(this, arguments);
            };
        }
        const eventsKey = {
            target: target,
            type: type,
            listener: listener
        };
        target.addEventListener(type, listener);
        return eventsKey;
    }
    function listenOnce(target, type, listener, thisArg) {
        return listen(target, type, listener, thisArg, true);
    }
    function unlistenByKey(key) {
        if (key && key.target) {
            key.target.removeEventListener(key.type, key.listener);
            clear(key);
        }
    }
    class Observable extends Target {
        constructor() {
            super();
            this.on = this.onInternal;
            this.once = this.onceInternal;
            this.un = this.unInternal;
            this.revision_ = 0;
        }
        changed() {
            ++this.revision_;
            this.dispatchEvent(EventType.CHANGE);
        }
        getRevision() {
            return this.revision_;
        }
        onInternal(type, listener) {
            if (Array.isArray(type)) {
                const len = type.length;
                const keys = new Array(len);
                for (let i = 0; i < len; ++i) {
                    keys[i] = listen(this, type[i], listener);
                }
                return keys;
            }
            return listen(this, type, listener);
        }
        onceInternal(type, listener) {
            let key;
            if (Array.isArray(type)) {
                const len = type.length;
                key = new Array(len);
                for (let i = 0; i < len; ++i) {
                    key[i] = listenOnce(this, type[i], listener);
                }
            } else {
                key = listenOnce(this, type, listener);
            }
            listener.ol_key = key;
            return key;
        }
        unInternal(type, listener) {
            const key = listener.ol_key;
            if (key) {
                unByKey(key);
            } else if (Array.isArray(type)) {
                for (let i = 0, ii = type.length; i < ii; ++i) {
                    this.removeEventListener(type[i], listener);
                }
            } else {
                this.removeEventListener(type, listener);
            }
        }
    }
    Observable.prototype.on;
    Observable.prototype.once;
    Observable.prototype.un;
    function unByKey(key) {
        if (Array.isArray(key)) {
            for (let i = 0, ii = key.length; i < ii; ++i) {
                unlistenByKey(key[i]);
            }
        } else {
            unlistenByKey(key);
        }
    }
    function abstract() {
        throw new Error("Unimplemented abstract method.");
    }
    let uidCounter_ = 0;
    function getUid(obj) {
        return obj.ol_uid || (obj.ol_uid = String(++uidCounter_));
    }
    class ObjectEvent extends BaseEvent {
        constructor(type, key, oldValue) {
            super(type);
            this.key = key;
            this.oldValue = oldValue;
        }
    }
    class BaseObject extends Observable {
        constructor(values) {
            super();
            this.on;
            this.once;
            this.un;
            getUid(this);
            this.values_ = null;
            if (values !== undefined) {
                this.setProperties(values);
            }
        }
        get(key) {
            let value;
            if (this.values_ && this.values_.hasOwnProperty(key)) {
                value = this.values_[key];
            }
            return value;
        }
        getKeys() {
            return this.values_ && Object.keys(this.values_) || [];
        }
        getProperties() {
            return this.values_ && Object.assign({}, this.values_) || {};
        }
        getPropertiesInternal() {
            return this.values_;
        }
        hasProperties() {
            return !!this.values_;
        }
        notify(key, oldValue) {
            let eventType;
            eventType = `change:${key}`;
            if (this.hasListener(eventType)) {
                this.dispatchEvent(new ObjectEvent(eventType, key, oldValue));
            }
            eventType = ObjectEventType.PROPERTYCHANGE;
            if (this.hasListener(eventType)) {
                this.dispatchEvent(new ObjectEvent(eventType, key, oldValue));
            }
        }
        addChangeListener(key, listener) {
            this.addEventListener(`change:${key}`, listener);
        }
        removeChangeListener(key, listener) {
            this.removeEventListener(`change:${key}`, listener);
        }
        set(key, value, silent) {
            const values = this.values_ || (this.values_ = {});
            if (silent) {
                values[key] = value;
            } else {
                const oldValue = values[key];
                values[key] = value;
                if (oldValue !== value) {
                    this.notify(key, oldValue);
                }
            }
        }
        setProperties(values, silent) {
            for (const key in values) {
                this.set(key, values[key], silent);
            }
        }
        applyProperties(source) {
            if (!source.values_) {
                return;
            }
            Object.assign(this.values_ || (this.values_ = {}), source.values_);
        }
        unset(key, silent) {
            if (this.values_ && key in this.values_) {
                const oldValue = this.values_[key];
                delete this.values_[key];
                if (isEmpty$1(this.values_)) {
                    this.values_ = null;
                }
                if (!silent) {
                    this.notify(key, oldValue);
                }
            }
        }
    }
    var CollectionEventType = {
        ADD: "add",
        REMOVE: "remove"
    };
    const Property$2 = {
        LENGTH: "length"
    };
    class CollectionEvent extends BaseEvent {
        constructor(type, element, index) {
            super(type);
            this.element = element;
            this.index = index;
        }
    }
    class Collection extends BaseObject {
        constructor(array, options) {
            super();
            this.on;
            this.once;
            this.un;
            options = options || {};
            this.unique_ = !!options.unique;
            this.array_ = array ? array : [];
            if (this.unique_) {
                for (let i = 0, ii = this.array_.length; i < ii; ++i) {
                    this.assertUnique_(this.array_[i], i);
                }
            }
            this.updateLength_();
        }
        clear() {
            while (this.getLength() > 0) {
                this.pop();
            }
        }
        extend(arr) {
            for (let i = 0, ii = arr.length; i < ii; ++i) {
                this.push(arr[i]);
            }
            return this;
        }
        forEach(f) {
            const array = this.array_;
            for (let i = 0, ii = array.length; i < ii; ++i) {
                f(array[i], i, array);
            }
        }
        getArray() {
            return this.array_;
        }
        item(index) {
            return this.array_[index];
        }
        getLength() {
            return this.get(Property$2.LENGTH);
        }
        insertAt(index, elem) {
            if (index < 0 || index > this.getLength()) {
                throw new Error("Index out of bounds: " + index);
            }
            if (this.unique_) {
                this.assertUnique_(elem);
            }
            this.array_.splice(index, 0, elem);
            this.updateLength_();
            this.dispatchEvent(new CollectionEvent(CollectionEventType.ADD, elem, index));
        }
        pop() {
            return this.removeAt(this.getLength() - 1);
        }
        push(elem) {
            if (this.unique_) {
                this.assertUnique_(elem);
            }
            const n = this.getLength();
            this.insertAt(n, elem);
            return this.getLength();
        }
        remove(elem) {
            const arr = this.array_;
            for (let i = 0, ii = arr.length; i < ii; ++i) {
                if (arr[i] === elem) {
                    return this.removeAt(i);
                }
            }
            return undefined;
        }
        removeAt(index) {
            if (index < 0 || index >= this.getLength()) {
                return undefined;
            }
            const prev = this.array_[index];
            this.array_.splice(index, 1);
            this.updateLength_();
            this.dispatchEvent(new CollectionEvent(CollectionEventType.REMOVE, prev, index));
            return prev;
        }
        setAt(index, elem) {
            const n = this.getLength();
            if (index >= n) {
                this.insertAt(index, elem);
                return;
            }
            if (index < 0) {
                throw new Error("Index out of bounds: " + index);
            }
            if (this.unique_) {
                this.assertUnique_(elem, index);
            }
            const prev = this.array_[index];
            this.array_[index] = elem;
            this.dispatchEvent(new CollectionEvent(CollectionEventType.REMOVE, prev, index));
            this.dispatchEvent(new CollectionEvent(CollectionEventType.ADD, elem, index));
        }
        updateLength_() {
            this.set(Property$2.LENGTH, this.array_.length);
        }
        assertUnique_(elem, except) {
            for (let i = 0, ii = this.array_.length; i < ii; ++i) {
                if (this.array_[i] === elem && i !== except) {
                    throw new Error("Duplicate item added to a unique collection");
                }
            }
        }
    }
    var LayerProperty = {
        OPACITY: "opacity",
        VISIBLE: "visible",
        EXTENT: "extent",
        Z_INDEX: "zIndex",
        MAX_RESOLUTION: "maxResolution",
        MIN_RESOLUTION: "minResolution",
        MAX_ZOOM: "maxZoom",
        MIN_ZOOM: "minZoom",
        SOURCE: "source",
        MAP: "map"
    };
    function assert(assertion, errorMessage) {
        if (!assertion) {
            throw new Error(errorMessage);
        }
    }
    function clamp(value, min, max) {
        return Math.min(Math.max(value, min), max);
    }
    function squaredSegmentDistance(x, y, x1, y1, x2, y2) {
        const dx = x2 - x1;
        const dy = y2 - y1;
        if (dx !== 0 || dy !== 0) {
            const t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy);
            if (t > 1) {
                x1 = x2;
                y1 = y2;
            } else if (t > 0) {
                x1 += dx * t;
                y1 += dy * t;
            }
        }
        return squaredDistance(x, y, x1, y1);
    }
    function squaredDistance(x1, y1, x2, y2) {
        const dx = x2 - x1;
        const dy = y2 - y1;
        return dx * dx + dy * dy;
    }
    function solveLinearSystem(mat) {
        const n = mat.length;
        for (let i = 0; i < n; i++) {
            let maxRow = i;
            let maxEl = Math.abs(mat[i][i]);
            for (let r = i + 1; r < n; r++) {
                const absValue = Math.abs(mat[r][i]);
                if (absValue > maxEl) {
                    maxEl = absValue;
                    maxRow = r;
                }
            }
            if (maxEl === 0) {
                return null;
            }
            const tmp = mat[maxRow];
            mat[maxRow] = mat[i];
            mat[i] = tmp;
            for (let j = i + 1; j < n; j++) {
                const coef = -mat[j][i] / mat[i][i];
                for (let k = i; k < n + 1; k++) {
                    if (i == k) {
                        mat[j][k] = 0;
                    } else {
                        mat[j][k] += coef * mat[i][k];
                    }
                }
            }
        }
        const x = new Array(n);
        for (let l = n - 1; l >= 0; l--) {
            x[l] = mat[l][n] / mat[l][l];
            for (let m = l - 1; m >= 0; m--) {
                mat[m][n] -= mat[m][l] * x[l];
            }
        }
        return x;
    }
    function toRadians(angleInDegrees) {
        return angleInDegrees * Math.PI / 180;
    }
    function modulo(a, b) {
        const r = a % b;
        return r * b < 0 ? r + b : r;
    }
    function lerp(a, b, x) {
        return a + x * (b - a);
    }
    function toFixed(n, decimals) {
        const factor = Math.pow(10, decimals);
        return Math.round(n * factor) / factor;
    }
    function floor(n, decimals) {
        return Math.floor(toFixed(n, decimals));
    }
    function ceil(n, decimals) {
        return Math.ceil(toFixed(n, decimals));
    }
    class BaseLayer extends BaseObject {
        constructor(options) {
            super();
            this.on;
            this.once;
            this.un;
            this.background_ = options.background;
            const properties = Object.assign({}, options);
            if (typeof options.properties === "object") {
                delete properties.properties;
                Object.assign(properties, options.properties);
            }
            properties[LayerProperty.OPACITY] = options.opacity !== undefined ? options.opacity : 1;
            assert(typeof properties[LayerProperty.OPACITY] === "number", "Layer opacity must be a number");
            properties[LayerProperty.VISIBLE] = options.visible !== undefined ? options.visible : true;
            properties[LayerProperty.Z_INDEX] = options.zIndex;
            properties[LayerProperty.MAX_RESOLUTION] = options.maxResolution !== undefined ? options.maxResolution : Infinity;
            properties[LayerProperty.MIN_RESOLUTION] = options.minResolution !== undefined ? options.minResolution : 0;
            properties[LayerProperty.MIN_ZOOM] = options.minZoom !== undefined ? options.minZoom : -Infinity;
            properties[LayerProperty.MAX_ZOOM] = options.maxZoom !== undefined ? options.maxZoom : Infinity;
            this.className_ = properties.className !== undefined ? properties.className : "ol-layer";
            delete properties.className;
            this.setProperties(properties);
            this.state_ = null;
        }
        getBackground() {
            return this.background_;
        }
        getClassName() {
            return this.className_;
        }
        getLayerState(managed) {
            const state = this.state_ || {
                layer: this,
                managed: managed === undefined ? true : managed
            };
            const zIndex = this.getZIndex();
            state.opacity = clamp(Math.round(this.getOpacity() * 100) / 100, 0, 1);
            state.visible = this.getVisible();
            state.extent = this.getExtent();
            state.zIndex = zIndex === undefined && !state.managed ? Infinity : zIndex;
            state.maxResolution = this.getMaxResolution();
            state.minResolution = Math.max(this.getMinResolution(), 0);
            state.minZoom = this.getMinZoom();
            state.maxZoom = this.getMaxZoom();
            this.state_ = state;
            return state;
        }
        getLayersArray(array) {
            return abstract();
        }
        getLayerStatesArray(states) {
            return abstract();
        }
        getExtent() {
            return this.get(LayerProperty.EXTENT);
        }
        getMaxResolution() {
            return this.get(LayerProperty.MAX_RESOLUTION);
        }
        getMinResolution() {
            return this.get(LayerProperty.MIN_RESOLUTION);
        }
        getMinZoom() {
            return this.get(LayerProperty.MIN_ZOOM);
        }
        getMaxZoom() {
            return this.get(LayerProperty.MAX_ZOOM);
        }
        getOpacity() {
            return this.get(LayerProperty.OPACITY);
        }
        getSourceState() {
            return abstract();
        }
        getVisible() {
            return this.get(LayerProperty.VISIBLE);
        }
        getZIndex() {
            return this.get(LayerProperty.Z_INDEX);
        }
        setBackground(background) {
            this.background_ = background;
            this.changed();
        }
        setExtent(extent) {
            this.set(LayerProperty.EXTENT, extent);
        }
        setMaxResolution(maxResolution) {
            this.set(LayerProperty.MAX_RESOLUTION, maxResolution);
        }
        setMinResolution(minResolution) {
            this.set(LayerProperty.MIN_RESOLUTION, minResolution);
        }
        setMaxZoom(maxZoom) {
            this.set(LayerProperty.MAX_ZOOM, maxZoom);
        }
        setMinZoom(minZoom) {
            this.set(LayerProperty.MIN_ZOOM, minZoom);
        }
        setOpacity(opacity) {
            assert(typeof opacity === "number", "Layer opacity must be a number");
            this.set(LayerProperty.OPACITY, opacity);
        }
        setVisible(visible) {
            this.set(LayerProperty.VISIBLE, visible);
        }
        setZIndex(zindex) {
            this.set(LayerProperty.Z_INDEX, zindex);
        }
        disposeInternal() {
            if (this.state_) {
                this.state_.layer = null;
                this.state_ = null;
            }
            super.disposeInternal();
        }
    }
    var RenderEventType = {
        PRERENDER: "prerender",
        POSTRENDER: "postrender",
        PRECOMPOSE: "precompose",
        POSTCOMPOSE: "postcompose",
        RENDERCOMPLETE: "rendercomplete"
    };
    var ViewHint = {
        ANIMATING: 0,
        INTERACTING: 1
    };
    var ViewProperty = {
        CENTER: "center",
        RESOLUTION: "resolution",
        ROTATION: "rotation"
    };
    const DEFAULT_MAX_ZOOM = 42;
    const DEFAULT_TILE_SIZE = 256;
    const METERS_PER_UNIT$1 = {
        radians: 6370997 / (2 * Math.PI),
        degrees: 2 * Math.PI * 6370997 / 360,
        ft: .3048,
        m: 1,
        "us-ft": 1200 / 3937
    };
    class Projection {
        constructor(options) {
            this.code_ = options.code;
            this.units_ = options.units;
            this.extent_ = options.extent !== undefined ? options.extent : null;
            this.worldExtent_ = options.worldExtent !== undefined ? options.worldExtent : null;
            this.axisOrientation_ = options.axisOrientation !== undefined ? options.axisOrientation : "enu";
            this.global_ = options.global !== undefined ? options.global : false;
            this.canWrapX_ = !!(this.global_ && this.extent_);
            this.getPointResolutionFunc_ = options.getPointResolution;
            this.defaultTileGrid_ = null;
            this.metersPerUnit_ = options.metersPerUnit;
        }
        canWrapX() {
            return this.canWrapX_;
        }
        getCode() {
            return this.code_;
        }
        getExtent() {
            return this.extent_;
        }
        getUnits() {
            return this.units_;
        }
        getMetersPerUnit() {
            return this.metersPerUnit_ || METERS_PER_UNIT$1[this.units_];
        }
        getWorldExtent() {
            return this.worldExtent_;
        }
        getAxisOrientation() {
            return this.axisOrientation_;
        }
        isGlobal() {
            return this.global_;
        }
        setGlobal(global) {
            this.global_ = global;
            this.canWrapX_ = !!(global && this.extent_);
        }
        getDefaultTileGrid() {
            return this.defaultTileGrid_;
        }
        setDefaultTileGrid(tileGrid) {
            this.defaultTileGrid_ = tileGrid;
        }
        setExtent(extent) {
            this.extent_ = extent;
            this.canWrapX_ = !!(this.global_ && extent);
        }
        setWorldExtent(worldExtent) {
            this.worldExtent_ = worldExtent;
        }
        setGetPointResolution(func) {
            this.getPointResolutionFunc_ = func;
        }
        getPointResolutionFunc() {
            return this.getPointResolutionFunc_;
        }
    }
    const RADIUS$1 = 6378137;
    const HALF_SIZE = Math.PI * RADIUS$1;
    const EXTENT$1 = [ -HALF_SIZE, -HALF_SIZE, HALF_SIZE, HALF_SIZE ];
    const WORLD_EXTENT = [ -180, -85, 180, 85 ];
    const MAX_SAFE_Y = RADIUS$1 * Math.log(Math.tan(Math.PI / 2));
    class EPSG3857Projection extends Projection {
        constructor(code) {
            super({
                code: code,
                units: "m",
                extent: EXTENT$1,
                global: true,
                worldExtent: WORLD_EXTENT,
                getPointResolution: function(resolution, point) {
                    return resolution / Math.cosh(point[1] / RADIUS$1);
                }
            });
        }
    }
    const PROJECTIONS$1 = [ new EPSG3857Projection("EPSG:3857"), new EPSG3857Projection("EPSG:102100"), new EPSG3857Projection("EPSG:102113"), new EPSG3857Projection("EPSG:900913"), new EPSG3857Projection("http://www.opengis.net/def/crs/EPSG/0/3857"), new EPSG3857Projection("http://www.opengis.net/gml/srs/epsg.xml#3857") ];
    function fromEPSG4326(input, output, dimension) {
        const length = input.length;
        dimension = dimension > 1 ? dimension : 2;
        if (output === undefined) {
            if (dimension > 2) {
                output = input.slice();
            } else {
                output = new Array(length);
            }
        }
        for (let i = 0; i < length; i += dimension) {
            output[i] = HALF_SIZE * input[i] / 180;
            let y = RADIUS$1 * Math.log(Math.tan(Math.PI * (+input[i + 1] + 90) / 360));
            if (y > MAX_SAFE_Y) {
                y = MAX_SAFE_Y;
            } else if (y < -MAX_SAFE_Y) {
                y = -MAX_SAFE_Y;
            }
            output[i + 1] = y;
        }
        return output;
    }
    function toEPSG4326(input, output, dimension) {
        const length = input.length;
        dimension = dimension > 1 ? dimension : 2;
        if (output === undefined) {
            if (dimension > 2) {
                output = input.slice();
            } else {
                output = new Array(length);
            }
        }
        for (let i = 0; i < length; i += dimension) {
            output[i] = 180 * input[i] / HALF_SIZE;
            output[i + 1] = 360 * Math.atan(Math.exp(input[i + 1] / RADIUS$1)) / Math.PI - 90;
        }
        return output;
    }
    const RADIUS = 6378137;
    const EXTENT = [ -180, -90, 180, 90 ];
    const METERS_PER_UNIT = Math.PI * RADIUS / 180;
    class EPSG4326Projection extends Projection {
        constructor(code, axisOrientation) {
            super({
                code: code,
                units: "degrees",
                extent: EXTENT,
                axisOrientation: axisOrientation,
                global: true,
                metersPerUnit: METERS_PER_UNIT,
                worldExtent: EXTENT
            });
        }
    }
    const PROJECTIONS = [ new EPSG4326Projection("CRS:84"), new EPSG4326Projection("EPSG:4326", "neu"), new EPSG4326Projection("urn:ogc:def:crs:OGC:1.3:CRS84"), new EPSG4326Projection("urn:ogc:def:crs:OGC:2:84"), new EPSG4326Projection("http://www.opengis.net/def/crs/OGC/1.3/CRS84"), new EPSG4326Projection("http://www.opengis.net/gml/srs/epsg.xml#4326", "neu"), new EPSG4326Projection("http://www.opengis.net/def/crs/EPSG/0/4326", "neu") ];
    let cache$1 = {};
    function get$3(code) {
        return cache$1[code] || cache$1[code.replace(/urn:(x-)?ogc:def:crs:EPSG:(.*:)?(\w+)$/, "EPSG:$3")] || null;
    }
    function add$2(code, projection) {
        cache$1[code] = projection;
    }
    let transforms = {};
    function add$1(source, destination, transformFn) {
        const sourceCode = source.getCode();
        const destinationCode = destination.getCode();
        if (!(sourceCode in transforms)) {
            transforms[sourceCode] = {};
        }
        transforms[sourceCode][destinationCode] = transformFn;
    }
    function get$2(sourceCode, destinationCode) {
        let transform;
        if (sourceCode in transforms && destinationCode in transforms[sourceCode]) {
            transform = transforms[sourceCode][destinationCode];
        }
        return transform;
    }
    var Relationship = {
        UNKNOWN: 0,
        INTERSECTING: 1,
        ABOVE: 2,
        RIGHT: 4,
        BELOW: 8,
        LEFT: 16
    };
    function boundingExtent(coordinates) {
        const extent = createEmpty();
        for (let i = 0, ii = coordinates.length; i < ii; ++i) {
            extendCoordinate(extent, coordinates[i]);
        }
        return extent;
    }
    function buffer(extent, value, dest) {
        if (dest) {
            dest[0] = extent[0] - value;
            dest[1] = extent[1] - value;
            dest[2] = extent[2] + value;
            dest[3] = extent[3] + value;
            return dest;
        }
        return [ extent[0] - value, extent[1] - value, extent[2] + value, extent[3] + value ];
    }
    function clone(extent, dest) {
        if (dest) {
            dest[0] = extent[0];
            dest[1] = extent[1];
            dest[2] = extent[2];
            dest[3] = extent[3];
            return dest;
        }
        return extent.slice();
    }
    function closestSquaredDistanceXY(extent, x, y) {
        let dx, dy;
        if (x < extent[0]) {
            dx = extent[0] - x;
        } else if (extent[2] < x) {
            dx = x - extent[2];
        } else {
            dx = 0;
        }
        if (y < extent[1]) {
            dy = extent[1] - y;
        } else if (extent[3] < y) {
            dy = y - extent[3];
        } else {
            dy = 0;
        }
        return dx * dx + dy * dy;
    }
    function containsCoordinate(extent, coordinate) {
        return containsXY(extent, coordinate[0], coordinate[1]);
    }
    function containsExtent(extent1, extent2) {
        return extent1[0] <= extent2[0] && extent2[2] <= extent1[2] && extent1[1] <= extent2[1] && extent2[3] <= extent1[3];
    }
    function containsXY(extent, x, y) {
        return extent[0] <= x && x <= extent[2] && extent[1] <= y && y <= extent[3];
    }
    function coordinateRelationship(extent, coordinate) {
        const minX = extent[0];
        const minY = extent[1];
        const maxX = extent[2];
        const maxY = extent[3];
        const x = coordinate[0];
        const y = coordinate[1];
        let relationship = Relationship.UNKNOWN;
        if (x < minX) {
            relationship = relationship | Relationship.LEFT;
        } else if (x > maxX) {
            relationship = relationship | Relationship.RIGHT;
        }
        if (y < minY) {
            relationship = relationship | Relationship.BELOW;
        } else if (y > maxY) {
            relationship = relationship | Relationship.ABOVE;
        }
        if (relationship === Relationship.UNKNOWN) {
            relationship = Relationship.INTERSECTING;
        }
        return relationship;
    }
    function createEmpty() {
        return [ Infinity, Infinity, -Infinity, -Infinity ];
    }
    function createOrUpdate$2(minX, minY, maxX, maxY, dest) {
        if (dest) {
            dest[0] = minX;
            dest[1] = minY;
            dest[2] = maxX;
            dest[3] = maxY;
            return dest;
        }
        return [ minX, minY, maxX, maxY ];
    }
    function createOrUpdateEmpty(dest) {
        return createOrUpdate$2(Infinity, Infinity, -Infinity, -Infinity, dest);
    }
    function createOrUpdateFromCoordinate(coordinate, dest) {
        const x = coordinate[0];
        const y = coordinate[1];
        return createOrUpdate$2(x, y, x, y, dest);
    }
    function createOrUpdateFromFlatCoordinates(flatCoordinates, offset, end, stride, dest) {
        const extent = createOrUpdateEmpty(dest);
        return extendFlatCoordinates(extent, flatCoordinates, offset, end, stride);
    }
    function equals$1(extent1, extent2) {
        return extent1[0] == extent2[0] && extent1[2] == extent2[2] && extent1[1] == extent2[1] && extent1[3] == extent2[3];
    }
    function extend$1(extent1, extent2) {
        if (extent2[0] < extent1[0]) {
            extent1[0] = extent2[0];
        }
        if (extent2[2] > extent1[2]) {
            extent1[2] = extent2[2];
        }
        if (extent2[1] < extent1[1]) {
            extent1[1] = extent2[1];
        }
        if (extent2[3] > extent1[3]) {
            extent1[3] = extent2[3];
        }
        return extent1;
    }
    function extendCoordinate(extent, coordinate) {
        if (coordinate[0] < extent[0]) {
            extent[0] = coordinate[0];
        }
        if (coordinate[0] > extent[2]) {
            extent[2] = coordinate[0];
        }
        if (coordinate[1] < extent[1]) {
            extent[1] = coordinate[1];
        }
        if (coordinate[1] > extent[3]) {
            extent[3] = coordinate[1];
        }
    }
    function extendFlatCoordinates(extent, flatCoordinates, offset, end, stride) {
        for (;offset < end; offset += stride) {
            extendXY(extent, flatCoordinates[offset], flatCoordinates[offset + 1]);
        }
        return extent;
    }
    function extendXY(extent, x, y) {
        extent[0] = Math.min(extent[0], x);
        extent[1] = Math.min(extent[1], y);
        extent[2] = Math.max(extent[2], x);
        extent[3] = Math.max(extent[3], y);
    }
    function forEachCorner(extent, callback) {
        let val;
        val = callback(getBottomLeft(extent));
        if (val) {
            return val;
        }
        val = callback(getBottomRight(extent));
        if (val) {
            return val;
        }
        val = callback(getTopRight(extent));
        if (val) {
            return val;
        }
        val = callback(getTopLeft(extent));
        if (val) {
            return val;
        }
        return false;
    }
    function getArea(extent) {
        let area = 0;
        if (!isEmpty(extent)) {
            area = getWidth(extent) * getHeight(extent);
        }
        return area;
    }
    function getBottomLeft(extent) {
        return [ extent[0], extent[1] ];
    }
    function getBottomRight(extent) {
        return [ extent[2], extent[1] ];
    }
    function getCenter(extent) {
        return [ (extent[0] + extent[2]) / 2, (extent[1] + extent[3]) / 2 ];
    }
    function getCorner(extent, corner) {
        let coordinate;
        if (corner === "bottom-left") {
            coordinate = getBottomLeft(extent);
        } else if (corner === "bottom-right") {
            coordinate = getBottomRight(extent);
        } else if (corner === "top-left") {
            coordinate = getTopLeft(extent);
        } else if (corner === "top-right") {
            coordinate = getTopRight(extent);
        } else {
            throw new Error("Invalid corner");
        }
        return coordinate;
    }
    function getForViewAndSize(center, resolution, rotation, size, dest) {
        const [x0, y0, x1, y1, x2, y2, x3, y3] = getRotatedViewport(center, resolution, rotation, size);
        return createOrUpdate$2(Math.min(x0, x1, x2, x3), Math.min(y0, y1, y2, y3), Math.max(x0, x1, x2, x3), Math.max(y0, y1, y2, y3), dest);
    }
    function getRotatedViewport(center, resolution, rotation, size) {
        const dx = resolution * size[0] / 2;
        const dy = resolution * size[1] / 2;
        const cosRotation = Math.cos(rotation);
        const sinRotation = Math.sin(rotation);
        const xCos = dx * cosRotation;
        const xSin = dx * sinRotation;
        const yCos = dy * cosRotation;
        const ySin = dy * sinRotation;
        const x = center[0];
        const y = center[1];
        return [ x - xCos + ySin, y - xSin - yCos, x - xCos - ySin, y - xSin + yCos, x + xCos - ySin, y + xSin + yCos, x + xCos + ySin, y + xSin - yCos, x - xCos + ySin, y - xSin - yCos ];
    }
    function getHeight(extent) {
        return extent[3] - extent[1];
    }
    function getIntersection(extent1, extent2, dest) {
        const intersection = dest ? dest : createEmpty();
        if (intersects$1(extent1, extent2)) {
            if (extent1[0] > extent2[0]) {
                intersection[0] = extent1[0];
            } else {
                intersection[0] = extent2[0];
            }
            if (extent1[1] > extent2[1]) {
                intersection[1] = extent1[1];
            } else {
                intersection[1] = extent2[1];
            }
            if (extent1[2] < extent2[2]) {
                intersection[2] = extent1[2];
            } else {
                intersection[2] = extent2[2];
            }
            if (extent1[3] < extent2[3]) {
                intersection[3] = extent1[3];
            } else {
                intersection[3] = extent2[3];
            }
        } else {
            createOrUpdateEmpty(intersection);
        }
        return intersection;
    }
    function getTopLeft(extent) {
        return [ extent[0], extent[3] ];
    }
    function getTopRight(extent) {
        return [ extent[2], extent[3] ];
    }
    function getWidth(extent) {
        return extent[2] - extent[0];
    }
    function intersects$1(extent1, extent2) {
        return extent1[0] <= extent2[2] && extent1[2] >= extent2[0] && extent1[1] <= extent2[3] && extent1[3] >= extent2[1];
    }
    function isEmpty(extent) {
        return extent[2] < extent[0] || extent[3] < extent[1];
    }
    function returnOrUpdate(extent, dest) {
        if (dest) {
            dest[0] = extent[0];
            dest[1] = extent[1];
            dest[2] = extent[2];
            dest[3] = extent[3];
            return dest;
        }
        return extent;
    }
    function intersectsSegment(extent, start, end) {
        let intersects = false;
        const startRel = coordinateRelationship(extent, start);
        const endRel = coordinateRelationship(extent, end);
        if (startRel === Relationship.INTERSECTING || endRel === Relationship.INTERSECTING) {
            intersects = true;
        } else {
            const minX = extent[0];
            const minY = extent[1];
            const maxX = extent[2];
            const maxY = extent[3];
            const startX = start[0];
            const startY = start[1];
            const endX = end[0];
            const endY = end[1];
            const slope = (endY - startY) / (endX - startX);
            let x, y;
            if (!!(endRel & Relationship.ABOVE) && !(startRel & Relationship.ABOVE)) {
                x = endX - (endY - maxY) / slope;
                intersects = x >= minX && x <= maxX;
            }
            if (!intersects && !!(endRel & Relationship.RIGHT) && !(startRel & Relationship.RIGHT)) {
                y = endY - (endX - maxX) * slope;
                intersects = y >= minY && y <= maxY;
            }
            if (!intersects && !!(endRel & Relationship.BELOW) && !(startRel & Relationship.BELOW)) {
                x = endX - (endY - minY) / slope;
                intersects = x >= minX && x <= maxX;
            }
            if (!intersects && !!(endRel & Relationship.LEFT) && !(startRel & Relationship.LEFT)) {
                y = endY - (endX - minX) * slope;
                intersects = y >= minY && y <= maxY;
            }
        }
        return intersects;
    }
    function wrapX$2(extent, projection) {
        const projectionExtent = projection.getExtent();
        const center = getCenter(extent);
        if (projection.canWrapX() && (center[0] < projectionExtent[0] || center[0] >= projectionExtent[2])) {
            const worldWidth = getWidth(projectionExtent);
            const worldsAway = Math.floor((center[0] - projectionExtent[0]) / worldWidth);
            const offset = worldsAway * worldWidth;
            extent[0] -= offset;
            extent[2] -= offset;
        }
        return extent;
    }
    function wrapAndSliceX(extent, projection, multiWorld) {
        if (projection.canWrapX()) {
            const projectionExtent = projection.getExtent();
            if (!isFinite(extent[0]) || !isFinite(extent[2])) {
                return [ [ projectionExtent[0], extent[1], projectionExtent[2], extent[3] ] ];
            }
            wrapX$2(extent, projection);
            const worldWidth = getWidth(projectionExtent);
            if (getWidth(extent) > worldWidth && !multiWorld) {
                return [ [ projectionExtent[0], extent[1], projectionExtent[2], extent[3] ] ];
            }
            if (extent[0] < projectionExtent[0]) {
                return [ [ extent[0] + worldWidth, extent[1], projectionExtent[2], extent[3] ], [ projectionExtent[0], extent[1], extent[2], extent[3] ] ];
            }
            if (extent[2] > projectionExtent[2]) {
                return [ [ extent[0], extent[1], projectionExtent[2], extent[3] ], [ projectionExtent[0], extent[1], extent[2] - worldWidth, extent[3] ] ];
            }
        }
        return [ extent ];
    }
    function add(coordinate, delta) {
        coordinate[0] += +delta[0];
        coordinate[1] += +delta[1];
        return coordinate;
    }
    function equals(coordinate1, coordinate2) {
        let equals = true;
        for (let i = coordinate1.length - 1; i >= 0; --i) {
            if (coordinate1[i] != coordinate2[i]) {
                equals = false;
                break;
            }
        }
        return equals;
    }
    function rotate$1(coordinate, angle) {
        const cosAngle = Math.cos(angle);
        const sinAngle = Math.sin(angle);
        const x = coordinate[0] * cosAngle - coordinate[1] * sinAngle;
        const y = coordinate[1] * cosAngle + coordinate[0] * sinAngle;
        coordinate[0] = x;
        coordinate[1] = y;
        return coordinate;
    }
    function scale$2(coordinate, scale) {
        coordinate[0] *= scale;
        coordinate[1] *= scale;
        return coordinate;
    }
    function wrapX$1(coordinate, projection) {
        if (projection.canWrapX()) {
            const worldWidth = getWidth(projection.getExtent());
            const worldsAway = getWorldsAway(coordinate, projection, worldWidth);
            if (worldsAway) {
                coordinate[0] -= worldsAway * worldWidth;
            }
        }
        return coordinate;
    }
    function getWorldsAway(coordinate, projection, sourceExtentWidth) {
        const projectionExtent = projection.getExtent();
        let worldsAway = 0;
        if (projection.canWrapX() && (coordinate[0] < projectionExtent[0] || coordinate[0] > projectionExtent[2])) {
            sourceExtentWidth = sourceExtentWidth || getWidth(projectionExtent);
            worldsAway = Math.floor((coordinate[0] - projectionExtent[0]) / sourceExtentWidth);
        }
        return worldsAway;
    }
    const DEFAULT_RADIUS = 6371008.8;
    function getDistance(c1, c2, radius) {
        radius = radius || DEFAULT_RADIUS;
        const lat1 = toRadians(c1[1]);
        const lat2 = toRadians(c2[1]);
        const deltaLatBy2 = (lat2 - lat1) / 2;
        const deltaLonBy2 = toRadians(c2[0] - c1[0]) / 2;
        const a = Math.sin(deltaLatBy2) * Math.sin(deltaLatBy2) + Math.sin(deltaLonBy2) * Math.sin(deltaLonBy2) * Math.cos(lat1) * Math.cos(lat2);
        return 2 * radius * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    }
    function warn(...args) {
        console.warn(...args);
    }
    let showCoordinateWarning = true;
    function disableCoordinateWarning(disable) {
        showCoordinateWarning = false;
    }
    function cloneTransform(input, output) {
        if (output !== undefined) {
            for (let i = 0, ii = input.length; i < ii; ++i) {
                output[i] = input[i];
            }
            output = output;
        } else {
            output = input.slice();
        }
        return output;
    }
    function identityTransform(input, output) {
        if (output !== undefined && input !== output) {
            for (let i = 0, ii = input.length; i < ii; ++i) {
                output[i] = input[i];
            }
            input = output;
        }
        return input;
    }
    function addProjection(projection) {
        add$2(projection.getCode(), projection);
        add$1(projection, projection, cloneTransform);
    }
    function addProjections(projections) {
        projections.forEach(addProjection);
    }
    function get$1(projectionLike) {
        return typeof projectionLike === "string" ? get$3(projectionLike) : projectionLike || null;
    }
    function getPointResolution(projection, resolution, point, units) {
        projection = get$1(projection);
        let pointResolution;
        const getter = projection.getPointResolutionFunc();
        if (getter) {
            pointResolution = getter(resolution, point);
        } else {
            const projUnits = projection.getUnits();
            if (projUnits == "degrees" && !units || units == "degrees") {
                pointResolution = resolution;
            } else {
                const toEPSG4326 = getTransformFromProjections(projection, get$1("EPSG:4326"));
                if (toEPSG4326 === identityTransform && projUnits !== "degrees") {
                    pointResolution = resolution * projection.getMetersPerUnit();
                } else {
                    let vertices = [ point[0] - resolution / 2, point[1], point[0] + resolution / 2, point[1], point[0], point[1] - resolution / 2, point[0], point[1] + resolution / 2 ];
                    vertices = toEPSG4326(vertices, vertices, 2);
                    const width = getDistance(vertices.slice(0, 2), vertices.slice(2, 4));
                    const height = getDistance(vertices.slice(4, 6), vertices.slice(6, 8));
                    pointResolution = (width + height) / 2;
                }
                const metersPerUnit = projection.getMetersPerUnit();
                if (metersPerUnit !== undefined) {
                    pointResolution /= metersPerUnit;
                }
            }
        }
        return pointResolution;
    }
    function addEquivalentProjections(projections) {
        addProjections(projections);
        projections.forEach(function(source) {
            projections.forEach(function(destination) {
                if (source !== destination) {
                    add$1(source, destination, cloneTransform);
                }
            });
        });
    }
    function addEquivalentTransforms(projections1, projections2, forwardTransform, inverseTransform) {
        projections1.forEach(function(projection1) {
            projections2.forEach(function(projection2) {
                add$1(projection1, projection2, forwardTransform);
                add$1(projection2, projection1, inverseTransform);
            });
        });
    }
    function createProjection(projection, defaultCode) {
        if (!projection) {
            return get$1(defaultCode);
        }
        if (typeof projection === "string") {
            return get$1(projection);
        }
        return projection;
    }
    function fromLonLat(coordinate, projection) {
        disableCoordinateWarning();
        return transform(coordinate, "EPSG:4326", projection !== undefined ? projection : "EPSG:3857");
    }
    function toLonLat(coordinate, projection) {
        const lonLat = transform(coordinate, projection !== undefined ? projection : "EPSG:3857", "EPSG:4326");
        const lon = lonLat[0];
        if (lon < -180 || lon > 180) {
            lonLat[0] = modulo(lon + 180, 360) - 180;
        }
        return lonLat;
    }
    function equivalent(projection1, projection2) {
        if (projection1 === projection2) {
            return true;
        }
        const equalUnits = projection1.getUnits() === projection2.getUnits();
        if (projection1.getCode() === projection2.getCode()) {
            return equalUnits;
        }
        const transformFunc = getTransformFromProjections(projection1, projection2);
        return transformFunc === cloneTransform && equalUnits;
    }
    function getTransformFromProjections(sourceProjection, destinationProjection) {
        const sourceCode = sourceProjection.getCode();
        const destinationCode = destinationProjection.getCode();
        let transformFunc = get$2(sourceCode, destinationCode);
        if (!transformFunc) {
            transformFunc = identityTransform;
        }
        return transformFunc;
    }
    function getTransform(source, destination) {
        const sourceProjection = get$1(source);
        const destinationProjection = get$1(destination);
        return getTransformFromProjections(sourceProjection, destinationProjection);
    }
    function transform(coordinate, source, destination) {
        const transformFunc = getTransform(source, destination);
        return transformFunc(coordinate, undefined, coordinate.length);
    }
    function toUserCoordinate(coordinate, sourceProjection) {
        {
            return coordinate;
        }
    }
    function fromUserCoordinate(coordinate, destProjection) {
        {
            if (showCoordinateWarning && !equals(coordinate, [ 0, 0 ]) && coordinate[0] >= -180 && coordinate[0] <= 180 && coordinate[1] >= -90 && coordinate[1] <= 90) {
                showCoordinateWarning = false;
                warn("Call useGeographic() from ol/proj once to work with [longitude, latitude] coordinates.");
            }
            return coordinate;
        }
    }
    function toUserExtent(extent, sourceProjection) {
        {
            return extent;
        }
    }
    function fromUserExtent(extent, destProjection) {
        {
            return extent;
        }
    }
    function addCommon() {
        addEquivalentProjections(PROJECTIONS$1);
        addEquivalentProjections(PROJECTIONS);
        addEquivalentTransforms(PROJECTIONS, PROJECTIONS$1, fromEPSG4326, toEPSG4326);
    }
    addCommon();
    function createExtent(extent, onlyCenter, smooth) {
        return function(center, resolution, size, isMoving, centerShift) {
            if (!center) {
                return undefined;
            }
            if (!resolution && !onlyCenter) {
                return center;
            }
            const viewWidth = onlyCenter ? 0 : size[0] * resolution;
            const viewHeight = onlyCenter ? 0 : size[1] * resolution;
            const shiftX = centerShift ? centerShift[0] : 0;
            const shiftY = centerShift ? centerShift[1] : 0;
            let minX = extent[0] + viewWidth / 2 + shiftX;
            let maxX = extent[2] - viewWidth / 2 + shiftX;
            let minY = extent[1] + viewHeight / 2 + shiftY;
            let maxY = extent[3] - viewHeight / 2 + shiftY;
            if (minX > maxX) {
                minX = (maxX + minX) / 2;
                maxX = minX;
            }
            if (minY > maxY) {
                minY = (maxY + minY) / 2;
                maxY = minY;
            }
            let x = clamp(center[0], minX, maxX);
            let y = clamp(center[1], minY, maxY);
            if (isMoving && smooth && resolution) {
                const ratio = 30 * resolution;
                x += -ratio * Math.log(1 + Math.max(0, minX - center[0]) / ratio) + ratio * Math.log(1 + Math.max(0, center[0] - maxX) / ratio);
                y += -ratio * Math.log(1 + Math.max(0, minY - center[1]) / ratio) + ratio * Math.log(1 + Math.max(0, center[1] - maxY) / ratio);
            }
            return [ x, y ];
        };
    }
    function none$1(center) {
        return center;
    }
    function getViewportClampedResolution(resolution, maxExtent, viewportSize, showFullExtent) {
        const xResolution = getWidth(maxExtent) / viewportSize[0];
        const yResolution = getHeight(maxExtent) / viewportSize[1];
        if (showFullExtent) {
            return Math.min(resolution, Math.max(xResolution, yResolution));
        }
        return Math.min(resolution, Math.min(xResolution, yResolution));
    }
    function getSmoothClampedResolution(resolution, maxResolution, minResolution) {
        let result = Math.min(resolution, maxResolution);
        const ratio = 50;
        result *= Math.log(1 + ratio * Math.max(0, resolution / maxResolution - 1)) / ratio + 1;
        if (minResolution) {
            result = Math.max(result, minResolution);
            result /= Math.log(1 + ratio * Math.max(0, minResolution / resolution - 1)) / ratio + 1;
        }
        return clamp(result, minResolution / 2, maxResolution * 2);
    }
    function createSnapToResolutions(resolutions, smooth, maxExtent, showFullExtent) {
        smooth = smooth !== undefined ? smooth : true;
        return function(resolution, direction, size, isMoving) {
            if (resolution !== undefined) {
                const maxResolution = resolutions[0];
                const minResolution = resolutions[resolutions.length - 1];
                const cappedMaxRes = maxExtent ? getViewportClampedResolution(maxResolution, maxExtent, size, showFullExtent) : maxResolution;
                if (isMoving) {
                    if (!smooth) {
                        return clamp(resolution, minResolution, cappedMaxRes);
                    }
                    return getSmoothClampedResolution(resolution, cappedMaxRes, minResolution);
                }
                const capped = Math.min(cappedMaxRes, resolution);
                const z = Math.floor(linearFindNearest(resolutions, capped, direction));
                if (resolutions[z] > cappedMaxRes && z < resolutions.length - 1) {
                    return resolutions[z + 1];
                }
                return resolutions[z];
            }
            return undefined;
        };
    }
    function createSnapToPower(power, maxResolution, minResolution, smooth, maxExtent, showFullExtent) {
        smooth = smooth !== undefined ? smooth : true;
        minResolution = minResolution !== undefined ? minResolution : 0;
        return function(resolution, direction, size, isMoving) {
            if (resolution !== undefined) {
                const cappedMaxRes = maxExtent ? getViewportClampedResolution(maxResolution, maxExtent, size, showFullExtent) : maxResolution;
                if (isMoving) {
                    if (!smooth) {
                        return clamp(resolution, minResolution, cappedMaxRes);
                    }
                    return getSmoothClampedResolution(resolution, cappedMaxRes, minResolution);
                }
                const tolerance = 1e-9;
                const minZoomLevel = Math.ceil(Math.log(maxResolution / cappedMaxRes) / Math.log(power) - tolerance);
                const offset = -direction * (.5 - tolerance) + .5;
                const capped = Math.min(cappedMaxRes, resolution);
                const cappedZoomLevel = Math.floor(Math.log(maxResolution / capped) / Math.log(power) + offset);
                const zoomLevel = Math.max(minZoomLevel, cappedZoomLevel);
                const newResolution = maxResolution / Math.pow(power, zoomLevel);
                return clamp(newResolution, minResolution, cappedMaxRes);
            }
            return undefined;
        };
    }
    function createMinMaxResolution(maxResolution, minResolution, smooth, maxExtent, showFullExtent) {
        smooth = smooth !== undefined ? smooth : true;
        return function(resolution, direction, size, isMoving) {
            if (resolution !== undefined) {
                const cappedMaxRes = maxExtent ? getViewportClampedResolution(maxResolution, maxExtent, size, showFullExtent) : maxResolution;
                if (!smooth || !isMoving) {
                    return clamp(resolution, minResolution, cappedMaxRes);
                }
                return getSmoothClampedResolution(resolution, cappedMaxRes, minResolution);
            }
            return undefined;
        };
    }
    function disable(rotation) {
        if (rotation !== undefined) {
            return 0;
        }
        return undefined;
    }
    function none(rotation) {
        if (rotation !== undefined) {
            return rotation;
        }
        return undefined;
    }
    function createSnapToN(n) {
        const theta = 2 * Math.PI / n;
        return function(rotation, isMoving) {
            if (isMoving) {
                return rotation;
            }
            if (rotation !== undefined) {
                rotation = Math.floor(rotation / theta + .5) * theta;
                return rotation;
            }
            return undefined;
        };
    }
    function createSnapToZero(tolerance) {
        const t = toRadians(5);
        return function(rotation, isMoving) {
            if (isMoving || rotation === undefined) {
                return rotation;
            }
            if (Math.abs(rotation) <= t) {
                return 0;
            }
            return rotation;
        };
    }
    function easeIn(t) {
        return Math.pow(t, 3);
    }
    function easeOut(t) {
        return 1 - easeIn(1 - t);
    }
    function inAndOut(t) {
        return 3 * t * t - 2 * t * t * t;
    }
    function linear(t) {
        return t;
    }
    new Array(6);
    function create() {
        return [ 1, 0, 0, 1, 0, 0 ];
    }
    function setFromArray(transform1, transform2) {
        transform1[0] = transform2[0];
        transform1[1] = transform2[1];
        transform1[2] = transform2[2];
        transform1[3] = transform2[3];
        transform1[4] = transform2[4];
        transform1[5] = transform2[5];
        return transform1;
    }
    function apply(transform, coordinate) {
        const x = coordinate[0];
        const y = coordinate[1];
        coordinate[0] = transform[0] * x + transform[2] * y + transform[4];
        coordinate[1] = transform[1] * x + transform[3] * y + transform[5];
        return coordinate;
    }
    function compose(transform, dx1, dy1, sx, sy, angle, dx2, dy2) {
        const sin = Math.sin(angle);
        const cos = Math.cos(angle);
        transform[0] = sx * cos;
        transform[1] = sy * sin;
        transform[2] = -sx * sin;
        transform[3] = sy * cos;
        transform[4] = dx2 * sx * cos - dy2 * sx * sin + dx1;
        transform[5] = dx2 * sy * sin + dy2 * sy * cos + dy1;
        return transform;
    }
    function makeInverse(target, source) {
        const det = determinant(source);
        assert(det !== 0, "Transformation matrix cannot be inverted");
        const a = source[0];
        const b = source[1];
        const c = source[2];
        const d = source[3];
        const e = source[4];
        const f = source[5];
        target[0] = d / det;
        target[1] = -b / det;
        target[2] = -c / det;
        target[3] = a / det;
        target[4] = (c * f - d * e) / det;
        target[5] = -(a * f - b * e) / det;
        return target;
    }
    function determinant(mat) {
        return mat[0] * mat[3] - mat[1] * mat[2];
    }
    const matrixPrecision = [ 1e6, 1e6, 1e6, 1e6, 2, 2 ];
    function toString$1(mat) {
        const transformString = "matrix(" + mat.map((value, i) => Math.round(value * matrixPrecision[i]) / matrixPrecision[i]).join(", ") + ")";
        return transformString;
    }
    function transform2D(flatCoordinates, offset, end, stride, transform, dest) {
        dest = dest ? dest : [];
        let i = 0;
        for (let j = offset; j < end; j += stride) {
            const x = flatCoordinates[j];
            const y = flatCoordinates[j + 1];
            dest[i++] = transform[0] * x + transform[2] * y + transform[4];
            dest[i++] = transform[1] * x + transform[3] * y + transform[5];
        }
        if (dest && dest.length != i) {
            dest.length = i;
        }
        return dest;
    }
    function rotate(flatCoordinates, offset, end, stride, angle, anchor, dest) {
        dest = dest ? dest : [];
        const cos = Math.cos(angle);
        const sin = Math.sin(angle);
        const anchorX = anchor[0];
        const anchorY = anchor[1];
        let i = 0;
        for (let j = offset; j < end; j += stride) {
            const deltaX = flatCoordinates[j] - anchorX;
            const deltaY = flatCoordinates[j + 1] - anchorY;
            dest[i++] = anchorX + deltaX * cos - deltaY * sin;
            dest[i++] = anchorY + deltaX * sin + deltaY * cos;
            for (let k = j + 2; k < j + stride; ++k) {
                dest[i++] = flatCoordinates[k];
            }
        }
        if (dest && dest.length != i) {
            dest.length = i;
        }
        return dest;
    }
    function scale$1(flatCoordinates, offset, end, stride, sx, sy, anchor, dest) {
        dest = dest ? dest : [];
        const anchorX = anchor[0];
        const anchorY = anchor[1];
        let i = 0;
        for (let j = offset; j < end; j += stride) {
            const deltaX = flatCoordinates[j] - anchorX;
            const deltaY = flatCoordinates[j + 1] - anchorY;
            dest[i++] = anchorX + sx * deltaX;
            dest[i++] = anchorY + sy * deltaY;
            for (let k = j + 2; k < j + stride; ++k) {
                dest[i++] = flatCoordinates[k];
            }
        }
        if (dest && dest.length != i) {
            dest.length = i;
        }
        return dest;
    }
    function translate(flatCoordinates, offset, end, stride, deltaX, deltaY, dest) {
        dest = dest ? dest : [];
        let i = 0;
        for (let j = offset; j < end; j += stride) {
            dest[i++] = flatCoordinates[j] + deltaX;
            dest[i++] = flatCoordinates[j + 1] + deltaY;
            for (let k = j + 2; k < j + stride; ++k) {
                dest[i++] = flatCoordinates[k];
            }
        }
        if (dest && dest.length != i) {
            dest.length = i;
        }
        return dest;
    }
    const tmpTransform$1 = create();
    class Geometry extends BaseObject {
        constructor() {
            super();
            this.extent_ = createEmpty();
            this.extentRevision_ = -1;
            this.simplifiedGeometryMaxMinSquaredTolerance = 0;
            this.simplifiedGeometryRevision = 0;
            this.simplifyTransformedInternal = memoizeOne((revision, squaredTolerance, transform) => {
                if (!transform) {
                    return this.getSimplifiedGeometry(squaredTolerance);
                }
                const clone = this.clone();
                clone.applyTransform(transform);
                return clone.getSimplifiedGeometry(squaredTolerance);
            });
        }
        simplifyTransformed(squaredTolerance, transform) {
            return this.simplifyTransformedInternal(this.getRevision(), squaredTolerance, transform);
        }
        clone() {
            return abstract();
        }
        closestPointXY(x, y, closestPoint, minSquaredDistance) {
            return abstract();
        }
        containsXY(x, y) {
            const coord = this.getClosestPoint([ x, y ]);
            return coord[0] === x && coord[1] === y;
        }
        getClosestPoint(point, closestPoint) {
            closestPoint = closestPoint ? closestPoint : [ NaN, NaN ];
            this.closestPointXY(point[0], point[1], closestPoint, Infinity);
            return closestPoint;
        }
        intersectsCoordinate(coordinate) {
            return this.containsXY(coordinate[0], coordinate[1]);
        }
        computeExtent(extent) {
            return abstract();
        }
        getExtent(extent) {
            if (this.extentRevision_ != this.getRevision()) {
                const extent = this.computeExtent(this.extent_);
                if (isNaN(extent[0]) || isNaN(extent[1])) {
                    createOrUpdateEmpty(extent);
                }
                this.extentRevision_ = this.getRevision();
            }
            return returnOrUpdate(this.extent_, extent);
        }
        rotate(angle, anchor) {
            abstract();
        }
        scale(sx, sy, anchor) {
            abstract();
        }
        simplify(tolerance) {
            return this.getSimplifiedGeometry(tolerance * tolerance);
        }
        getSimplifiedGeometry(squaredTolerance) {
            return abstract();
        }
        getType() {
            return abstract();
        }
        applyTransform(transformFn) {
            abstract();
        }
        intersectsExtent(extent) {
            return abstract();
        }
        translate(deltaX, deltaY) {
            abstract();
        }
        transform(source, destination) {
            const sourceProj = get$1(source);
            const transformFn = sourceProj.getUnits() == "tile-pixels" ? function(inCoordinates, outCoordinates, stride) {
                const pixelExtent = sourceProj.getExtent();
                const projectedExtent = sourceProj.getWorldExtent();
                const scale = getHeight(projectedExtent) / getHeight(pixelExtent);
                compose(tmpTransform$1, projectedExtent[0], projectedExtent[3], scale, -scale, 0, 0, 0);
                transform2D(inCoordinates, 0, inCoordinates.length, stride, tmpTransform$1, outCoordinates);
                return getTransform(sourceProj, destination)(inCoordinates, outCoordinates, stride);
            } : getTransform(sourceProj, destination);
            this.applyTransform(transformFn);
            return this;
        }
    }
    class SimpleGeometry extends Geometry {
        constructor() {
            super();
            this.layout = "XY";
            this.stride = 2;
            this.flatCoordinates;
        }
        computeExtent(extent) {
            return createOrUpdateFromFlatCoordinates(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, extent);
        }
        getCoordinates() {
            return abstract();
        }
        getFirstCoordinate() {
            return this.flatCoordinates.slice(0, this.stride);
        }
        getFlatCoordinates() {
            return this.flatCoordinates;
        }
        getLastCoordinate() {
            return this.flatCoordinates.slice(this.flatCoordinates.length - this.stride);
        }
        getLayout() {
            return this.layout;
        }
        getSimplifiedGeometry(squaredTolerance) {
            if (this.simplifiedGeometryRevision !== this.getRevision()) {
                this.simplifiedGeometryMaxMinSquaredTolerance = 0;
                this.simplifiedGeometryRevision = this.getRevision();
            }
            if (squaredTolerance < 0 || this.simplifiedGeometryMaxMinSquaredTolerance !== 0 && squaredTolerance <= this.simplifiedGeometryMaxMinSquaredTolerance) {
                return this;
            }
            const simplifiedGeometry = this.getSimplifiedGeometryInternal(squaredTolerance);
            const simplifiedFlatCoordinates = simplifiedGeometry.getFlatCoordinates();
            if (simplifiedFlatCoordinates.length < this.flatCoordinates.length) {
                return simplifiedGeometry;
            }
            this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance;
            return this;
        }
        getSimplifiedGeometryInternal(squaredTolerance) {
            return this;
        }
        getStride() {
            return this.stride;
        }
        setFlatCoordinates(layout, flatCoordinates) {
            this.stride = getStrideForLayout(layout);
            this.layout = layout;
            this.flatCoordinates = flatCoordinates;
        }
        setCoordinates(coordinates, layout) {
            abstract();
        }
        setLayout(layout, coordinates, nesting) {
            let stride;
            if (layout) {
                stride = getStrideForLayout(layout);
            } else {
                for (let i = 0; i < nesting; ++i) {
                    if (coordinates.length === 0) {
                        this.layout = "XY";
                        this.stride = 2;
                        return;
                    }
                    coordinates = coordinates[0];
                }
                stride = coordinates.length;
                layout = getLayoutForStride(stride);
            }
            this.layout = layout;
            this.stride = stride;
        }
        applyTransform(transformFn) {
            if (this.flatCoordinates) {
                transformFn(this.flatCoordinates, this.flatCoordinates, this.stride);
                this.changed();
            }
        }
        rotate(angle, anchor) {
            const flatCoordinates = this.getFlatCoordinates();
            if (flatCoordinates) {
                const stride = this.getStride();
                rotate(flatCoordinates, 0, flatCoordinates.length, stride, angle, anchor, flatCoordinates);
                this.changed();
            }
        }
        scale(sx, sy, anchor) {
            if (sy === undefined) {
                sy = sx;
            }
            if (!anchor) {
                anchor = getCenter(this.getExtent());
            }
            const flatCoordinates = this.getFlatCoordinates();
            if (flatCoordinates) {
                const stride = this.getStride();
                scale$1(flatCoordinates, 0, flatCoordinates.length, stride, sx, sy, anchor, flatCoordinates);
                this.changed();
            }
        }
        translate(deltaX, deltaY) {
            const flatCoordinates = this.getFlatCoordinates();
            if (flatCoordinates) {
                const stride = this.getStride();
                translate(flatCoordinates, 0, flatCoordinates.length, stride, deltaX, deltaY, flatCoordinates);
                this.changed();
            }
        }
    }
    function getLayoutForStride(stride) {
        let layout;
        if (stride == 2) {
            layout = "XY";
        } else if (stride == 3) {
            layout = "XYZ";
        } else if (stride == 4) {
            layout = "XYZM";
        }
        return layout;
    }
    function getStrideForLayout(layout) {
        let stride;
        if (layout == "XY") {
            stride = 2;
        } else if (layout == "XYZ" || layout == "XYM") {
            stride = 3;
        } else if (layout == "XYZM") {
            stride = 4;
        }
        return stride;
    }
    function transformGeom2D(simpleGeometry, transform, dest) {
        const flatCoordinates = simpleGeometry.getFlatCoordinates();
        if (!flatCoordinates) {
            return null;
        }
        const stride = simpleGeometry.getStride();
        return transform2D(flatCoordinates, 0, flatCoordinates.length, stride, transform, dest);
    }
    function assignClosest(flatCoordinates, offset1, offset2, stride, x, y, closestPoint) {
        const x1 = flatCoordinates[offset1];
        const y1 = flatCoordinates[offset1 + 1];
        const dx = flatCoordinates[offset2] - x1;
        const dy = flatCoordinates[offset2 + 1] - y1;
        let offset;
        if (dx === 0 && dy === 0) {
            offset = offset1;
        } else {
            const t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy);
            if (t > 1) {
                offset = offset2;
            } else if (t > 0) {
                for (let i = 0; i < stride; ++i) {
                    closestPoint[i] = lerp(flatCoordinates[offset1 + i], flatCoordinates[offset2 + i], t);
                }
                closestPoint.length = stride;
                return;
            } else {
                offset = offset1;
            }
        }
        for (let i = 0; i < stride; ++i) {
            closestPoint[i] = flatCoordinates[offset + i];
        }
        closestPoint.length = stride;
    }
    function maxSquaredDelta(flatCoordinates, offset, end, stride, max) {
        let x1 = flatCoordinates[offset];
        let y1 = flatCoordinates[offset + 1];
        for (offset += stride; offset < end; offset += stride) {
            const x2 = flatCoordinates[offset];
            const y2 = flatCoordinates[offset + 1];
            const squaredDelta = squaredDistance(x1, y1, x2, y2);
            if (squaredDelta > max) {
                max = squaredDelta;
            }
            x1 = x2;
            y1 = y2;
        }
        return max;
    }
    function arrayMaxSquaredDelta(flatCoordinates, offset, ends, stride, max) {
        for (let i = 0, ii = ends.length; i < ii; ++i) {
            const end = ends[i];
            max = maxSquaredDelta(flatCoordinates, offset, end, stride, max);
            offset = end;
        }
        return max;
    }
    function assignClosestPoint(flatCoordinates, offset, end, stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance, tmpPoint) {
        if (offset == end) {
            return minSquaredDistance;
        }
        let i, squaredDistance$1;
        if (maxDelta === 0) {
            squaredDistance$1 = squaredDistance(x, y, flatCoordinates[offset], flatCoordinates[offset + 1]);
            if (squaredDistance$1 < minSquaredDistance) {
                for (i = 0; i < stride; ++i) {
                    closestPoint[i] = flatCoordinates[offset + i];
                }
                closestPoint.length = stride;
                return squaredDistance$1;
            }
            return minSquaredDistance;
        }
        tmpPoint = tmpPoint ? tmpPoint : [ NaN, NaN ];
        let index = offset + stride;
        while (index < end) {
            assignClosest(flatCoordinates, index - stride, index, stride, x, y, tmpPoint);
            squaredDistance$1 = squaredDistance(x, y, tmpPoint[0], tmpPoint[1]);
            if (squaredDistance$1 < minSquaredDistance) {
                minSquaredDistance = squaredDistance$1;
                for (i = 0; i < stride; ++i) {
                    closestPoint[i] = tmpPoint[i];
                }
                closestPoint.length = stride;
                index += stride;
            } else {
                index += stride * Math.max((Math.sqrt(squaredDistance$1) - Math.sqrt(minSquaredDistance)) / maxDelta | 0, 1);
            }
        }
        {
            assignClosest(flatCoordinates, end - stride, offset, stride, x, y, tmpPoint);
            squaredDistance$1 = squaredDistance(x, y, tmpPoint[0], tmpPoint[1]);
            if (squaredDistance$1 < minSquaredDistance) {
                minSquaredDistance = squaredDistance$1;
                for (i = 0; i < stride; ++i) {
                    closestPoint[i] = tmpPoint[i];
                }
                closestPoint.length = stride;
            }
        }
        return minSquaredDistance;
    }
    function assignClosestArrayPoint(flatCoordinates, offset, ends, stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance, tmpPoint) {
        tmpPoint = tmpPoint ? tmpPoint : [ NaN, NaN ];
        for (let i = 0, ii = ends.length; i < ii; ++i) {
            const end = ends[i];
            minSquaredDistance = assignClosestPoint(flatCoordinates, offset, end, stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance, tmpPoint);
            offset = end;
        }
        return minSquaredDistance;
    }
    function deflateCoordinate(flatCoordinates, offset, coordinate, stride) {
        for (let i = 0, ii = coordinate.length; i < ii; ++i) {
            flatCoordinates[offset++] = coordinate[i];
        }
        return offset;
    }
    function deflateCoordinates(flatCoordinates, offset, coordinates, stride) {
        for (let i = 0, ii = coordinates.length; i < ii; ++i) {
            const coordinate = coordinates[i];
            for (let j = 0; j < stride; ++j) {
                flatCoordinates[offset++] = coordinate[j];
            }
        }
        return offset;
    }
    function deflateCoordinatesArray(flatCoordinates, offset, coordinatess, stride, ends) {
        ends = ends ? ends : [];
        let i = 0;
        for (let j = 0, jj = coordinatess.length; j < jj; ++j) {
            const end = deflateCoordinates(flatCoordinates, offset, coordinatess[j], stride);
            ends[i++] = end;
            offset = end;
        }
        ends.length = i;
        return ends;
    }
    function douglasPeucker(flatCoordinates, offset, end, stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset) {
        const n = (end - offset) / stride;
        if (n < 3) {
            for (;offset < end; offset += stride) {
                simplifiedFlatCoordinates[simplifiedOffset++] = flatCoordinates[offset];
                simplifiedFlatCoordinates[simplifiedOffset++] = flatCoordinates[offset + 1];
            }
            return simplifiedOffset;
        }
        const markers = new Array(n);
        markers[0] = 1;
        markers[n - 1] = 1;
        const stack = [ offset, end - stride ];
        let index = 0;
        while (stack.length > 0) {
            const last = stack.pop();
            const first = stack.pop();
            let maxSquaredDistance = 0;
            const x1 = flatCoordinates[first];
            const y1 = flatCoordinates[first + 1];
            const x2 = flatCoordinates[last];
            const y2 = flatCoordinates[last + 1];
            for (let i = first + stride; i < last; i += stride) {
                const x = flatCoordinates[i];
                const y = flatCoordinates[i + 1];
                const squaredDistance = squaredSegmentDistance(x, y, x1, y1, x2, y2);
                if (squaredDistance > maxSquaredDistance) {
                    index = i;
                    maxSquaredDistance = squaredDistance;
                }
            }
            if (maxSquaredDistance > squaredTolerance) {
                markers[(index - offset) / stride] = 1;
                if (first + stride < index) {
                    stack.push(first, index);
                }
                if (index + stride < last) {
                    stack.push(index, last);
                }
            }
        }
        for (let i = 0; i < n; ++i) {
            if (markers[i]) {
                simplifiedFlatCoordinates[simplifiedOffset++] = flatCoordinates[offset + i * stride];
                simplifiedFlatCoordinates[simplifiedOffset++] = flatCoordinates[offset + i * stride + 1];
            }
        }
        return simplifiedOffset;
    }
    function douglasPeuckerArray(flatCoordinates, offset, ends, stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds) {
        for (let i = 0, ii = ends.length; i < ii; ++i) {
            const end = ends[i];
            simplifiedOffset = douglasPeucker(flatCoordinates, offset, end, stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset);
            simplifiedEnds.push(simplifiedOffset);
            offset = end;
        }
        return simplifiedOffset;
    }
    function snap(value, tolerance) {
        return tolerance * Math.round(value / tolerance);
    }
    function quantize(flatCoordinates, offset, end, stride, tolerance, simplifiedFlatCoordinates, simplifiedOffset) {
        if (offset == end) {
            return simplifiedOffset;
        }
        let x1 = snap(flatCoordinates[offset], tolerance);
        let y1 = snap(flatCoordinates[offset + 1], tolerance);
        offset += stride;
        simplifiedFlatCoordinates[simplifiedOffset++] = x1;
        simplifiedFlatCoordinates[simplifiedOffset++] = y1;
        let x2, y2;
        do {
            x2 = snap(flatCoordinates[offset], tolerance);
            y2 = snap(flatCoordinates[offset + 1], tolerance);
            offset += stride;
            if (offset == end) {
                simplifiedFlatCoordinates[simplifiedOffset++] = x2;
                simplifiedFlatCoordinates[simplifiedOffset++] = y2;
                return simplifiedOffset;
            }
        } while (x2 == x1 && y2 == y1);
        while (offset < end) {
            const x3 = snap(flatCoordinates[offset], tolerance);
            const y3 = snap(flatCoordinates[offset + 1], tolerance);
            offset += stride;
            if (x3 == x2 && y3 == y2) {
                continue;
            }
            const dx1 = x2 - x1;
            const dy1 = y2 - y1;
            const dx2 = x3 - x1;
            const dy2 = y3 - y1;
            if (dx1 * dy2 == dy1 * dx2 && (dx1 < 0 && dx2 < dx1 || dx1 == dx2 || dx1 > 0 && dx2 > dx1) && (dy1 < 0 && dy2 < dy1 || dy1 == dy2 || dy1 > 0 && dy2 > dy1)) {
                x2 = x3;
                y2 = y3;
                continue;
            }
            simplifiedFlatCoordinates[simplifiedOffset++] = x2;
            simplifiedFlatCoordinates[simplifiedOffset++] = y2;
            x1 = x2;
            y1 = y2;
            x2 = x3;
            y2 = y3;
        }
        simplifiedFlatCoordinates[simplifiedOffset++] = x2;
        simplifiedFlatCoordinates[simplifiedOffset++] = y2;
        return simplifiedOffset;
    }
    function quantizeArray(flatCoordinates, offset, ends, stride, tolerance, simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds) {
        for (let i = 0, ii = ends.length; i < ii; ++i) {
            const end = ends[i];
            simplifiedOffset = quantize(flatCoordinates, offset, end, stride, tolerance, simplifiedFlatCoordinates, simplifiedOffset);
            simplifiedEnds.push(simplifiedOffset);
            offset = end;
        }
        return simplifiedOffset;
    }
    function inflateCoordinates(flatCoordinates, offset, end, stride, coordinates) {
        coordinates = coordinates !== undefined ? coordinates : [];
        let i = 0;
        for (let j = offset; j < end; j += stride) {
            coordinates[i++] = flatCoordinates.slice(j, j + stride);
        }
        coordinates.length = i;
        return coordinates;
    }
    function inflateCoordinatesArray(flatCoordinates, offset, ends, stride, coordinatess) {
        coordinatess = coordinatess !== undefined ? coordinatess : [];
        let i = 0;
        for (let j = 0, jj = ends.length; j < jj; ++j) {
            const end = ends[j];
            coordinatess[i++] = inflateCoordinates(flatCoordinates, offset, end, stride, coordinatess[i]);
            offset = end;
        }
        coordinatess.length = i;
        return coordinatess;
    }
    function inflateMultiCoordinatesArray(flatCoordinates, offset, endss, stride, coordinatesss) {
        coordinatesss = coordinatesss !== undefined ? coordinatesss : [];
        let i = 0;
        for (let j = 0, jj = endss.length; j < jj; ++j) {
            const ends = endss[j];
            coordinatesss[i++] = ends.length === 1 && ends[0] === offset ? [] : inflateCoordinatesArray(flatCoordinates, offset, ends, stride, coordinatesss[i]);
            offset = ends[ends.length - 1];
        }
        coordinatesss.length = i;
        return coordinatesss;
    }
    function linearRing(flatCoordinates, offset, end, stride) {
        let twiceArea = 0;
        let x1 = flatCoordinates[end - stride];
        let y1 = flatCoordinates[end - stride + 1];
        for (;offset < end; offset += stride) {
            const x2 = flatCoordinates[offset];
            const y2 = flatCoordinates[offset + 1];
            twiceArea += y1 * x2 - x1 * y2;
            x1 = x2;
            y1 = y2;
        }
        return twiceArea / 2;
    }
    function linearRings(flatCoordinates, offset, ends, stride) {
        let area = 0;
        for (let i = 0, ii = ends.length; i < ii; ++i) {
            const end = ends[i];
            area += linearRing(flatCoordinates, offset, end, stride);
            offset = end;
        }
        return area;
    }
    class LinearRing extends SimpleGeometry {
        constructor(coordinates, layout) {
            super();
            this.maxDelta_ = -1;
            this.maxDeltaRevision_ = -1;
            if (layout !== undefined && !Array.isArray(coordinates[0])) {
                this.setFlatCoordinates(layout, coordinates);
            } else {
                this.setCoordinates(coordinates, layout);
            }
        }
        clone() {
            return new LinearRing(this.flatCoordinates.slice(), this.layout);
        }
        closestPointXY(x, y, closestPoint, minSquaredDistance) {
            if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
                return minSquaredDistance;
            }
            if (this.maxDeltaRevision_ != this.getRevision()) {
                this.maxDelta_ = Math.sqrt(maxSquaredDelta(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0));
                this.maxDeltaRevision_ = this.getRevision();
            }
            return assignClosestPoint(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);
        }
        getArea() {
            return linearRing(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
        }
        getCoordinates() {
            return inflateCoordinates(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride);
        }
        getSimplifiedGeometryInternal(squaredTolerance) {
            const simplifiedFlatCoordinates = [];
            simplifiedFlatCoordinates.length = douglasPeucker(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, squaredTolerance, simplifiedFlatCoordinates, 0);
            return new LinearRing(simplifiedFlatCoordinates, "XY");
        }
        getType() {
            return "LinearRing";
        }
        intersectsExtent(extent) {
            return false;
        }
        setCoordinates(coordinates, layout) {
            this.setLayout(layout, coordinates, 1);
            if (!this.flatCoordinates) {
                this.flatCoordinates = [];
            }
            this.flatCoordinates.length = deflateCoordinates(this.flatCoordinates, 0, coordinates, this.stride);
            this.changed();
        }
    }
    class Point extends SimpleGeometry {
        constructor(coordinates, layout) {
            super();
            this.setCoordinates(coordinates, layout);
        }
        clone() {
            const point = new Point(this.flatCoordinates.slice(), this.layout);
            point.applyProperties(this);
            return point;
        }
        closestPointXY(x, y, closestPoint, minSquaredDistance) {
            const flatCoordinates = this.flatCoordinates;
            const squaredDistance$1 = squaredDistance(x, y, flatCoordinates[0], flatCoordinates[1]);
            if (squaredDistance$1 < minSquaredDistance) {
                const stride = this.stride;
                for (let i = 0; i < stride; ++i) {
                    closestPoint[i] = flatCoordinates[i];
                }
                closestPoint.length = stride;
                return squaredDistance$1;
            }
            return minSquaredDistance;
        }
        getCoordinates() {
            return this.flatCoordinates.slice();
        }
        computeExtent(extent) {
            return createOrUpdateFromCoordinate(this.flatCoordinates, extent);
        }
        getType() {
            return "Point";
        }
        intersectsExtent(extent) {
            return containsXY(extent, this.flatCoordinates[0], this.flatCoordinates[1]);
        }
        setCoordinates(coordinates, layout) {
            this.setLayout(layout, coordinates, 0);
            if (!this.flatCoordinates) {
                this.flatCoordinates = [];
            }
            this.flatCoordinates.length = deflateCoordinate(this.flatCoordinates, 0, coordinates, this.stride);
            this.changed();
        }
    }
    function linearRingContainsExtent(flatCoordinates, offset, end, stride, extent) {
        const outside = forEachCorner(extent, function(coordinate) {
            return !linearRingContainsXY(flatCoordinates, offset, end, stride, coordinate[0], coordinate[1]);
        });
        return !outside;
    }
    function linearRingContainsXY(flatCoordinates, offset, end, stride, x, y) {
        let wn = 0;
        let x1 = flatCoordinates[end - stride];
        let y1 = flatCoordinates[end - stride + 1];
        for (;offset < end; offset += stride) {
            const x2 = flatCoordinates[offset];
            const y2 = flatCoordinates[offset + 1];
            if (y1 <= y) {
                if (y2 > y && (x2 - x1) * (y - y1) - (x - x1) * (y2 - y1) > 0) {
                    wn++;
                }
            } else if (y2 <= y && (x2 - x1) * (y - y1) - (x - x1) * (y2 - y1) < 0) {
                wn--;
            }
            x1 = x2;
            y1 = y2;
        }
        return wn !== 0;
    }
    function linearRingsContainsXY(flatCoordinates, offset, ends, stride, x, y) {
        if (ends.length === 0) {
            return false;
        }
        if (!linearRingContainsXY(flatCoordinates, offset, ends[0], stride, x, y)) {
            return false;
        }
        for (let i = 1, ii = ends.length; i < ii; ++i) {
            if (linearRingContainsXY(flatCoordinates, ends[i - 1], ends[i], stride, x, y)) {
                return false;
            }
        }
        return true;
    }
    function getInteriorPointOfArray(flatCoordinates, offset, ends, stride, flatCenters, flatCentersOffset, dest) {
        let i, ii, x, x1, x2, y1, y2;
        const y = flatCenters[flatCentersOffset + 1];
        const intersections = [];
        for (let r = 0, rr = ends.length; r < rr; ++r) {
            const end = ends[r];
            x1 = flatCoordinates[end - stride];
            y1 = flatCoordinates[end - stride + 1];
            for (i = offset; i < end; i += stride) {
                x2 = flatCoordinates[i];
                y2 = flatCoordinates[i + 1];
                if (y <= y1 && y2 <= y || y1 <= y && y <= y2) {
                    x = (y - y1) / (y2 - y1) * (x2 - x1) + x1;
                    intersections.push(x);
                }
                x1 = x2;
                y1 = y2;
            }
        }
        let pointX = NaN;
        let maxSegmentLength = -Infinity;
        intersections.sort(ascending);
        x1 = intersections[0];
        for (i = 1, ii = intersections.length; i < ii; ++i) {
            x2 = intersections[i];
            const segmentLength = Math.abs(x2 - x1);
            if (segmentLength > maxSegmentLength) {
                x = (x1 + x2) / 2;
                if (linearRingsContainsXY(flatCoordinates, offset, ends, stride, x, y)) {
                    pointX = x;
                    maxSegmentLength = segmentLength;
                }
            }
            x1 = x2;
        }
        if (isNaN(pointX)) {
            pointX = flatCenters[flatCentersOffset];
        }
        if (dest) {
            dest.push(pointX, y, maxSegmentLength);
            return dest;
        }
        return [ pointX, y, maxSegmentLength ];
    }
    function getInteriorPointsOfMultiArray(flatCoordinates, offset, endss, stride, flatCenters) {
        let interiorPoints = [];
        for (let i = 0, ii = endss.length; i < ii; ++i) {
            const ends = endss[i];
            interiorPoints = getInteriorPointOfArray(flatCoordinates, offset, ends, stride, flatCenters, 2 * i, interiorPoints);
            offset = ends[ends.length - 1];
        }
        return interiorPoints;
    }
    function forEach(flatCoordinates, offset, end, stride, callback) {
        let ret;
        offset += stride;
        for (;offset < end; offset += stride) {
            ret = callback(flatCoordinates.slice(offset - stride, offset), flatCoordinates.slice(offset, offset + stride));
            if (ret) {
                return ret;
            }
        }
        return false;
    }
    function intersectsLineString(flatCoordinates, offset, end, stride, extent) {
        const coordinatesExtent = extendFlatCoordinates(createEmpty(), flatCoordinates, offset, end, stride);
        if (!intersects$1(extent, coordinatesExtent)) {
            return false;
        }
        if (containsExtent(extent, coordinatesExtent)) {
            return true;
        }
        if (coordinatesExtent[0] >= extent[0] && coordinatesExtent[2] <= extent[2]) {
            return true;
        }
        if (coordinatesExtent[1] >= extent[1] && coordinatesExtent[3] <= extent[3]) {
            return true;
        }
        return forEach(flatCoordinates, offset, end, stride, function(point1, point2) {
            return intersectsSegment(extent, point1, point2);
        });
    }
    function intersectsLinearRing(flatCoordinates, offset, end, stride, extent) {
        if (intersectsLineString(flatCoordinates, offset, end, stride, extent)) {
            return true;
        }
        if (linearRingContainsXY(flatCoordinates, offset, end, stride, extent[0], extent[1])) {
            return true;
        }
        if (linearRingContainsXY(flatCoordinates, offset, end, stride, extent[0], extent[3])) {
            return true;
        }
        if (linearRingContainsXY(flatCoordinates, offset, end, stride, extent[2], extent[1])) {
            return true;
        }
        if (linearRingContainsXY(flatCoordinates, offset, end, stride, extent[2], extent[3])) {
            return true;
        }
        return false;
    }
    function intersectsLinearRingArray(flatCoordinates, offset, ends, stride, extent) {
        if (!intersectsLinearRing(flatCoordinates, offset, ends[0], stride, extent)) {
            return false;
        }
        if (ends.length === 1) {
            return true;
        }
        for (let i = 1, ii = ends.length; i < ii; ++i) {
            if (linearRingContainsExtent(flatCoordinates, ends[i - 1], ends[i], stride, extent)) {
                if (!intersectsLineString(flatCoordinates, ends[i - 1], ends[i], stride, extent)) {
                    return false;
                }
            }
        }
        return true;
    }
    function coordinates(flatCoordinates, offset, end, stride) {
        while (offset < end - stride) {
            for (let i = 0; i < stride; ++i) {
                const tmp = flatCoordinates[offset + i];
                flatCoordinates[offset + i] = flatCoordinates[end - stride + i];
                flatCoordinates[end - stride + i] = tmp;
            }
            offset += stride;
            end -= stride;
        }
    }
    function linearRingIsClockwise(flatCoordinates, offset, end, stride) {
        let edge = 0;
        let x1 = flatCoordinates[end - stride];
        let y1 = flatCoordinates[end - stride + 1];
        for (;offset < end; offset += stride) {
            const x2 = flatCoordinates[offset];
            const y2 = flatCoordinates[offset + 1];
            edge += (x2 - x1) * (y2 + y1);
            x1 = x2;
            y1 = y2;
        }
        return edge === 0 ? undefined : edge > 0;
    }
    function linearRingsAreOriented(flatCoordinates, offset, ends, stride, right) {
        right = right !== undefined ? right : false;
        for (let i = 0, ii = ends.length; i < ii; ++i) {
            const end = ends[i];
            const isClockwise = linearRingIsClockwise(flatCoordinates, offset, end, stride);
            if (i === 0) {
                if (right && isClockwise || !right && !isClockwise) {
                    return false;
                }
            } else {
                if (right && !isClockwise || !right && isClockwise) {
                    return false;
                }
            }
            offset = end;
        }
        return true;
    }
    function orientLinearRings(flatCoordinates, offset, ends, stride, right) {
        right = right !== undefined ? right : false;
        for (let i = 0, ii = ends.length; i < ii; ++i) {
            const end = ends[i];
            const isClockwise = linearRingIsClockwise(flatCoordinates, offset, end, stride);
            const reverse = i === 0 ? right && isClockwise || !right && !isClockwise : right && !isClockwise || !right && isClockwise;
            if (reverse) {
                coordinates(flatCoordinates, offset, end, stride);
            }
            offset = end;
        }
        return offset;
    }
    function inflateEnds(flatCoordinates, ends) {
        const endss = [];
        let offset = 0;
        let prevEndIndex = 0;
        let startOrientation;
        for (let i = 0, ii = ends.length; i < ii; ++i) {
            const end = ends[i];
            const orientation = linearRingIsClockwise(flatCoordinates, offset, end, 2);
            if (startOrientation === undefined) {
                startOrientation = orientation;
            }
            if (orientation === startOrientation) {
                endss.push(ends.slice(prevEndIndex, i + 1));
            } else {
                if (endss.length === 0) {
                    continue;
                }
                endss[endss.length - 1].push(ends[prevEndIndex]);
            }
            prevEndIndex = i + 1;
            offset = end;
        }
        return endss;
    }
    class Polygon extends SimpleGeometry {
        constructor(coordinates, layout, ends) {
            super();
            this.ends_ = [];
            this.flatInteriorPointRevision_ = -1;
            this.flatInteriorPoint_ = null;
            this.maxDelta_ = -1;
            this.maxDeltaRevision_ = -1;
            this.orientedRevision_ = -1;
            this.orientedFlatCoordinates_ = null;
            if (layout !== undefined && ends) {
                this.setFlatCoordinates(layout, coordinates);
                this.ends_ = ends;
            } else {
                this.setCoordinates(coordinates, layout);
            }
        }
        appendLinearRing(linearRing) {
            if (!this.flatCoordinates) {
                this.flatCoordinates = linearRing.getFlatCoordinates().slice();
            } else {
                extend$2(this.flatCoordinates, linearRing.getFlatCoordinates());
            }
            this.ends_.push(this.flatCoordinates.length);
            this.changed();
        }
        clone() {
            const polygon = new Polygon(this.flatCoordinates.slice(), this.layout, this.ends_.slice());
            polygon.applyProperties(this);
            return polygon;
        }
        closestPointXY(x, y, closestPoint, minSquaredDistance) {
            if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
                return minSquaredDistance;
            }
            if (this.maxDeltaRevision_ != this.getRevision()) {
                this.maxDelta_ = Math.sqrt(arrayMaxSquaredDelta(this.flatCoordinates, 0, this.ends_, this.stride, 0));
                this.maxDeltaRevision_ = this.getRevision();
            }
            return assignClosestArrayPoint(this.flatCoordinates, 0, this.ends_, this.stride, this.maxDelta_, true, x, y, closestPoint, minSquaredDistance);
        }
        containsXY(x, y) {
            return linearRingsContainsXY(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, x, y);
        }
        getArea() {
            return linearRings(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride);
        }
        getCoordinates(right) {
            let flatCoordinates;
            if (right !== undefined) {
                flatCoordinates = this.getOrientedFlatCoordinates().slice();
                orientLinearRings(flatCoordinates, 0, this.ends_, this.stride, right);
            } else {
                flatCoordinates = this.flatCoordinates;
            }
            return inflateCoordinatesArray(flatCoordinates, 0, this.ends_, this.stride);
        }
        getEnds() {
            return this.ends_;
        }
        getFlatInteriorPoint() {
            if (this.flatInteriorPointRevision_ != this.getRevision()) {
                const flatCenter = getCenter(this.getExtent());
                this.flatInteriorPoint_ = getInteriorPointOfArray(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, flatCenter, 0);
                this.flatInteriorPointRevision_ = this.getRevision();
            }
            return this.flatInteriorPoint_;
        }
        getInteriorPoint() {
            return new Point(this.getFlatInteriorPoint(), "XYM");
        }
        getLinearRingCount() {
            return this.ends_.length;
        }
        getLinearRing(index) {
            if (index < 0 || this.ends_.length <= index) {
                return null;
            }
            return new LinearRing(this.flatCoordinates.slice(index === 0 ? 0 : this.ends_[index - 1], this.ends_[index]), this.layout);
        }
        getLinearRings() {
            const layout = this.layout;
            const flatCoordinates = this.flatCoordinates;
            const ends = this.ends_;
            const linearRings = [];
            let offset = 0;
            for (let i = 0, ii = ends.length; i < ii; ++i) {
                const end = ends[i];
                const linearRing = new LinearRing(flatCoordinates.slice(offset, end), layout);
                linearRings.push(linearRing);
                offset = end;
            }
            return linearRings;
        }
        getOrientedFlatCoordinates() {
            if (this.orientedRevision_ != this.getRevision()) {
                const flatCoordinates = this.flatCoordinates;
                if (linearRingsAreOriented(flatCoordinates, 0, this.ends_, this.stride)) {
                    this.orientedFlatCoordinates_ = flatCoordinates;
                } else {
                    this.orientedFlatCoordinates_ = flatCoordinates.slice();
                    this.orientedFlatCoordinates_.length = orientLinearRings(this.orientedFlatCoordinates_, 0, this.ends_, this.stride);
                }
                this.orientedRevision_ = this.getRevision();
            }
            return this.orientedFlatCoordinates_;
        }
        getSimplifiedGeometryInternal(squaredTolerance) {
            const simplifiedFlatCoordinates = [];
            const simplifiedEnds = [];
            simplifiedFlatCoordinates.length = quantizeArray(this.flatCoordinates, 0, this.ends_, this.stride, Math.sqrt(squaredTolerance), simplifiedFlatCoordinates, 0, simplifiedEnds);
            return new Polygon(simplifiedFlatCoordinates, "XY", simplifiedEnds);
        }
        getType() {
            return "Polygon";
        }
        intersectsExtent(extent) {
            return intersectsLinearRingArray(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, extent);
        }
        setCoordinates(coordinates, layout) {
            this.setLayout(layout, coordinates, 2);
            if (!this.flatCoordinates) {
                this.flatCoordinates = [];
            }
            const ends = deflateCoordinatesArray(this.flatCoordinates, 0, coordinates, this.stride, this.ends_);
            this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1];
            this.changed();
        }
    }
    function fromExtent(extent) {
        if (isEmpty(extent)) {
            throw new Error("Cannot create polygon from empty extent");
        }
        const minX = extent[0];
        const minY = extent[1];
        const maxX = extent[2];
        const maxY = extent[3];
        const flatCoordinates = [ minX, minY, minX, maxY, maxX, maxY, maxX, minY, minX, minY ];
        return new Polygon(flatCoordinates, "XY", [ flatCoordinates.length ]);
    }
    const DEFAULT_MIN_ZOOM = 0;
    class View extends BaseObject {
        constructor(options) {
            super();
            this.on;
            this.once;
            this.un;
            options = Object.assign({}, options);
            this.hints_ = [ 0, 0 ];
            this.animations_ = [];
            this.updateAnimationKey_;
            this.projection_ = createProjection(options.projection, "EPSG:3857");
            this.viewportSize_ = [ 100, 100 ];
            this.targetCenter_ = null;
            this.targetResolution_;
            this.targetRotation_;
            this.nextCenter_ = null;
            this.nextResolution_;
            this.nextRotation_;
            this.cancelAnchor_ = undefined;
            if (options.projection) {
                disableCoordinateWarning();
            }
            if (options.center) {
                options.center = fromUserCoordinate(options.center, this.projection_);
            }
            if (options.extent) {
                options.extent = fromUserExtent(options.extent, this.projection_);
            }
            this.applyOptions_(options);
        }
        applyOptions_(options) {
            const properties = Object.assign({}, options);
            for (const key in ViewProperty) {
                delete properties[key];
            }
            this.setProperties(properties, true);
            const resolutionConstraintInfo = createResolutionConstraint(options);
            this.maxResolution_ = resolutionConstraintInfo.maxResolution;
            this.minResolution_ = resolutionConstraintInfo.minResolution;
            this.zoomFactor_ = resolutionConstraintInfo.zoomFactor;
            this.resolutions_ = options.resolutions;
            this.padding_ = options.padding;
            this.minZoom_ = resolutionConstraintInfo.minZoom;
            const centerConstraint = createCenterConstraint(options);
            const resolutionConstraint = resolutionConstraintInfo.constraint;
            const rotationConstraint = createRotationConstraint(options);
            this.constraints_ = {
                center: centerConstraint,
                resolution: resolutionConstraint,
                rotation: rotationConstraint
            };
            this.setRotation(options.rotation !== undefined ? options.rotation : 0);
            this.setCenterInternal(options.center !== undefined ? options.center : null);
            if (options.resolution !== undefined) {
                this.setResolution(options.resolution);
            } else if (options.zoom !== undefined) {
                this.setZoom(options.zoom);
            }
        }
        get padding() {
            return this.padding_;
        }
        set padding(padding) {
            let oldPadding = this.padding_;
            this.padding_ = padding;
            const center = this.getCenterInternal();
            if (center) {
                const newPadding = padding || [ 0, 0, 0, 0 ];
                oldPadding = oldPadding || [ 0, 0, 0, 0 ];
                const resolution = this.getResolution();
                const offsetX = resolution / 2 * (newPadding[3] - oldPadding[3] + oldPadding[1] - newPadding[1]);
                const offsetY = resolution / 2 * (newPadding[0] - oldPadding[0] + oldPadding[2] - newPadding[2]);
                this.setCenterInternal([ center[0] + offsetX, center[1] - offsetY ]);
            }
        }
        getUpdatedOptions_(newOptions) {
            const options = this.getProperties();
            if (options.resolution !== undefined) {
                options.resolution = this.getResolution();
            } else {
                options.zoom = this.getZoom();
            }
            options.center = this.getCenterInternal();
            options.rotation = this.getRotation();
            return Object.assign({}, options, newOptions);
        }
        animate(var_args) {
            if (this.isDef() && !this.getAnimating()) {
                this.resolveConstraints(0);
            }
            const args = new Array(arguments.length);
            for (let i = 0; i < args.length; ++i) {
                let options = arguments[i];
                if (options.center) {
                    options = Object.assign({}, options);
                    options.center = fromUserCoordinate(options.center, this.getProjection());
                }
                if (options.anchor) {
                    options = Object.assign({}, options);
                    options.anchor = fromUserCoordinate(options.anchor, this.getProjection());
                }
                args[i] = options;
            }
            this.animateInternal.apply(this, args);
        }
        animateInternal(var_args) {
            let animationCount = arguments.length;
            let callback;
            if (animationCount > 1 && typeof arguments[animationCount - 1] === "function") {
                callback = arguments[animationCount - 1];
                --animationCount;
            }
            let i = 0;
            for (;i < animationCount && !this.isDef(); ++i) {
                const state = arguments[i];
                if (state.center) {
                    this.setCenterInternal(state.center);
                }
                if (state.zoom !== undefined) {
                    this.setZoom(state.zoom);
                } else if (state.resolution) {
                    this.setResolution(state.resolution);
                }
                if (state.rotation !== undefined) {
                    this.setRotation(state.rotation);
                }
            }
            if (i === animationCount) {
                if (callback) {
                    animationCallback(callback, true);
                }
                return;
            }
            let start = Date.now();
            let center = this.targetCenter_.slice();
            let resolution = this.targetResolution_;
            let rotation = this.targetRotation_;
            const series = [];
            for (;i < animationCount; ++i) {
                const options = arguments[i];
                const animation = {
                    start: start,
                    complete: false,
                    anchor: options.anchor,
                    duration: options.duration !== undefined ? options.duration : 1e3,
                    easing: options.easing || inAndOut,
                    callback: callback
                };
                if (options.center) {
                    animation.sourceCenter = center;
                    animation.targetCenter = options.center.slice();
                    center = animation.targetCenter;
                }
                if (options.zoom !== undefined) {
                    animation.sourceResolution = resolution;
                    animation.targetResolution = this.getResolutionForZoom(options.zoom);
                    resolution = animation.targetResolution;
                } else if (options.resolution) {
                    animation.sourceResolution = resolution;
                    animation.targetResolution = options.resolution;
                    resolution = animation.targetResolution;
                }
                if (options.rotation !== undefined) {
                    animation.sourceRotation = rotation;
                    const delta = modulo(options.rotation - rotation + Math.PI, 2 * Math.PI) - Math.PI;
                    animation.targetRotation = rotation + delta;
                    rotation = animation.targetRotation;
                }
                if (isNoopAnimation(animation)) {
                    animation.complete = true;
                } else {
                    start += animation.duration;
                }
                series.push(animation);
            }
            this.animations_.push(series);
            this.setHint(ViewHint.ANIMATING, 1);
            this.updateAnimations_();
        }
        getAnimating() {
            return this.hints_[ViewHint.ANIMATING] > 0;
        }
        getInteracting() {
            return this.hints_[ViewHint.INTERACTING] > 0;
        }
        cancelAnimations() {
            this.setHint(ViewHint.ANIMATING, -this.hints_[ViewHint.ANIMATING]);
            let anchor;
            for (let i = 0, ii = this.animations_.length; i < ii; ++i) {
                const series = this.animations_[i];
                if (series[0].callback) {
                    animationCallback(series[0].callback, false);
                }
                if (!anchor) {
                    for (let j = 0, jj = series.length; j < jj; ++j) {
                        const animation = series[j];
                        if (!animation.complete) {
                            anchor = animation.anchor;
                            break;
                        }
                    }
                }
            }
            this.animations_.length = 0;
            this.cancelAnchor_ = anchor;
            this.nextCenter_ = null;
            this.nextResolution_ = NaN;
            this.nextRotation_ = NaN;
        }
        updateAnimations_() {
            if (this.updateAnimationKey_ !== undefined) {
                cancelAnimationFrame(this.updateAnimationKey_);
                this.updateAnimationKey_ = undefined;
            }
            if (!this.getAnimating()) {
                return;
            }
            const now = Date.now();
            let more = false;
            for (let i = this.animations_.length - 1; i >= 0; --i) {
                const series = this.animations_[i];
                let seriesComplete = true;
                for (let j = 0, jj = series.length; j < jj; ++j) {
                    const animation = series[j];
                    if (animation.complete) {
                        continue;
                    }
                    const elapsed = now - animation.start;
                    let fraction = animation.duration > 0 ? elapsed / animation.duration : 1;
                    if (fraction >= 1) {
                        animation.complete = true;
                        fraction = 1;
                    } else {
                        seriesComplete = false;
                    }
                    const progress = animation.easing(fraction);
                    if (animation.sourceCenter) {
                        const x0 = animation.sourceCenter[0];
                        const y0 = animation.sourceCenter[1];
                        const x1 = animation.targetCenter[0];
                        const y1 = animation.targetCenter[1];
                        this.nextCenter_ = animation.targetCenter;
                        const x = x0 + progress * (x1 - x0);
                        const y = y0 + progress * (y1 - y0);
                        this.targetCenter_ = [ x, y ];
                    }
                    if (animation.sourceResolution && animation.targetResolution) {
                        const resolution = progress === 1 ? animation.targetResolution : animation.sourceResolution + progress * (animation.targetResolution - animation.sourceResolution);
                        if (animation.anchor) {
                            const size = this.getViewportSize_(this.getRotation());
                            const constrainedResolution = this.constraints_.resolution(resolution, 0, size, true);
                            this.targetCenter_ = this.calculateCenterZoom(constrainedResolution, animation.anchor);
                        }
                        this.nextResolution_ = animation.targetResolution;
                        this.targetResolution_ = resolution;
                        this.applyTargetState_(true);
                    }
                    if (animation.sourceRotation !== undefined && animation.targetRotation !== undefined) {
                        const rotation = progress === 1 ? modulo(animation.targetRotation + Math.PI, 2 * Math.PI) - Math.PI : animation.sourceRotation + progress * (animation.targetRotation - animation.sourceRotation);
                        if (animation.anchor) {
                            const constrainedRotation = this.constraints_.rotation(rotation, true);
                            this.targetCenter_ = this.calculateCenterRotate(constrainedRotation, animation.anchor);
                        }
                        this.nextRotation_ = animation.targetRotation;
                        this.targetRotation_ = rotation;
                    }
                    this.applyTargetState_(true);
                    more = true;
                    if (!animation.complete) {
                        break;
                    }
                }
                if (seriesComplete) {
                    this.animations_[i] = null;
                    this.setHint(ViewHint.ANIMATING, -1);
                    this.nextCenter_ = null;
                    this.nextResolution_ = NaN;
                    this.nextRotation_ = NaN;
                    const callback = series[0].callback;
                    if (callback) {
                        animationCallback(callback, true);
                    }
                }
            }
            this.animations_ = this.animations_.filter(Boolean);
            if (more && this.updateAnimationKey_ === undefined) {
                this.updateAnimationKey_ = requestAnimationFrame(this.updateAnimations_.bind(this));
            }
        }
        calculateCenterRotate(rotation, anchor) {
            let center;
            const currentCenter = this.getCenterInternal();
            if (currentCenter !== undefined) {
                center = [ currentCenter[0] - anchor[0], currentCenter[1] - anchor[1] ];
                rotate$1(center, rotation - this.getRotation());
                add(center, anchor);
            }
            return center;
        }
        calculateCenterZoom(resolution, anchor) {
            let center;
            const currentCenter = this.getCenterInternal();
            const currentResolution = this.getResolution();
            if (currentCenter !== undefined && currentResolution !== undefined) {
                const x = anchor[0] - resolution * (anchor[0] - currentCenter[0]) / currentResolution;
                const y = anchor[1] - resolution * (anchor[1] - currentCenter[1]) / currentResolution;
                center = [ x, y ];
            }
            return center;
        }
        getViewportSize_(rotation) {
            const size = this.viewportSize_;
            if (rotation) {
                const w = size[0];
                const h = size[1];
                return [ Math.abs(w * Math.cos(rotation)) + Math.abs(h * Math.sin(rotation)), Math.abs(w * Math.sin(rotation)) + Math.abs(h * Math.cos(rotation)) ];
            }
            return size;
        }
        setViewportSize(size) {
            this.viewportSize_ = Array.isArray(size) ? size.slice() : [ 100, 100 ];
            if (!this.getAnimating()) {
                this.resolveConstraints(0);
            }
        }
        getCenter() {
            const center = this.getCenterInternal();
            if (!center) {
                return center;
            }
            return toUserCoordinate(center, this.getProjection());
        }
        getCenterInternal() {
            return this.get(ViewProperty.CENTER);
        }
        getConstraints() {
            return this.constraints_;
        }
        getConstrainResolution() {
            return this.get("constrainResolution");
        }
        getHints(hints) {
            if (hints !== undefined) {
                hints[0] = this.hints_[0];
                hints[1] = this.hints_[1];
                return hints;
            }
            return this.hints_.slice();
        }
        calculateExtent(size) {
            const extent = this.calculateExtentInternal(size);
            return toUserExtent(extent, this.getProjection());
        }
        calculateExtentInternal(size) {
            size = size || this.getViewportSizeMinusPadding_();
            const center = this.getCenterInternal();
            assert(center, "The view center is not defined");
            const resolution = this.getResolution();
            assert(resolution !== undefined, "The view resolution is not defined");
            const rotation = this.getRotation();
            assert(rotation !== undefined, "The view rotation is not defined");
            return getForViewAndSize(center, resolution, rotation, size);
        }
        getMaxResolution() {
            return this.maxResolution_;
        }
        getMinResolution() {
            return this.minResolution_;
        }
        getMaxZoom() {
            return this.getZoomForResolution(this.minResolution_);
        }
        setMaxZoom(zoom) {
            this.applyOptions_(this.getUpdatedOptions_({
                maxZoom: zoom
            }));
        }
        getMinZoom() {
            return this.getZoomForResolution(this.maxResolution_);
        }
        setMinZoom(zoom) {
            this.applyOptions_(this.getUpdatedOptions_({
                minZoom: zoom
            }));
        }
        setConstrainResolution(enabled) {
            this.applyOptions_(this.getUpdatedOptions_({
                constrainResolution: enabled
            }));
        }
        getProjection() {
            return this.projection_;
        }
        getResolution() {
            return this.get(ViewProperty.RESOLUTION);
        }
        getResolutions() {
            return this.resolutions_;
        }
        getResolutionForExtent(extent, size) {
            return this.getResolutionForExtentInternal(fromUserExtent(extent, this.getProjection()), size);
        }
        getResolutionForExtentInternal(extent, size) {
            size = size || this.getViewportSizeMinusPadding_();
            const xResolution = getWidth(extent) / size[0];
            const yResolution = getHeight(extent) / size[1];
            return Math.max(xResolution, yResolution);
        }
        getResolutionForValueFunction(power) {
            power = power || 2;
            const maxResolution = this.getConstrainedResolution(this.maxResolution_);
            const minResolution = this.minResolution_;
            const max = Math.log(maxResolution / minResolution) / Math.log(power);
            return function(value) {
                const resolution = maxResolution / Math.pow(power, value * max);
                return resolution;
            };
        }
        getRotation() {
            return this.get(ViewProperty.ROTATION);
        }
        getValueForResolutionFunction(power) {
            const logPower = Math.log(power || 2);
            const maxResolution = this.getConstrainedResolution(this.maxResolution_);
            const minResolution = this.minResolution_;
            const max = Math.log(maxResolution / minResolution) / logPower;
            return function(resolution) {
                const value = Math.log(maxResolution / resolution) / logPower / max;
                return value;
            };
        }
        getViewportSizeMinusPadding_(rotation) {
            let size = this.getViewportSize_(rotation);
            const padding = this.padding_;
            if (padding) {
                size = [ size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2] ];
            }
            return size;
        }
        getState() {
            const projection = this.getProjection();
            const resolution = this.getResolution();
            const rotation = this.getRotation();
            let center = this.getCenterInternal();
            const padding = this.padding_;
            if (padding) {
                const reducedSize = this.getViewportSizeMinusPadding_();
                center = calculateCenterOn(center, this.getViewportSize_(), [ reducedSize[0] / 2 + padding[3], reducedSize[1] / 2 + padding[0] ], resolution, rotation);
            }
            return {
                center: center.slice(0),
                projection: projection !== undefined ? projection : null,
                resolution: resolution,
                nextCenter: this.nextCenter_,
                nextResolution: this.nextResolution_,
                nextRotation: this.nextRotation_,
                rotation: rotation,
                zoom: this.getZoom()
            };
        }
        getViewStateAndExtent() {
            return {
                viewState: this.getState(),
                extent: this.calculateExtent()
            };
        }
        getZoom() {
            let zoom;
            const resolution = this.getResolution();
            if (resolution !== undefined) {
                zoom = this.getZoomForResolution(resolution);
            }
            return zoom;
        }
        getZoomForResolution(resolution) {
            let offset = this.minZoom_ || 0;
            let max, zoomFactor;
            if (this.resolutions_) {
                const nearest = linearFindNearest(this.resolutions_, resolution, 1);
                offset = nearest;
                max = this.resolutions_[nearest];
                if (nearest == this.resolutions_.length - 1) {
                    zoomFactor = 2;
                } else {
                    zoomFactor = max / this.resolutions_[nearest + 1];
                }
            } else {
                max = this.maxResolution_;
                zoomFactor = this.zoomFactor_;
            }
            return offset + Math.log(max / resolution) / Math.log(zoomFactor);
        }
        getResolutionForZoom(zoom) {
            if (this.resolutions_) {
                if (this.resolutions_.length <= 1) {
                    return 0;
                }
                const baseLevel = clamp(Math.floor(zoom), 0, this.resolutions_.length - 2);
                const zoomFactor = this.resolutions_[baseLevel] / this.resolutions_[baseLevel + 1];
                return this.resolutions_[baseLevel] / Math.pow(zoomFactor, clamp(zoom - baseLevel, 0, 1));
            }
            return this.maxResolution_ / Math.pow(this.zoomFactor_, zoom - this.minZoom_);
        }
        fit(geometryOrExtent, options) {
            let geometry;
            assert(Array.isArray(geometryOrExtent) || typeof geometryOrExtent.getSimplifiedGeometry === "function", "Invalid extent or geometry provided as `geometry`");
            if (Array.isArray(geometryOrExtent)) {
                assert(!isEmpty(geometryOrExtent), "Cannot fit empty extent provided as `geometry`");
                const extent = fromUserExtent(geometryOrExtent, this.getProjection());
                geometry = fromExtent(extent);
            } else if (geometryOrExtent.getType() === "Circle") {
                const extent = fromUserExtent(geometryOrExtent.getExtent(), this.getProjection());
                geometry = fromExtent(extent);
                geometry.rotate(this.getRotation(), getCenter(extent));
            } else {
                {
                    geometry = geometryOrExtent;
                }
            }
            this.fitInternal(geometry, options);
        }
        rotatedExtentForGeometry(geometry) {
            const rotation = this.getRotation();
            const cosAngle = Math.cos(rotation);
            const sinAngle = Math.sin(-rotation);
            const coords = geometry.getFlatCoordinates();
            const stride = geometry.getStride();
            let minRotX = +Infinity;
            let minRotY = +Infinity;
            let maxRotX = -Infinity;
            let maxRotY = -Infinity;
            for (let i = 0, ii = coords.length; i < ii; i += stride) {
                const rotX = coords[i] * cosAngle - coords[i + 1] * sinAngle;
                const rotY = coords[i] * sinAngle + coords[i + 1] * cosAngle;
                minRotX = Math.min(minRotX, rotX);
                minRotY = Math.min(minRotY, rotY);
                maxRotX = Math.max(maxRotX, rotX);
                maxRotY = Math.max(maxRotY, rotY);
            }
            return [ minRotX, minRotY, maxRotX, maxRotY ];
        }
        fitInternal(geometry, options) {
            options = options || {};
            let size = options.size;
            if (!size) {
                size = this.getViewportSizeMinusPadding_();
            }
            const padding = options.padding !== undefined ? options.padding : [ 0, 0, 0, 0 ];
            const nearest = options.nearest !== undefined ? options.nearest : false;
            let minResolution;
            if (options.minResolution !== undefined) {
                minResolution = options.minResolution;
            } else if (options.maxZoom !== undefined) {
                minResolution = this.getResolutionForZoom(options.maxZoom);
            } else {
                minResolution = 0;
            }
            const rotatedExtent = this.rotatedExtentForGeometry(geometry);
            let resolution = this.getResolutionForExtentInternal(rotatedExtent, [ size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2] ]);
            resolution = isNaN(resolution) ? minResolution : Math.max(resolution, minResolution);
            resolution = this.getConstrainedResolution(resolution, nearest ? 0 : 1);
            const rotation = this.getRotation();
            const sinAngle = Math.sin(rotation);
            const cosAngle = Math.cos(rotation);
            const centerRot = getCenter(rotatedExtent);
            centerRot[0] += (padding[1] - padding[3]) / 2 * resolution;
            centerRot[1] += (padding[0] - padding[2]) / 2 * resolution;
            const centerX = centerRot[0] * cosAngle - centerRot[1] * sinAngle;
            const centerY = centerRot[1] * cosAngle + centerRot[0] * sinAngle;
            const center = this.getConstrainedCenter([ centerX, centerY ], resolution);
            const callback = options.callback ? options.callback : VOID;
            if (options.duration !== undefined) {
                this.animateInternal({
                    resolution: resolution,
                    center: center,
                    duration: options.duration,
                    easing: options.easing
                }, callback);
            } else {
                this.targetResolution_ = resolution;
                this.targetCenter_ = center;
                this.applyTargetState_(false, true);
                animationCallback(callback, true);
            }
        }
        centerOn(coordinate, size, position) {
            this.centerOnInternal(fromUserCoordinate(coordinate, this.getProjection()), size, position);
        }
        centerOnInternal(coordinate, size, position) {
            this.setCenterInternal(calculateCenterOn(coordinate, size, position, this.getResolution(), this.getRotation()));
        }
        calculateCenterShift(center, resolution, rotation, size) {
            let centerShift;
            const padding = this.padding_;
            if (padding && center) {
                const reducedSize = this.getViewportSizeMinusPadding_(-rotation);
                const shiftedCenter = calculateCenterOn(center, size, [ reducedSize[0] / 2 + padding[3], reducedSize[1] / 2 + padding[0] ], resolution, rotation);
                centerShift = [ center[0] - shiftedCenter[0], center[1] - shiftedCenter[1] ];
            }
            return centerShift;
        }
        isDef() {
            return !!this.getCenterInternal() && this.getResolution() !== undefined;
        }
        adjustCenter(deltaCoordinates) {
            const center = toUserCoordinate(this.targetCenter_, this.getProjection());
            this.setCenter([ center[0] + deltaCoordinates[0], center[1] + deltaCoordinates[1] ]);
        }
        adjustCenterInternal(deltaCoordinates) {
            const center = this.targetCenter_;
            this.setCenterInternal([ center[0] + deltaCoordinates[0], center[1] + deltaCoordinates[1] ]);
        }
        adjustResolution(ratio, anchor) {
            anchor = anchor && fromUserCoordinate(anchor, this.getProjection());
            this.adjustResolutionInternal(ratio, anchor);
        }
        adjustResolutionInternal(ratio, anchor) {
            const isMoving = this.getAnimating() || this.getInteracting();
            const size = this.getViewportSize_(this.getRotation());
            const newResolution = this.constraints_.resolution(this.targetResolution_ * ratio, 0, size, isMoving);
            if (anchor) {
                this.targetCenter_ = this.calculateCenterZoom(newResolution, anchor);
            }
            this.targetResolution_ *= ratio;
            this.applyTargetState_();
        }
        adjustZoom(delta, anchor) {
            this.adjustResolution(Math.pow(this.zoomFactor_, -delta), anchor);
        }
        adjustRotation(delta, anchor) {
            if (anchor) {
                anchor = fromUserCoordinate(anchor, this.getProjection());
            }
            this.adjustRotationInternal(delta, anchor);
        }
        adjustRotationInternal(delta, anchor) {
            const isMoving = this.getAnimating() || this.getInteracting();
            const newRotation = this.constraints_.rotation(this.targetRotation_ + delta, isMoving);
            if (anchor) {
                this.targetCenter_ = this.calculateCenterRotate(newRotation, anchor);
            }
            this.targetRotation_ += delta;
            this.applyTargetState_();
        }
        setCenter(center) {
            this.setCenterInternal(center ? fromUserCoordinate(center, this.getProjection()) : center);
        }
        setCenterInternal(center) {
            this.targetCenter_ = center;
            this.applyTargetState_();
        }
        setHint(hint, delta) {
            this.hints_[hint] += delta;
            this.changed();
            return this.hints_[hint];
        }
        setResolution(resolution) {
            this.targetResolution_ = resolution;
            this.applyTargetState_();
        }
        setRotation(rotation) {
            this.targetRotation_ = rotation;
            this.applyTargetState_();
        }
        setZoom(zoom) {
            this.setResolution(this.getResolutionForZoom(zoom));
        }
        applyTargetState_(doNotCancelAnims, forceMoving) {
            const isMoving = this.getAnimating() || this.getInteracting() || forceMoving;
            const newRotation = this.constraints_.rotation(this.targetRotation_, isMoving);
            const size = this.getViewportSize_(newRotation);
            const newResolution = this.constraints_.resolution(this.targetResolution_, 0, size, isMoving);
            const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size, isMoving, this.calculateCenterShift(this.targetCenter_, newResolution, newRotation, size));
            if (this.get(ViewProperty.ROTATION) !== newRotation) {
                this.set(ViewProperty.ROTATION, newRotation);
            }
            if (this.get(ViewProperty.RESOLUTION) !== newResolution) {
                this.set(ViewProperty.RESOLUTION, newResolution);
                this.set("zoom", this.getZoom(), true);
            }
            if (!newCenter || !this.get(ViewProperty.CENTER) || !equals(this.get(ViewProperty.CENTER), newCenter)) {
                this.set(ViewProperty.CENTER, newCenter);
            }
            if (this.getAnimating() && !doNotCancelAnims) {
                this.cancelAnimations();
            }
            this.cancelAnchor_ = undefined;
        }
        resolveConstraints(duration, resolutionDirection, anchor) {
            duration = duration !== undefined ? duration : 200;
            const direction = resolutionDirection || 0;
            const newRotation = this.constraints_.rotation(this.targetRotation_);
            const size = this.getViewportSize_(newRotation);
            const newResolution = this.constraints_.resolution(this.targetResolution_, direction, size);
            const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size, false, this.calculateCenterShift(this.targetCenter_, newResolution, newRotation, size));
            if (duration === 0 && !this.cancelAnchor_) {
                this.targetResolution_ = newResolution;
                this.targetRotation_ = newRotation;
                this.targetCenter_ = newCenter;
                this.applyTargetState_();
                return;
            }
            anchor = anchor || (duration === 0 ? this.cancelAnchor_ : undefined);
            this.cancelAnchor_ = undefined;
            if (this.getResolution() !== newResolution || this.getRotation() !== newRotation || !this.getCenterInternal() || !equals(this.getCenterInternal(), newCenter)) {
                if (this.getAnimating()) {
                    this.cancelAnimations();
                }
                this.animateInternal({
                    rotation: newRotation,
                    center: newCenter,
                    resolution: newResolution,
                    duration: duration,
                    easing: easeOut,
                    anchor: anchor
                });
            }
        }
        beginInteraction() {
            this.resolveConstraints(0);
            this.setHint(ViewHint.INTERACTING, 1);
        }
        endInteraction(duration, resolutionDirection, anchor) {
            anchor = anchor && fromUserCoordinate(anchor, this.getProjection());
            this.endInteractionInternal(duration, resolutionDirection, anchor);
        }
        endInteractionInternal(duration, resolutionDirection, anchor) {
            if (!this.getInteracting()) {
                return;
            }
            this.setHint(ViewHint.INTERACTING, -1);
            this.resolveConstraints(duration, resolutionDirection, anchor);
        }
        getConstrainedCenter(targetCenter, targetResolution) {
            const size = this.getViewportSize_(this.getRotation());
            return this.constraints_.center(targetCenter, targetResolution || this.getResolution(), size);
        }
        getConstrainedZoom(targetZoom, direction) {
            const targetRes = this.getResolutionForZoom(targetZoom);
            return this.getZoomForResolution(this.getConstrainedResolution(targetRes, direction));
        }
        getConstrainedResolution(targetResolution, direction) {
            direction = direction || 0;
            const size = this.getViewportSize_(this.getRotation());
            return this.constraints_.resolution(targetResolution, direction, size);
        }
    }
    function animationCallback(callback, returnValue) {
        setTimeout(function() {
            callback(returnValue);
        }, 0);
    }
    function createCenterConstraint(options) {
        if (options.extent !== undefined) {
            const smooth = options.smoothExtentConstraint !== undefined ? options.smoothExtentConstraint : true;
            return createExtent(options.extent, options.constrainOnlyCenter, smooth);
        }
        const projection = createProjection(options.projection, "EPSG:3857");
        if (options.multiWorld !== true && projection.isGlobal()) {
            const extent = projection.getExtent().slice();
            extent[0] = -Infinity;
            extent[2] = Infinity;
            return createExtent(extent, false, false);
        }
        return none$1;
    }
    function createResolutionConstraint(options) {
        let resolutionConstraint;
        let maxResolution;
        let minResolution;
        const defaultMaxZoom = 28;
        const defaultZoomFactor = 2;
        let minZoom = options.minZoom !== undefined ? options.minZoom : DEFAULT_MIN_ZOOM;
        let maxZoom = options.maxZoom !== undefined ? options.maxZoom : defaultMaxZoom;
        const zoomFactor = options.zoomFactor !== undefined ? options.zoomFactor : defaultZoomFactor;
        const multiWorld = options.multiWorld !== undefined ? options.multiWorld : false;
        const smooth = options.smoothResolutionConstraint !== undefined ? options.smoothResolutionConstraint : true;
        const showFullExtent = options.showFullExtent !== undefined ? options.showFullExtent : false;
        const projection = createProjection(options.projection, "EPSG:3857");
        const projExtent = projection.getExtent();
        let constrainOnlyCenter = options.constrainOnlyCenter;
        let extent = options.extent;
        if (!multiWorld && !extent && projection.isGlobal()) {
            constrainOnlyCenter = false;
            extent = projExtent;
        }
        if (options.resolutions !== undefined) {
            const resolutions = options.resolutions;
            maxResolution = resolutions[minZoom];
            minResolution = resolutions[maxZoom] !== undefined ? resolutions[maxZoom] : resolutions[resolutions.length - 1];
            if (options.constrainResolution) {
                resolutionConstraint = createSnapToResolutions(resolutions, smooth, !constrainOnlyCenter && extent, showFullExtent);
            } else {
                resolutionConstraint = createMinMaxResolution(maxResolution, minResolution, smooth, !constrainOnlyCenter && extent, showFullExtent);
            }
        } else {
            const size = !projExtent ? 360 * METERS_PER_UNIT$1.degrees / projection.getMetersPerUnit() : Math.max(getWidth(projExtent), getHeight(projExtent));
            const defaultMaxResolution = size / DEFAULT_TILE_SIZE / Math.pow(defaultZoomFactor, DEFAULT_MIN_ZOOM);
            const defaultMinResolution = defaultMaxResolution / Math.pow(defaultZoomFactor, defaultMaxZoom - DEFAULT_MIN_ZOOM);
            maxResolution = options.maxResolution;
            if (maxResolution !== undefined) {
                minZoom = 0;
            } else {
                maxResolution = defaultMaxResolution / Math.pow(zoomFactor, minZoom);
            }
            minResolution = options.minResolution;
            if (minResolution === undefined) {
                if (options.maxZoom !== undefined) {
                    if (options.maxResolution !== undefined) {
                        minResolution = maxResolution / Math.pow(zoomFactor, maxZoom);
                    } else {
                        minResolution = defaultMaxResolution / Math.pow(zoomFactor, maxZoom);
                    }
                } else {
                    minResolution = defaultMinResolution;
                }
            }
            maxZoom = minZoom + Math.floor(Math.log(maxResolution / minResolution) / Math.log(zoomFactor));
            minResolution = maxResolution / Math.pow(zoomFactor, maxZoom - minZoom);
            if (options.constrainResolution) {
                resolutionConstraint = createSnapToPower(zoomFactor, maxResolution, minResolution, smooth, !constrainOnlyCenter && extent, showFullExtent);
            } else {
                resolutionConstraint = createMinMaxResolution(maxResolution, minResolution, smooth, !constrainOnlyCenter && extent, showFullExtent);
            }
        }
        return {
            constraint: resolutionConstraint,
            maxResolution: maxResolution,
            minResolution: minResolution,
            minZoom: minZoom,
            zoomFactor: zoomFactor
        };
    }
    function createRotationConstraint(options) {
        const enableRotation = options.enableRotation !== undefined ? options.enableRotation : true;
        if (enableRotation) {
            const constrainRotation = options.constrainRotation;
            if (constrainRotation === undefined || constrainRotation === true) {
                return createSnapToZero();
            }
            if (constrainRotation === false) {
                return none;
            }
            if (typeof constrainRotation === "number") {
                return createSnapToN(constrainRotation);
            }
            return none;
        }
        return disable;
    }
    function isNoopAnimation(animation) {
        if (animation.sourceCenter && animation.targetCenter) {
            if (!equals(animation.sourceCenter, animation.targetCenter)) {
                return false;
            }
        }
        if (animation.sourceResolution !== animation.targetResolution) {
            return false;
        }
        if (animation.sourceRotation !== animation.targetRotation) {
            return false;
        }
        return true;
    }
    function calculateCenterOn(coordinate, size, position, resolution, rotation) {
        const cosAngle = Math.cos(-rotation);
        let sinAngle = Math.sin(-rotation);
        let rotX = coordinate[0] * cosAngle - coordinate[1] * sinAngle;
        let rotY = coordinate[1] * cosAngle + coordinate[0] * sinAngle;
        rotX += (size[0] / 2 - position[0]) * resolution;
        rotY += (position[1] - size[1] / 2) * resolution;
        sinAngle = -sinAngle;
        const centerX = rotX * cosAngle - rotY * sinAngle;
        const centerY = rotY * cosAngle + rotX * sinAngle;
        return [ centerX, centerY ];
    }
    class Layer extends BaseLayer {
        constructor(options) {
            const baseOptions = Object.assign({}, options);
            delete baseOptions.source;
            super(baseOptions);
            this.on;
            this.once;
            this.un;
            this.mapPrecomposeKey_ = null;
            this.mapRenderKey_ = null;
            this.sourceChangeKey_ = null;
            this.renderer_ = null;
            this.sourceReady_ = false;
            this.rendered = false;
            if (options.render) {
                this.render = options.render;
            }
            if (options.map) {
                this.setMap(options.map);
            }
            this.addChangeListener(LayerProperty.SOURCE, this.handleSourcePropertyChange_);
            const source = options.source ? options.source : null;
            this.setSource(source);
        }
        getLayersArray(array) {
            array = array ? array : [];
            array.push(this);
            return array;
        }
        getLayerStatesArray(states) {
            states = states ? states : [];
            states.push(this.getLayerState());
            return states;
        }
        getSource() {
            return this.get(LayerProperty.SOURCE) || null;
        }
        getRenderSource() {
            return this.getSource();
        }
        getSourceState() {
            const source = this.getSource();
            return !source ? "undefined" : source.getState();
        }
        handleSourceChange_() {
            this.changed();
            if (this.sourceReady_ || this.getSource().getState() !== "ready") {
                return;
            }
            this.sourceReady_ = true;
            this.dispatchEvent("sourceready");
        }
        handleSourcePropertyChange_() {
            if (this.sourceChangeKey_) {
                unlistenByKey(this.sourceChangeKey_);
                this.sourceChangeKey_ = null;
            }
            this.sourceReady_ = false;
            const source = this.getSource();
            if (source) {
                this.sourceChangeKey_ = listen(source, EventType.CHANGE, this.handleSourceChange_, this);
                if (source.getState() === "ready") {
                    this.sourceReady_ = true;
                    setTimeout(() => {
                        this.dispatchEvent("sourceready");
                    }, 0);
                }
            }
            this.changed();
        }
        getFeatures(pixel) {
            if (!this.renderer_) {
                return Promise.resolve([]);
            }
            return this.renderer_.getFeatures(pixel);
        }
        getData(pixel) {
            if (!this.renderer_ || !this.rendered) {
                return null;
            }
            return this.renderer_.getData(pixel);
        }
        isVisible(view) {
            let frameState;
            const map = this.getMapInternal();
            if (!view && map) {
                view = map.getView();
            }
            if (view instanceof View) {
                frameState = {
                    viewState: view.getState(),
                    extent: view.calculateExtent()
                };
            } else {
                frameState = view;
            }
            if (!frameState.layerStatesArray && map) {
                frameState.layerStatesArray = map.getLayerGroup().getLayerStatesArray();
            }
            let layerState;
            if (frameState.layerStatesArray) {
                layerState = frameState.layerStatesArray.find(layerState => layerState.layer === this);
            } else {
                layerState = this.getLayerState();
            }
            const layerExtent = this.getExtent();
            return inView(layerState, frameState.viewState) && (!layerExtent || intersects$1(layerExtent, frameState.extent));
        }
        getAttributions(view) {
            if (!this.isVisible(view)) {
                return [];
            }
            let getAttributions;
            const source = this.getSource();
            if (source) {
                getAttributions = source.getAttributions();
            }
            if (!getAttributions) {
                return [];
            }
            const frameState = view instanceof View ? view.getViewStateAndExtent() : view;
            let attributions = getAttributions(frameState);
            if (!Array.isArray(attributions)) {
                attributions = [ attributions ];
            }
            return attributions;
        }
        render(frameState, target) {
            const layerRenderer = this.getRenderer();
            if (layerRenderer.prepareFrame(frameState)) {
                this.rendered = true;
                return layerRenderer.renderFrame(frameState, target);
            }
            return null;
        }
        unrender() {
            this.rendered = false;
        }
        getDeclutter() {
            return undefined;
        }
        renderDeclutter(frameState, layerState) {}
        renderDeferred(frameState) {
            const layerRenderer = this.getRenderer();
            if (!layerRenderer) {
                return;
            }
            layerRenderer.renderDeferred(frameState);
        }
        setMapInternal(map) {
            if (!map) {
                this.unrender();
            }
            this.set(LayerProperty.MAP, map);
        }
        getMapInternal() {
            return this.get(LayerProperty.MAP);
        }
        setMap(map) {
            if (this.mapPrecomposeKey_) {
                unlistenByKey(this.mapPrecomposeKey_);
                this.mapPrecomposeKey_ = null;
            }
            if (!map) {
                this.changed();
            }
            if (this.mapRenderKey_) {
                unlistenByKey(this.mapRenderKey_);
                this.mapRenderKey_ = null;
            }
            if (map) {
                this.mapPrecomposeKey_ = listen(map, RenderEventType.PRECOMPOSE, function(evt) {
                    const renderEvent = evt;
                    const layerStatesArray = renderEvent.frameState.layerStatesArray;
                    const layerState = this.getLayerState(false);
                    assert(!layerStatesArray.some(function(arrayLayerState) {
                        return arrayLayerState.layer === layerState.layer;
                    }), "A layer can only be added to the map once. Use either `layer.setMap()` or `map.addLayer()`, not both.");
                    layerStatesArray.push(layerState);
                }, this);
                this.mapRenderKey_ = listen(this, EventType.CHANGE, map.render, map);
                this.changed();
            }
        }
        setSource(source) {
            this.set(LayerProperty.SOURCE, source);
        }
        getRenderer() {
            if (!this.renderer_) {
                this.renderer_ = this.createRenderer();
            }
            return this.renderer_;
        }
        hasRenderer() {
            return !!this.renderer_;
        }
        createRenderer() {
            return null;
        }
        disposeInternal() {
            if (this.renderer_) {
                this.renderer_.dispose();
                delete this.renderer_;
            }
            this.setSource(null);
            super.disposeInternal();
        }
    }
    function inView(layerState, viewState) {
        if (!layerState.visible) {
            return false;
        }
        const resolution = viewState.resolution;
        if (resolution < layerState.minResolution || resolution >= layerState.maxResolution) {
            return false;
        }
        const zoom = viewState.zoom;
        return zoom > layerState.minZoom && zoom <= layerState.maxZoom;
    }
    function quickselect(arr, k, left, right, compare) {
        quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare);
    }
    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 = .5 * Math.exp(2 * z / 3);
                var sd = .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(a, b) {
        return a < b ? -1 : a > b ? 1 : 0;
    }
    let RBush$1 = class RBush {
        constructor(maxEntries = 9) {
            this._maxEntries = Math.max(4, maxEntries);
            this._minEntries = Math.max(2, Math.ceil(this._maxEntries * .4));
            this.clear();
        }
        all() {
            return this._all(this.data, []);
        }
        search(bbox) {
            let node = this.data;
            const result = [];
            if (!intersects(bbox, node)) return result;
            const toBBox = this.toBBox;
            const nodesToSearch = [];
            while (node) {
                for (let i = 0; i < node.children.length; i++) {
                    const child = node.children[i];
                    const childBBox = node.leaf ? toBBox(child) : child;
                    if (intersects(bbox, childBBox)) {
                        if (node.leaf) result.push(child); else if (contains(bbox, childBBox)) this._all(child, result); else nodesToSearch.push(child);
                    }
                }
                node = nodesToSearch.pop();
            }
            return result;
        }
        collides(bbox) {
            let node = this.data;
            if (!intersects(bbox, node)) return false;
            const nodesToSearch = [];
            while (node) {
                for (let i = 0; i < node.children.length; i++) {
                    const child = node.children[i];
                    const childBBox = node.leaf ? this.toBBox(child) : child;
                    if (intersects(bbox, childBBox)) {
                        if (node.leaf || contains(bbox, childBBox)) return true;
                        nodesToSearch.push(child);
                    }
                }
                node = nodesToSearch.pop();
            }
            return false;
        }
        load(data) {
            if (!(data && data.length)) return this;
            if (data.length < this._minEntries) {
                for (let i = 0; i < data.length; i++) {
                    this.insert(data[i]);
                }
                return this;
            }
            let node = this._build(data.slice(), 0, data.length - 1, 0);
            if (!this.data.children.length) {
                this.data = node;
            } else if (this.data.height === node.height) {
                this._splitRoot(this.data, node);
            } else {
                if (this.data.height < node.height) {
                    const tmpNode = this.data;
                    this.data = node;
                    node = tmpNode;
                }
                this._insert(node, this.data.height - node.height - 1, true);
            }
            return this;
        }
        insert(item) {
            if (item) this._insert(item, this.data.height - 1);
            return this;
        }
        clear() {
            this.data = createNode([]);
            return this;
        }
        remove(item, equalsFn) {
            if (!item) return this;
            let node = this.data;
            const bbox = this.toBBox(item);
            const path = [];
            const indexes = [];
            let i, parent, goingUp;
            while (node || path.length) {
                if (!node) {
                    node = path.pop();
                    parent = path[path.length - 1];
                    i = indexes.pop();
                    goingUp = true;
                }
                if (node.leaf) {
                    const index = findItem(item, node.children, equalsFn);
                    if (index !== -1) {
                        node.children.splice(index, 1);
                        path.push(node);
                        this._condense(path);
                        return this;
                    }
                }
                if (!goingUp && !node.leaf && contains(node, bbox)) {
                    path.push(node);
                    indexes.push(i);
                    i = 0;
                    parent = node;
                    node = node.children[0];
                } else if (parent) {
                    i++;
                    node = parent.children[i];
                    goingUp = false;
                } else node = null;
            }
            return this;
        }
        toBBox(item) {
            return item;
        }
        compareMinX(a, b) {
            return a.minX - b.minX;
        }
        compareMinY(a, b) {
            return a.minY - b.minY;
        }
        toJSON() {
            return this.data;
        }
        fromJSON(data) {
            this.data = data;
            return this;
        }
        _all(node, result) {
            const nodesToSearch = [];
            while (node) {
                if (node.leaf) result.push(...node.children); else nodesToSearch.push(...node.children);
                node = nodesToSearch.pop();
            }
            return result;
        }
        _build(items, left, right, height) {
            const N = right - left + 1;
            let M = this._maxEntries;
            let node;
            if (N <= M) {
                node = createNode(items.slice(left, right + 1));
                calcBBox(node, this.toBBox);
                return node;
            }
            if (!height) {
                height = Math.ceil(Math.log(N) / Math.log(M));
                M = Math.ceil(N / Math.pow(M, height - 1));
            }
            node = createNode([]);
            node.leaf = false;
            node.height = height;
            const N2 = Math.ceil(N / M);
            const N1 = N2 * Math.ceil(Math.sqrt(M));
            multiSelect(items, left, right, N1, this.compareMinX);
            for (let i = left; i <= right; i += N1) {
                const right2 = Math.min(i + N1 - 1, right);
                multiSelect(items, i, right2, N2, this.compareMinY);
                for (let j = i; j <= right2; j += N2) {
                    const right3 = Math.min(j + N2 - 1, right2);
                    node.children.push(this._build(items, j, right3, height - 1));
                }
            }
            calcBBox(node, this.toBBox);
            return node;
        }
        _chooseSubtree(bbox, node, level, path) {
            while (true) {
                path.push(node);
                if (node.leaf || path.length - 1 === level) break;
                let minArea = Infinity;
                let minEnlargement = Infinity;
                let targetNode;
                for (let i = 0; i < node.children.length; i++) {
                    const child = node.children[i];
                    const area = bboxArea(child);
                    const enlargement = enlargedArea(bbox, child) - area;
                    if (enlargement < minEnlargement) {
                        minEnlargement = enlargement;
                        minArea = area < minArea ? area : minArea;
                        targetNode = child;
                    } else if (enlargement === minEnlargement) {
                        if (area < minArea) {
                            minArea = area;
                            targetNode = child;
                        }
                    }
                }
                node = targetNode || node.children[0];
            }
            return node;
        }
        _insert(item, level, isNode) {
            const bbox = isNode ? item : this.toBBox(item);
            const insertPath = [];
            const node = this._chooseSubtree(bbox, this.data, level, insertPath);
            node.children.push(item);
            extend(node, bbox);
            while (level >= 0) {
                if (insertPath[level].children.length > this._maxEntries) {
                    this._split(insertPath, level);
                    level--;
                } else break;
            }
            this._adjustParentBBoxes(bbox, insertPath, level);
        }
        _split(insertPath, level) {
            const node = insertPath[level];
            const M = node.children.length;
            const m = this._minEntries;
            this._chooseSplitAxis(node, m, M);
            const splitIndex = this._chooseSplitIndex(node, m, M);
            const newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
            newNode.height = node.height;
            newNode.leaf = node.leaf;
            calcBBox(node, this.toBBox);
            calcBBox(newNode, this.toBBox);
            if (level) insertPath[level - 1].children.push(newNode); else this._splitRoot(node, newNode);
        }
        _splitRoot(node, newNode) {
            this.data = createNode([ node, newNode ]);
            this.data.height = node.height + 1;
            this.data.leaf = false;
            calcBBox(this.data, this.toBBox);
        }
        _chooseSplitIndex(node, m, M) {
            let index;
            let minOverlap = Infinity;
            let minArea = Infinity;
            for (let i = m; i <= M - m; i++) {
                const bbox1 = distBBox(node, 0, i, this.toBBox);
                const bbox2 = distBBox(node, i, M, this.toBBox);
                const overlap = intersectionArea(bbox1, bbox2);
                const area = bboxArea(bbox1) + bboxArea(bbox2);
                if (overlap < minOverlap) {
                    minOverlap = overlap;
                    index = i;
                    minArea = area < minArea ? area : minArea;
                } else if (overlap === minOverlap) {
                    if (area < minArea) {
                        minArea = area;
                        index = i;
                    }
                }
            }
            return index || M - m;
        }
        _chooseSplitAxis(node, m, M) {
            const compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
            const compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
            const xMargin = this._allDistMargin(node, m, M, compareMinX);
            const yMargin = this._allDistMargin(node, m, M, compareMinY);
            if (xMargin < yMargin) node.children.sort(compareMinX);
        }
        _allDistMargin(node, m, M, compare) {
            node.children.sort(compare);
            const toBBox = this.toBBox;
            const leftBBox = distBBox(node, 0, m, toBBox);
            const rightBBox = distBBox(node, M - m, M, toBBox);
            let margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
            for (let i = m; i < M - m; i++) {
                const child = node.children[i];
                extend(leftBBox, node.leaf ? toBBox(child) : child);
                margin += bboxMargin(leftBBox);
            }
            for (let i = M - m - 1; i >= m; i--) {
                const child = node.children[i];
                extend(rightBBox, node.leaf ? toBBox(child) : child);
                margin += bboxMargin(rightBBox);
            }
            return margin;
        }
        _adjustParentBBoxes(bbox, path, level) {
            for (let i = level; i >= 0; i--) {
                extend(path[i], bbox);
            }
        }
        _condense(path) {
            for (let i = path.length - 1, siblings; i >= 0; i--) {
                if (path[i].children.length === 0) {
                    if (i > 0) {
                        siblings = path[i - 1].children;
                        siblings.splice(siblings.indexOf(path[i]), 1);
                    } else this.clear();
                } else calcBBox(path[i], this.toBBox);
            }
        }
    };
    function findItem(item, items, equalsFn) {
        if (!equalsFn) return items.indexOf(item);
        for (let i = 0; i < items.length; i++) {
            if (equalsFn(item, items[i])) return i;
        }
        return -1;
    }
    function calcBBox(node, toBBox) {
        distBBox(node, 0, node.children.length, toBBox, node);
    }
    function distBBox(node, k, p, toBBox, destNode) {
        if (!destNode) destNode = createNode(null);
        destNode.minX = Infinity;
        destNode.minY = Infinity;
        destNode.maxX = -Infinity;
        destNode.maxY = -Infinity;
        for (let i = k; i < p; i++) {
            const child = node.children[i];
            extend(destNode, node.leaf ? toBBox(child) : child);
        }
        return destNode;
    }
    function extend(a, b) {
        a.minX = Math.min(a.minX, b.minX);
        a.minY = Math.min(a.minY, b.minY);
        a.maxX = Math.max(a.maxX, b.maxX);
        a.maxY = Math.max(a.maxY, b.maxY);
        return a;
    }
    function compareNodeMinX(a, b) {
        return a.minX - b.minX;
    }
    function compareNodeMinY(a, b) {
        return a.minY - b.minY;
    }
    function bboxArea(a) {
        return (a.maxX - a.minX) * (a.maxY - a.minY);
    }
    function bboxMargin(a) {
        return a.maxX - a.minX + (a.maxY - a.minY);
    }
    function enlargedArea(a, b) {
        return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) * (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
    }
    function intersectionArea(a, b) {
        const minX = Math.max(a.minX, b.minX);
        const minY = Math.max(a.minY, b.minY);
        const maxX = Math.min(a.maxX, b.maxX);
        const maxY = Math.min(a.maxY, b.maxY);
        return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
    }
    function contains(a, b) {
        return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
    }
    function intersects(a, b) {
        return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
    }
    function createNode(children) {
        return {
            children: children,
            height: 1,
            leaf: true,
            minX: Infinity,
            minY: Infinity,
            maxX: -Infinity,
            maxY: -Infinity
        };
    }
    function multiSelect(arr, left, right, n, compare) {
        const stack = [ left, right ];
        while (stack.length) {
            right = stack.pop();
            left = stack.pop();
            if (right - left <= n) continue;
            const mid = left + Math.ceil((right - left) / n / 2) * n;
            quickselect(arr, mid, left, right, compare);
            stack.push(left, mid, mid, right);
        }
    }
    var ImageState = {
        IDLE: 0,
        LOADING: 1,
        LOADED: 2,
        ERROR: 3
    };
    function hasArea(size) {
        return size[0] > 0 && size[1] > 0;
    }
    function scale(size, ratio, dest) {
        if (dest === undefined) {
            dest = [ 0, 0 ];
        }
        dest[0] = size[0] * ratio + .5 | 0;
        dest[1] = size[1] * ratio + .5 | 0;
        return dest;
    }
    function toSize(size, dest) {
        if (Array.isArray(size)) {
            return size;
        }
        if (dest === undefined) {
            dest = [ size, size ];
        } else {
            dest[0] = size;
            dest[1] = size;
        }
        return dest;
    }
    class ImageStyle {
        constructor(options) {
            this.opacity_ = options.opacity;
            this.rotateWithView_ = options.rotateWithView;
            this.rotation_ = options.rotation;
            this.scale_ = options.scale;
            this.scaleArray_ = toSize(options.scale);
            this.displacement_ = options.displacement;
            this.declutterMode_ = options.declutterMode;
        }
        clone() {
            const scale = this.getScale();
            return new ImageStyle({
                opacity: this.getOpacity(),
                scale: Array.isArray(scale) ? scale.slice() : scale,
                rotation: this.getRotation(),
                rotateWithView: this.getRotateWithView(),
                displacement: this.getDisplacement().slice(),
                declutterMode: this.getDeclutterMode()
            });
        }
        getOpacity() {
            return this.opacity_;
        }
        getRotateWithView() {
            return this.rotateWithView_;
        }
        getRotation() {
            return this.rotation_;
        }
        getScale() {
            return this.scale_;
        }
        getScaleArray() {
            return this.scaleArray_;
        }
        getDisplacement() {
            return this.displacement_;
        }
        getDeclutterMode() {
            return this.declutterMode_;
        }
        getAnchor() {
            return abstract();
        }
        getImage(pixelRatio) {
            return abstract();
        }
        getHitDetectionImage() {
            return abstract();
        }
        getPixelRatio(pixelRatio) {
            return 1;
        }
        getImageState() {
            return abstract();
        }
        getImageSize() {
            return abstract();
        }
        getOrigin() {
            return abstract();
        }
        getSize() {
            return abstract();
        }
        setDisplacement(displacement) {
            this.displacement_ = displacement;
        }
        setOpacity(opacity) {
            this.opacity_ = opacity;
        }
        setRotateWithView(rotateWithView) {
            this.rotateWithView_ = rotateWithView;
        }
        setRotation(rotation) {
            this.rotation_ = rotation;
        }
        setScale(scale) {
            this.scale_ = scale;
            this.scaleArray_ = toSize(scale);
        }
        listenImageChange(listener) {
            abstract();
        }
        load() {
            abstract();
        }
        unlistenImageChange(listener) {
            abstract();
        }
        ready() {
            return Promise.resolve();
        }
    }
    const rgb = {
        name: "rgb",
        min: [ 0, 0, 0 ],
        max: [ 255, 255, 255 ],
        channel: [ "red", "green", "blue" ],
        alias: [ "RGB" ]
    };
    const xyz = {
        name: "xyz",
        min: [ 0, 0, 0 ],
        channel: [ "X", "Y", "Z" ],
        alias: [ "XYZ", "ciexyz", "cie1931" ],
        whitepoint: {
            2: {
                A: [ 109.85, 100, 35.585 ],
                C: [ 98.074, 100, 118.232 ],
                D50: [ 96.422, 100, 82.521 ],
                D55: [ 95.682, 100, 92.149 ],
                D65: [ 95.045592705167, 100, 108.9057750759878 ],
                D75: [ 94.972, 100, 122.638 ],
                F2: [ 99.187, 100, 67.395 ],
                F7: [ 95.044, 100, 108.755 ],
                F11: [ 100.966, 100, 64.37 ],
                E: [ 100, 100, 100 ]
            },
            10: {
                A: [ 111.144, 100, 35.2 ],
                C: [ 97.285, 100, 116.145 ],
                D50: [ 96.72, 100, 81.427 ],
                D55: [ 95.799, 100, 90.926 ],
                D65: [ 94.811, 100, 107.304 ],
                D75: [ 94.416, 100, 120.641 ],
                F2: [ 103.28, 100, 69.026 ],
                F7: [ 95.792, 100, 107.687 ],
                F11: [ 103.866, 100, 65.627 ],
                E: [ 100, 100, 100 ]
            }
        }
    };
    xyz.max = xyz.whitepoint[2].D65;
    xyz.rgb = function(_xyz, white) {
        white = white || xyz.whitepoint[2].E;
        var x = _xyz[0] / white[0], y = _xyz[1] / white[1], z = _xyz[2] / white[2], r, g, b;
        r = x * 3.240969941904521 + y * -1.537383177570093 + z * -.498610760293;
        g = x * -.96924363628087 + y * 1.87596750150772 + z * .041555057407175;
        b = x * .055630079696993 + y * -.20397695888897 + z * 1.056971514242878;
        r = r > .0031308 ? 1.055 * Math.pow(r, 1 / 2.4) - .055 : r = r * 12.92;
        g = g > .0031308 ? 1.055 * Math.pow(g, 1 / 2.4) - .055 : g = g * 12.92;
        b = b > .0031308 ? 1.055 * Math.pow(b, 1 / 2.4) - .055 : b = b * 12.92;
        r = Math.min(Math.max(0, r), 1);
        g = Math.min(Math.max(0, g), 1);
        b = Math.min(Math.max(0, b), 1);
        return [ r * 255, g * 255, b * 255 ];
    };
    rgb.xyz = function(rgb, white) {
        var r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255;
        r = r > .04045 ? Math.pow((r + .055) / 1.055, 2.4) : r / 12.92;
        g = g > .04045 ? Math.pow((g + .055) / 1.055, 2.4) : g / 12.92;
        b = b > .04045 ? Math.pow((b + .055) / 1.055, 2.4) : b / 12.92;
        var x = r * .41239079926595 + g * .35758433938387 + b * .18048078840183;
        var y = r * .21263900587151 + g * .71516867876775 + b * .072192315360733;
        var z = r * .019330818715591 + g * .11919477979462 + b * .95053215224966;
        white = white || xyz.whitepoint[2].E;
        return [ x * white[0], y * white[1], z * white[2] ];
    };
    var luv = {
        name: "luv",
        min: [ 0, -134, -140 ],
        max: [ 100, 224, 122 ],
        channel: [ "lightness", "u", "v" ],
        alias: [ "LUV", "cieluv", "cie1976" ],
        xyz: function(arg, i, o) {
            var _u, _v, l, u, v, x, y, z, xn, yn, zn, un, vn;
            l = arg[0], u = arg[1], v = arg[2];
            if (l === 0) return [ 0, 0, 0 ];
            var k = .0011070564598794539;
            i = i || "D65";
            o = o || 2;
            xn = xyz.whitepoint[o][i][0];
            yn = xyz.whitepoint[o][i][1];
            zn = xyz.whitepoint[o][i][2];
            un = 4 * xn / (xn + 15 * yn + 3 * zn);
            vn = 9 * yn / (xn + 15 * yn + 3 * zn);
            _u = u / (13 * l) + un || 0;
            _v = v / (13 * l) + vn || 0;
            y = l > 8 ? yn * Math.pow((l + 16) / 116, 3) : yn * l * k;
            x = y * 9 * _u / (4 * _v) || 0;
            z = y * (12 - 3 * _u - 20 * _v) / (4 * _v) || 0;
            return [ x, y, z ];
        }
    };
    xyz.luv = function(arg, i, o) {
        var _u, _v, l, u, v, x, y, z, xn, yn, zn, un, vn;
        var e = .008856451679035631;
        var k = 903.2962962962961;
        i = i || "D65";
        o = o || 2;
        xn = xyz.whitepoint[o][i][0];
        yn = xyz.whitepoint[o][i][1];
        zn = xyz.whitepoint[o][i][2];
        un = 4 * xn / (xn + 15 * yn + 3 * zn);
        vn = 9 * yn / (xn + 15 * yn + 3 * zn);
        x = arg[0], y = arg[1], z = arg[2];
        _u = 4 * x / (x + 15 * y + 3 * z) || 0;
        _v = 9 * y / (x + 15 * y + 3 * z) || 0;
        var yr = y / yn;
        l = yr <= e ? k * yr : 116 * Math.pow(yr, 1 / 3) - 16;
        u = 13 * l * (_u - un);
        v = 13 * l * (_v - vn);
        return [ l, u, v ];
    };
    var lchuv = {
        name: "lchuv",
        channel: [ "lightness", "chroma", "hue" ],
        alias: [ "LCHuv", "cielchuv" ],
        min: [ 0, 0, 0 ],
        max: [ 100, 100, 360 ],
        luv: function(luv) {
            var l = luv[0], c = luv[1], h = luv[2], u, v, hr;
            hr = h / 360 * 2 * Math.PI;
            u = c * Math.cos(hr);
            v = c * Math.sin(hr);
            return [ l, u, v ];
        },
        xyz: function(arg) {
            return luv.xyz(lchuv.luv(arg));
        }
    };
    luv.lchuv = function(luv) {
        var l = luv[0], u = luv[1], v = luv[2];
        var c = Math.sqrt(u * u + v * v);
        var hr = Math.atan2(v, u);
        var h = hr * 360 / 2 / Math.PI;
        if (h < 0) {
            h += 360;
        }
        return [ l, c, h ];
    };
    xyz.lchuv = function(arg) {
        return luv.lchuv(xyz.luv(arg));
    };
    const colors = {
        aliceblue: [ 240, 248, 255 ],
        antiquewhite: [ 250, 235, 215 ],
        aqua: [ 0, 255, 255 ],
        aquamarine: [ 127, 255, 212 ],
        azure: [ 240, 255, 255 ],
        beige: [ 245, 245, 220 ],
        bisque: [ 255, 228, 196 ],
        black: [ 0, 0, 0 ],
        blanchedalmond: [ 255, 235, 205 ],
        blue: [ 0, 0, 255 ],
        blueviolet: [ 138, 43, 226 ],
        brown: [ 165, 42, 42 ],
        burlywood: [ 222, 184, 135 ],
        cadetblue: [ 95, 158, 160 ],
        chartreuse: [ 127, 255, 0 ],
        chocolate: [ 210, 105, 30 ],
        coral: [ 255, 127, 80 ],
        cornflowerblue: [ 100, 149, 237 ],
        cornsilk: [ 255, 248, 220 ],
        crimson: [ 220, 20, 60 ],
        cyan: [ 0, 255, 255 ],
        darkblue: [ 0, 0, 139 ],
        darkcyan: [ 0, 139, 139 ],
        darkgoldenrod: [ 184, 134, 11 ],
        darkgray: [ 169, 169, 169 ],
        darkgreen: [ 0, 100, 0 ],
        darkgrey: [ 169, 169, 169 ],
        darkkhaki: [ 189, 183, 107 ],
        darkmagenta: [ 139, 0, 139 ],
        darkolivegreen: [ 85, 107, 47 ],
        darkorange: [ 255, 140, 0 ],
        darkorchid: [ 153, 50, 204 ],
        darkred: [ 139, 0, 0 ],
        darksalmon: [ 233, 150, 122 ],
        darkseagreen: [ 143, 188, 143 ],
        darkslateblue: [ 72, 61, 139 ],
        darkslategray: [ 47, 79, 79 ],
        darkslategrey: [ 47, 79, 79 ],
        darkturquoise: [ 0, 206, 209 ],
        darkviolet: [ 148, 0, 211 ],
        deeppink: [ 255, 20, 147 ],
        deepskyblue: [ 0, 191, 255 ],
        dimgray: [ 105, 105, 105 ],
        dimgrey: [ 105, 105, 105 ],
        dodgerblue: [ 30, 144, 255 ],
        firebrick: [ 178, 34, 34 ],
        floralwhite: [ 255, 250, 240 ],
        forestgreen: [ 34, 139, 34 ],
        fuchsia: [ 255, 0, 255 ],
        gainsboro: [ 220, 220, 220 ],
        ghostwhite: [ 248, 248, 255 ],
        gold: [ 255, 215, 0 ],
        goldenrod: [ 218, 165, 32 ],
        gray: [ 128, 128, 128 ],
        green: [ 0, 128, 0 ],
        greenyellow: [ 173, 255, 47 ],
        grey: [ 128, 128, 128 ],
        honeydew: [ 240, 255, 240 ],
        hotpink: [ 255, 105, 180 ],
        indianred: [ 205, 92, 92 ],
        indigo: [ 75, 0, 130 ],
        ivory: [ 255, 255, 240 ],
        khaki: [ 240, 230, 140 ],
        lavender: [ 230, 230, 250 ],
        lavenderblush: [ 255, 240, 245 ],
        lawngreen: [ 124, 252, 0 ],
        lemonchiffon: [ 255, 250, 205 ],
        lightblue: [ 173, 216, 230 ],
        lightcoral: [ 240, 128, 128 ],
        lightcyan: [ 224, 255, 255 ],
        lightgoldenrodyellow: [ 250, 250, 210 ],
        lightgray: [ 211, 211, 211 ],
        lightgreen: [ 144, 238, 144 ],
        lightgrey: [ 211, 211, 211 ],
        lightpink: [ 255, 182, 193 ],
        lightsalmon: [ 255, 160, 122 ],
        lightseagreen: [ 32, 178, 170 ],
        lightskyblue: [ 135, 206, 250 ],
        lightslategray: [ 119, 136, 153 ],
        lightslategrey: [ 119, 136, 153 ],
        lightsteelblue: [ 176, 196, 222 ],
        lightyellow: [ 255, 255, 224 ],
        lime: [ 0, 255, 0 ],
        limegreen: [ 50, 205, 50 ],
        linen: [ 250, 240, 230 ],
        magenta: [ 255, 0, 255 ],
        maroon: [ 128, 0, 0 ],
        mediumaquamarine: [ 102, 205, 170 ],
        mediumblue: [ 0, 0, 205 ],
        mediumorchid: [ 186, 85, 211 ],
        mediumpurple: [ 147, 112, 219 ],
        mediumseagreen: [ 60, 179, 113 ],
        mediumslateblue: [ 123, 104, 238 ],
        mediumspringgreen: [ 0, 250, 154 ],
        mediumturquoise: [ 72, 209, 204 ],
        mediumvioletred: [ 199, 21, 133 ],
        midnightblue: [ 25, 25, 112 ],
        mintcream: [ 245, 255, 250 ],
        mistyrose: [ 255, 228, 225 ],
        moccasin: [ 255, 228, 181 ],
        navajowhite: [ 255, 222, 173 ],
        navy: [ 0, 0, 128 ],
        oldlace: [ 253, 245, 230 ],
        olive: [ 128, 128, 0 ],
        olivedrab: [ 107, 142, 35 ],
        orange: [ 255, 165, 0 ],
        orangered: [ 255, 69, 0 ],
        orchid: [ 218, 112, 214 ],
        palegoldenrod: [ 238, 232, 170 ],
        palegreen: [ 152, 251, 152 ],
        paleturquoise: [ 175, 238, 238 ],
        palevioletred: [ 219, 112, 147 ],
        papayawhip: [ 255, 239, 213 ],
        peachpuff: [ 255, 218, 185 ],
        peru: [ 205, 133, 63 ],
        pink: [ 255, 192, 203 ],
        plum: [ 221, 160, 221 ],
        powderblue: [ 176, 224, 230 ],
        purple: [ 128, 0, 128 ],
        rebeccapurple: [ 102, 51, 153 ],
        red: [ 255, 0, 0 ],
        rosybrown: [ 188, 143, 143 ],
        royalblue: [ 65, 105, 225 ],
        saddlebrown: [ 139, 69, 19 ],
        salmon: [ 250, 128, 114 ],
        sandybrown: [ 244, 164, 96 ],
        seagreen: [ 46, 139, 87 ],
        seashell: [ 255, 245, 238 ],
        sienna: [ 160, 82, 45 ],
        silver: [ 192, 192, 192 ],
        skyblue: [ 135, 206, 235 ],
        slateblue: [ 106, 90, 205 ],
        slategray: [ 112, 128, 144 ],
        slategrey: [ 112, 128, 144 ],
        snow: [ 255, 250, 250 ],
        springgreen: [ 0, 255, 127 ],
        steelblue: [ 70, 130, 180 ],
        tan: [ 210, 180, 140 ],
        teal: [ 0, 128, 128 ],
        thistle: [ 216, 191, 216 ],
        tomato: [ 255, 99, 71 ],
        turquoise: [ 64, 224, 208 ],
        violet: [ 238, 130, 238 ],
        wheat: [ 245, 222, 179 ],
        white: [ 255, 255, 255 ],
        whitesmoke: [ 245, 245, 245 ],
        yellow: [ 255, 255, 0 ],
        yellowgreen: [ 154, 205, 50 ]
    };
    for (const key in colors) Object.freeze(colors[key]);
    var names = Object.freeze(colors);
    var baseHues = {
        red: 0,
        orange: 60,
        yellow: 120,
        green: 180,
        blue: 240,
        purple: 300
    };
    function parse$1(cstr) {
        var m, parts = [], alpha = 1, space;
        if (typeof cstr === "number") {
            return {
                space: "rgb",
                values: [ cstr >>> 16, (cstr & 65280) >>> 8, cstr & 255 ],
                alpha: 1
            };
        }
        if (typeof cstr === "number") return {
            space: "rgb",
            values: [ cstr >>> 16, (cstr & 65280) >>> 8, cstr & 255 ],
            alpha: 1
        };
        cstr = String(cstr).toLowerCase();
        if (names[cstr]) {
            parts = names[cstr].slice();
            space = "rgb";
        } else if (cstr === "transparent") {
            alpha = 0;
            space = "rgb";
            parts = [ 0, 0, 0 ];
        } else if (cstr[0] === "#") {
            var base = cstr.slice(1);
            var size = base.length;
            var isShort = size <= 4;
            alpha = 1;
            if (isShort) {
                parts = [ parseInt(base[0] + base[0], 16), parseInt(base[1] + base[1], 16), parseInt(base[2] + base[2], 16) ];
                if (size === 4) {
                    alpha = parseInt(base[3] + base[3], 16) / 255;
                }
            } else {
                parts = [ parseInt(base[0] + base[1], 16), parseInt(base[2] + base[3], 16), parseInt(base[4] + base[5], 16) ];
                if (size === 8) {
                    alpha = parseInt(base[6] + base[7], 16) / 255;
                }
            }
            if (!parts[0]) parts[0] = 0;
            if (!parts[1]) parts[1] = 0;
            if (!parts[2]) parts[2] = 0;
            space = "rgb";
        } else if (m = /^((?:rgba?|hs[lvb]a?|hwba?|cmyk?|xy[zy]|gray|lab|lchu?v?|[ly]uv|lms|oklch|oklab|color))\s*\(([^\)]*)\)/.exec(cstr)) {
            var name = m[1];
            space = name.replace(/a$/, "");
            var dims = space === "cmyk" ? 4 : space === "gray" ? 1 : 3;
            parts = m[2].trim().split(/\s*[,\/]\s*|\s+/);
            if (space === "color") space = parts.shift();
            parts = parts.map(function(x, i) {
                if (x[x.length - 1] === "%") {
                    x = parseFloat(x) / 100;
                    if (i === 3) return x;
                    if (space === "rgb") return x * 255;
                    if (space[0] === "h") return x * 100;
                    if (space[0] === "l" && !i) return x * 100;
                    if (space === "lab") return x * 125;
                    if (space === "lch") return i < 2 ? x * 150 : x * 360;
                    if (space[0] === "o" && !i) return x;
                    if (space === "oklab") return x * .4;
                    if (space === "oklch") return i < 2 ? x * .4 : x * 360;
                    return x;
                }
                if (space[i] === "h" || i === 2 && space[space.length - 1] === "h") {
                    if (baseHues[x] !== undefined) return baseHues[x];
                    if (x.endsWith("deg")) return parseFloat(x);
                    if (x.endsWith("turn")) return parseFloat(x) * 360;
                    if (x.endsWith("grad")) return parseFloat(x) * 360 / 400;
                    if (x.endsWith("rad")) return parseFloat(x) * 180 / Math.PI;
                }
                if (x === "none") return 0;
                return parseFloat(x);
            });
            alpha = parts.length > dims ? parts.pop() : 1;
        } else if (/[0-9](?:\s|\/|,)/.test(cstr)) {
            parts = cstr.match(/([0-9]+)/g).map(function(value) {
                return parseFloat(value);
            });
            space = cstr.match(/([a-z])/gi)?.join("")?.toLowerCase() || "rgb";
        }
        return {
            space: space,
            values: parts,
            alpha: alpha
        };
    }
    var hsl = {
        name: "hsl",
        min: [ 0, 0, 0 ],
        max: [ 360, 100, 100 ],
        channel: [ "hue", "saturation", "lightness" ],
        alias: [ "HSL" ],
        rgb: function(hsl) {
            var h = hsl[0] / 360, s = hsl[1] / 100, l = hsl[2] / 100, t1, t2, t3, rgb, val, i = 0;
            if (s === 0) return val = l * 255, [ val, val, val ];
            t2 = l < .5 ? l * (1 + s) : l + s - l * s;
            t1 = 2 * l - t2;
            rgb = [ 0, 0, 0 ];
            for (;i < 3; ) {
                t3 = h + 1 / 3 * -(i - 1);
                t3 < 0 ? t3++ : t3 > 1 && t3--;
                val = 6 * t3 < 1 ? t1 + (t2 - t1) * 6 * t3 : 2 * t3 < 1 ? t2 : 3 * t3 < 2 ? t1 + (t2 - t1) * (2 / 3 - t3) * 6 : t1;
                rgb[i++] = val * 255;
            }
            return rgb;
        }
    };
    rgb.hsl = function(rgb) {
        var r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255, min = Math.min(r, g, b), max = Math.max(r, g, b), delta = max - min, h, s, l;
        if (max === min) {
            h = 0;
        } else if (r === max) {
            h = (g - b) / delta;
        } else if (g === max) {
            h = 2 + (b - r) / delta;
        } else if (b === max) {
            h = 4 + (r - g) / delta;
        }
        h = Math.min(h * 60, 360);
        if (h < 0) {
            h += 360;
        }
        l = (min + max) / 2;
        if (max === min) {
            s = 0;
        } else if (l <= .5) {
            s = delta / (max + min);
        } else {
            s = delta / (2 - max - min);
        }
        return [ h, s * 100, l * 100 ];
    };
    function rgba(color) {
        if (Array.isArray(color) && color.raw) color = String.raw(...arguments);
        if (color instanceof Number) color = +color;
        var values;
        var parsed = parse$1(color);
        if (!parsed.space) return [];
        const min = parsed.space[0] === "h" ? hsl.min : rgb.min;
        const max = parsed.space[0] === "h" ? hsl.max : rgb.max;
        values = Array(3);
        values[0] = Math.min(Math.max(parsed.values[0], min[0]), max[0]);
        values[1] = Math.min(Math.max(parsed.values[1], min[1]), max[1]);
        values[2] = Math.min(Math.max(parsed.values[2], min[2]), max[2]);
        if (parsed.space[0] === "h") {
            values = hsl.rgb(values);
        }
        values.push(Math.min(Math.max(parsed.alpha, 0), 1));
        return values;
    }
    function asString(color) {
        if (typeof color === "string") {
            return color;
        }
        return toString(color);
    }
    const MAX_CACHE_SIZE = 1024;
    const cache = {};
    let cacheSize = 0;
    function withAlpha(color) {
        if (color.length === 4) {
            return color;
        }
        const output = color.slice();
        output[3] = 1;
        return output;
    }
    function rgbaToLcha(color) {
        const output = xyz.lchuv(rgb.xyz(color));
        output[3] = color[3];
        return output;
    }
    function lchaToRgba(color) {
        const output = xyz.rgb(lchuv.xyz(color));
        output[3] = color[3];
        return output;
    }
    function fromString(s) {
        if (cache.hasOwnProperty(s)) {
            return cache[s];
        }
        if (cacheSize >= MAX_CACHE_SIZE) {
            let i = 0;
            for (const key in cache) {
                if ((i++ & 3) === 0) {
                    delete cache[key];
                    --cacheSize;
                }
            }
        }
        const color = rgba(s);
        if (color.length !== 4) {
            throw new Error('Failed to parse "' + s + '" as color');
        }
        for (const c of color) {
            if (isNaN(c)) {
                throw new Error('Failed to parse "' + s + '" as color');
            }
        }
        normalize(color);
        cache[s] = color;
        ++cacheSize;
        return color;
    }
    function asArray(color) {
        if (Array.isArray(color)) {
            return color;
        }
        return fromString(color);
    }
    function normalize(color) {
        color[0] = clamp(color[0] + .5 | 0, 0, 255);
        color[1] = clamp(color[1] + .5 | 0, 0, 255);
        color[2] = clamp(color[2] + .5 | 0, 0, 255);
        color[3] = clamp(color[3], 0, 1);
        return color;
    }
    function toString(color) {
        let r = color[0];
        if (r != (r | 0)) {
            r = r + .5 | 0;
        }
        let g = color[1];
        if (g != (g | 0)) {
            g = g + .5 | 0;
        }
        let b = color[2];
        if (b != (b | 0)) {
            b = b + .5 | 0;
        }
        const a = color[3] === undefined ? 1 : Math.round(color[3] * 1e3) / 1e3;
        return "rgba(" + r + "," + g + "," + b + "," + a + ")";
    }
    function isStringColor(s) {
        try {
            fromString(s);
            return true;
        } catch (_) {
            return false;
        }
    }
    const ua = typeof navigator !== "undefined" && typeof navigator.userAgent !== "undefined" ? navigator.userAgent.toLowerCase() : "";
    const FIREFOX = ua.includes("firefox");
    const SAFARI = ua.includes("safari") && !ua.includes("chrom");
    SAFARI && (ua.includes("version/15.4") || /cpu (os|iphone os) 15_4 like mac os x/.test(ua));
    const WEBKIT = ua.includes("webkit") && !ua.includes("edge");
    const MAC = ua.includes("macintosh");
    const DEVICE_PIXEL_RATIO = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
    const WORKER_OFFSCREEN_CANVAS = typeof WorkerGlobalScope !== "undefined" && typeof OffscreenCanvas !== "undefined" && self instanceof WorkerGlobalScope;
    const IMAGE_DECODE = typeof Image !== "undefined" && Image.prototype.decode;
    const PASSIVE_EVENT_LISTENERS = function() {
        let passive = false;
        try {
            const options = Object.defineProperty({}, "passive", {
                get: function() {
                    passive = true;
                }
            });
            window.addEventListener("_", null, options);
            window.removeEventListener("_", null, options);
        } catch (error) {}
        return passive;
    }();
    function createCanvasContext2D(width, height, canvasPool, settings) {
        let canvas;
        if (canvasPool && canvasPool.length) {
            canvas = canvasPool.shift();
        } else if (WORKER_OFFSCREEN_CANVAS) {
            canvas = new OffscreenCanvas(width || 300, height || 300);
        } else {
            canvas = document.createElement("canvas");
        }
        if (width) {
            canvas.width = width;
        }
        if (height) {
            canvas.height = height;
        }
        return canvas.getContext("2d", settings);
    }
    let sharedCanvasContext;
    function getSharedCanvasContext2D() {
        if (!sharedCanvasContext) {
            sharedCanvasContext = createCanvasContext2D(1, 1);
        }
        return sharedCanvasContext;
    }
    function releaseCanvas(context) {
        const canvas = context.canvas;
        canvas.width = 1;
        canvas.height = 1;
        context.clearRect(0, 0, 1, 1);
    }
    function replaceNode(newNode, oldNode) {
        const parent = oldNode.parentNode;
        if (parent) {
            parent.replaceChild(newNode, oldNode);
        }
    }
    function removeNode(node) {
        return node && node.parentNode ? node.parentNode.removeChild(node) : null;
    }
    function removeChildren(node) {
        while (node.lastChild) {
            node.removeChild(node.lastChild);
        }
    }
    function replaceChildren(node, children) {
        const oldChildren = node.childNodes;
        for (let i = 0; true; ++i) {
            const oldChild = oldChildren[i];
            const newChild = children[i];
            if (!oldChild && !newChild) {
                break;
            }
            if (oldChild === newChild) {
                continue;
            }
            if (!oldChild) {
                node.appendChild(newChild);
                continue;
            }
            if (!newChild) {
                node.removeChild(oldChild);
                --i;
                continue;
            }
            node.insertBefore(newChild, oldChild);
        }
    }
    function listenImage(image, loadHandler, errorHandler) {
        const img = image;
        let listening = true;
        let decoding = false;
        let loaded = false;
        const listenerKeys = [ listenOnce(img, EventType.LOAD, function() {
            loaded = true;
            if (!decoding) {
                loadHandler();
            }
        }) ];
        if (img.src && IMAGE_DECODE) {
            decoding = true;
            img.decode().then(function() {
                if (listening) {
                    loadHandler();
                }
            }).catch(function(error) {
                if (listening) {
                    if (loaded) {
                        loadHandler();
                    } else {
                        errorHandler();
                    }
                }
            });
        } else {
            listenerKeys.push(listenOnce(img, EventType.ERROR, errorHandler));
        }
        return function unlisten() {
            listening = false;
            listenerKeys.forEach(unlistenByKey);
        };
    }
    function load(image, src) {
        return new Promise((resolve, reject) => {
            function handleLoad() {
                unlisten();
                resolve(image);
            }
            function handleError() {
                unlisten();
                reject(new Error("Image load error"));
            }
            function unlisten() {
                image.removeEventListener("load", handleLoad);
                image.removeEventListener("error", handleError);
            }
            image.addEventListener("load", handleLoad);
            image.addEventListener("error", handleError);
        });
    }
    function decodeFallback(image, src) {
        if (src) {
            image.src = src;
        }
        return image.src && IMAGE_DECODE ? new Promise((resolve, reject) => image.decode().then(() => resolve(image)).catch(e => image.complete && image.width ? resolve(image) : reject(e))) : load(image);
    }
    class IconImageCache {
        constructor() {
            this.cache_ = {};
            this.patternCache_ = {};
            this.cacheSize_ = 0;
            this.maxCacheSize_ = 32;
        }
        clear() {
            this.cache_ = {};
            this.patternCache_ = {};
            this.cacheSize_ = 0;
        }
        canExpireCache() {
            return this.cacheSize_ > this.maxCacheSize_;
        }
        expire() {
            if (this.canExpireCache()) {
                let i = 0;
                for (const key in this.cache_) {
                    const iconImage = this.cache_[key];
                    if ((i++ & 3) === 0 && !iconImage.hasListener()) {
                        delete this.cache_[key];
                        delete this.patternCache_[key];
                        --this.cacheSize_;
                    }
                }
            }
        }
        get(src, crossOrigin, color) {
            const key = getCacheKey(src, crossOrigin, color);
            return key in this.cache_ ? this.cache_[key] : null;
        }
        getPattern(src, crossOrigin, color) {
            const key = getCacheKey(src, crossOrigin, color);
            return key in this.patternCache_ ? this.patternCache_[key] : null;
        }
        set(src, crossOrigin, color, iconImage, pattern) {
            const key = getCacheKey(src, crossOrigin, color);
            const update = key in this.cache_;
            this.cache_[key] = iconImage;
            if (pattern) {
                if (iconImage.getImageState() === ImageState.IDLE) {
                    iconImage.load();
                }
                if (iconImage.getImageState() === ImageState.LOADING) {
                    iconImage.ready().then(() => {
                        this.patternCache_[key] = getSharedCanvasContext2D().createPattern(iconImage.getImage(1), "repeat");
                    });
                } else {
                    this.patternCache_[key] = getSharedCanvasContext2D().createPattern(iconImage.getImage(1), "repeat");
                }
            }
            if (!update) {
                ++this.cacheSize_;
            }
        }
        setSize(maxCacheSize) {
            this.maxCacheSize_ = maxCacheSize;
            this.expire();
        }
    }
    function getCacheKey(src, crossOrigin, color) {
        const colorString = color ? asArray(color) : "null";
        return crossOrigin + ":" + src + ":" + colorString;
    }
    const shared = new IconImageCache;
    let taintedTestContext = null;
    class IconImage extends Target {
        constructor(image, src, crossOrigin, imageState, color) {
            super();
            this.hitDetectionImage_ = null;
            this.image_ = image;
            this.crossOrigin_ = crossOrigin;
            this.canvas_ = {};
            this.color_ = color;
            this.imageState_ = imageState === undefined ? ImageState.IDLE : imageState;
            this.size_ = image && image.width && image.height ? [ image.width, image.height ] : null;
            this.src_ = src;
            this.tainted_;
            this.ready_ = null;
        }
        initializeImage_() {
            this.image_ = new Image;
            if (this.crossOrigin_ !== null) {
                this.image_.crossOrigin = this.crossOrigin_;
            }
        }
        isTainted_() {
            if (this.tainted_ === undefined && this.imageState_ === ImageState.LOADED) {
                if (!taintedTestContext) {
                    taintedTestContext = createCanvasContext2D(1, 1, undefined, {
                        willReadFrequently: true
                    });
                }
                taintedTestContext.drawImage(this.image_, 0, 0);
                try {
                    taintedTestContext.getImageData(0, 0, 1, 1);
                    this.tainted_ = false;
                } catch (e) {
                    taintedTestContext = null;
                    this.tainted_ = true;
                }
            }
            return this.tainted_ === true;
        }
        dispatchChangeEvent_() {
            this.dispatchEvent(EventType.CHANGE);
        }
        handleImageError_() {
            this.imageState_ = ImageState.ERROR;
            this.dispatchChangeEvent_();
        }
        handleImageLoad_() {
            this.imageState_ = ImageState.LOADED;
            this.size_ = [ this.image_.width, this.image_.height ];
            this.dispatchChangeEvent_();
        }
        getImage(pixelRatio) {
            if (!this.image_) {
                this.initializeImage_();
            }
            this.replaceColor_(pixelRatio);
            return this.canvas_[pixelRatio] ? this.canvas_[pixelRatio] : this.image_;
        }
        getPixelRatio(pixelRatio) {
            this.replaceColor_(pixelRatio);
            return this.canvas_[pixelRatio] ? pixelRatio : 1;
        }
        getImageState() {
            return this.imageState_;
        }
        getHitDetectionImage() {
            if (!this.image_) {
                this.initializeImage_();
            }
            if (!this.hitDetectionImage_) {
                if (this.isTainted_()) {
                    const width = this.size_[0];
                    const height = this.size_[1];
                    const context = createCanvasContext2D(width, height);
                    context.fillRect(0, 0, width, height);
                    this.hitDetectionImage_ = context.canvas;
                } else {
                    this.hitDetectionImage_ = this.image_;
                }
            }
            return this.hitDetectionImage_;
        }
        getSize() {
            return this.size_;
        }
        getSrc() {
            return this.src_;
        }
        load() {
            if (this.imageState_ !== ImageState.IDLE) {
                return;
            }
            if (!this.image_) {
                this.initializeImage_();
            }
            this.imageState_ = ImageState.LOADING;
            try {
                if (this.src_ !== undefined) {
                    this.image_.src = this.src_;
                }
            } catch (e) {
                this.handleImageError_();
            }
            if (this.image_ instanceof HTMLImageElement) {
                decodeFallback(this.image_, this.src_).then(image => {
                    this.image_ = image;
                    this.handleImageLoad_();
                }).catch(this.handleImageError_.bind(this));
            }
        }
        replaceColor_(pixelRatio) {
            if (!this.color_ || this.canvas_[pixelRatio] || this.imageState_ !== ImageState.LOADED) {
                return;
            }
            const image = this.image_;
            const canvas = document.createElement("canvas");
            canvas.width = Math.ceil(image.width * pixelRatio);
            canvas.height = Math.ceil(image.height * pixelRatio);
            const ctx = canvas.getContext("2d");
            ctx.scale(pixelRatio, pixelRatio);
            ctx.drawImage(image, 0, 0);
            ctx.globalCompositeOperation = "multiply";
            ctx.fillStyle = asString(this.color_);
            ctx.fillRect(0, 0, canvas.width / pixelRatio, canvas.height / pixelRatio);
            ctx.globalCompositeOperation = "destination-in";
            ctx.drawImage(image, 0, 0);
            this.canvas_[pixelRatio] = canvas;
        }
        ready() {
            if (!this.ready_) {
                this.ready_ = new Promise(resolve => {
                    if (this.imageState_ === ImageState.LOADED || this.imageState_ === ImageState.ERROR) {
                        resolve();
                    } else {
                        this.addEventListener(EventType.CHANGE, function onChange() {
                            if (this.imageState_ === ImageState.LOADED || this.imageState_ === ImageState.ERROR) {
                                this.removeEventListener(EventType.CHANGE, onChange);
                                resolve();
                            }
                        });
                    }
                });
            }
            return this.ready_;
        }
    }
    function get(image, cacheKey, crossOrigin, imageState, color, pattern) {
        let iconImage = cacheKey === undefined ? undefined : shared.get(cacheKey, crossOrigin, color);
        if (!iconImage) {
            iconImage = new IconImage(image, image && "src" in image ? image.src || undefined : cacheKey, crossOrigin, imageState, color);
            shared.set(cacheKey, crossOrigin, color, iconImage, pattern);
        }
        if (pattern && iconImage && !shared.getPattern(cacheKey, crossOrigin, color)) {
            shared.set(cacheKey, crossOrigin, color, iconImage, pattern);
        }
        return iconImage;
    }
    function asColorLike(color) {
        if (!color) {
            return null;
        }
        if (Array.isArray(color)) {
            return toString(color);
        }
        if (typeof color === "object" && "src" in color) {
            return asCanvasPattern(color);
        }
        return color;
    }
    function asCanvasPattern(pattern) {
        if (!pattern.offset || !pattern.size) {
            return shared.getPattern(pattern.src, "anonymous", pattern.color);
        }
        const cacheKey = pattern.src + ":" + pattern.offset;
        const canvasPattern = shared.getPattern(cacheKey, undefined, pattern.color);
        if (canvasPattern) {
            return canvasPattern;
        }
        const iconImage = shared.get(pattern.src, "anonymous", null);
        if (iconImage.getImageState() !== ImageState.LOADED) {
            return null;
        }
        const patternCanvasContext = createCanvasContext2D(pattern.size[0], pattern.size[1]);
        patternCanvasContext.drawImage(iconImage.getImage(1), pattern.offset[0], pattern.offset[1], pattern.size[0], pattern.size[1], 0, 0, pattern.size[0], pattern.size[1]);
        get(patternCanvasContext.canvas, cacheKey, undefined, ImageState.LOADED, pattern.color, true);
        return shared.getPattern(cacheKey, undefined, pattern.color);
    }
    const CLASS_HIDDEN = "ol-hidden";
    const CLASS_UNSELECTABLE = "ol-unselectable";
    const CLASS_CONTROL = "ol-control";
    const CLASS_COLLAPSED = "ol-collapsed";
    const fontRegEx = new RegExp([ "^\\s*(?=(?:(?:[-a-z]+\\s*){0,2}(italic|oblique))?)", "(?=(?:(?:[-a-z]+\\s*){0,2}(small-caps))?)", "(?=(?:(?:[-a-z]+\\s*){0,2}(bold(?:er)?|lighter|[1-9]00 ))?)", "(?:(?:normal|\\1|\\2|\\3)\\s*){0,3}((?:xx?-)?", "(?:small|large)|medium|smaller|larger|[\\.\\d]+(?:\\%|in|[cem]m|ex|p[ctx]))", "(?:\\s*\\/\\s*(normal|[\\.\\d]+(?:\\%|in|[cem]m|ex|p[ctx])?))", "?\\s*([-,\\\"\\'\\sa-z]+?)\\s*$" ].join(""), "i");
    const fontRegExMatchIndex = [ "style", "variant", "weight", "size", "lineHeight", "family" ];
    const getFontParameters = function(fontSpec) {
        const match = fontSpec.match(fontRegEx);
        if (!match) {
            return null;
        }
        const style = {
            lineHeight: "normal",
            size: "1.2em",
            style: "normal",
            weight: "normal",
            variant: "normal"
        };
        for (let i = 0, ii = fontRegExMatchIndex.length; i < ii; ++i) {
            const value = match[i + 1];
            if (value !== undefined) {
                style[fontRegExMatchIndex[i]] = value;
            }
        }
        style.families = style.family.split(/,\s?/);
        return style;
    };
    const defaultFont = "10px sans-serif";
    const defaultFillStyle = "#000";
    const defaultLineCap = "round";
    const defaultLineDash = [];
    const defaultLineDashOffset = 0;
    const defaultLineJoin = "round";
    const defaultMiterLimit = 10;
    const defaultStrokeStyle = "#000";
    const defaultTextAlign = "center";
    const defaultTextBaseline = "middle";
    const defaultPadding = [ 0, 0, 0, 0 ];
    const defaultLineWidth = 1;
    const checkedFonts = new BaseObject;
    let measureContext = null;
    let measureFont;
    const textHeights = {};
    const registerFont = function() {
        const retries = 100;
        const size = "32px ";
        const referenceFonts = [ "monospace", "serif" ];
        const len = referenceFonts.length;
        const text = "wmytzilWMYTZIL@#/&?$%10";
        let interval, referenceWidth;
        function isAvailable(fontStyle, fontWeight, fontFamily) {
            let available = true;
            for (let i = 0; i < len; ++i) {
                const referenceFont = referenceFonts[i];
                referenceWidth = measureTextWidth(fontStyle + " " + fontWeight + " " + size + referenceFont, text);
                if (fontFamily != referenceFont) {
                    const width = measureTextWidth(fontStyle + " " + fontWeight + " " + size + fontFamily + "," + referenceFont, text);
                    available = available && width != referenceWidth;
                }
            }
            if (available) {
                return true;
            }
            return false;
        }
        function check() {
            let done = true;
            const fonts = checkedFonts.getKeys();
            for (let i = 0, ii = fonts.length; i < ii; ++i) {
                const font = fonts[i];
                if (checkedFonts.get(font) < retries) {
                    if (isAvailable.apply(this, font.split("\n"))) {
                        clear(textHeights);
                        measureContext = null;
                        measureFont = undefined;
                        checkedFonts.set(font, retries);
                    } else {
                        checkedFonts.set(font, checkedFonts.get(font) + 1, true);
                        done = false;
                    }
                }
            }
            if (done) {
                clearInterval(interval);
                interval = undefined;
            }
        }
        return function(fontSpec) {
            const font = getFontParameters(fontSpec);
            if (!font) {
                return;
            }
            const families = font.families;
            for (let i = 0, ii = families.length; i < ii; ++i) {
                const family = families[i];
                const key = font.style + "\n" + font.weight + "\n" + family;
                if (checkedFonts.get(key) === undefined) {
                    checkedFonts.set(key, retries, true);
                    if (!isAvailable(font.style, font.weight, family)) {
                        checkedFonts.set(key, 0, true);
                        if (interval === undefined) {
                            interval = setInterval(check, 32);
                        }
                    }
                }
            }
        };
    }();
    const measureTextHeight = function() {
        let measureElement;
        return function(fontSpec) {
            let height = textHeights[fontSpec];
            if (height == undefined) {
                if (WORKER_OFFSCREEN_CANVAS) {
                    const font = getFontParameters(fontSpec);
                    const metrics = measureText(fontSpec, "Žg");
                    const lineHeight = isNaN(Number(font.lineHeight)) ? 1.2 : Number(font.lineHeight);
                    height = lineHeight * (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);
                } else {
                    if (!measureElement) {
                        measureElement = document.createElement("div");
                        measureElement.innerHTML = "M";
                        measureElement.style.minHeight = "0";
                        measureElement.style.maxHeight = "none";
                        measureElement.style.height = "auto";
                        measureElement.style.padding = "0";
                        measureElement.style.border = "none";
                        measureElement.style.position = "absolute";
                        measureElement.style.display = "block";
                        measureElement.style.left = "-99999px";
                    }
                    measureElement.style.font = fontSpec;
                    document.body.appendChild(measureElement);
                    height = measureElement.offsetHeight;
                    document.body.removeChild(measureElement);
                }
                textHeights[fontSpec] = height;
            }
            return height;
        };
    }();
    function measureText(font, text) {
        if (!measureContext) {
            measureContext = createCanvasContext2D(1, 1);
        }
        if (font != measureFont) {
            measureContext.font = font;
            measureFont = measureContext.font;
        }
        return measureContext.measureText(text);
    }
    function measureTextWidth(font, text) {
        return measureText(font, text).width;
    }
    function measureAndCacheTextWidth(font, text, cache) {
        if (text in cache) {
            return cache[text];
        }
        const width = text.split("\n").reduce((prev, curr) => Math.max(prev, measureTextWidth(font, curr)), 0);
        cache[text] = width;
        return width;
    }
    function getTextDimensions(baseStyle, chunks) {
        const widths = [];
        const heights = [];
        const lineWidths = [];
        let width = 0;
        let lineWidth = 0;
        let height = 0;
        let lineHeight = 0;
        for (let i = 0, ii = chunks.length; i <= ii; i += 2) {
            const text = chunks[i];
            if (text === "\n" || i === ii) {
                width = Math.max(width, lineWidth);
                lineWidths.push(lineWidth);
                lineWidth = 0;
                height += lineHeight;
                lineHeight = 0;
                continue;
            }
            const font = chunks[i + 1] || baseStyle.font;
            const currentWidth = measureTextWidth(font, text);
            widths.push(currentWidth);
            lineWidth += currentWidth;
            const currentHeight = measureTextHeight(font);
            heights.push(currentHeight);
            lineHeight = Math.max(lineHeight, currentHeight);
        }
        return {
            width: width,
            height: height,
            widths: widths,
            heights: heights,
            lineWidths: lineWidths
        };
    }
    function drawImageOrLabel(context, transform, opacity, labelOrImage, originX, originY, w, h, x, y, scale) {
        context.save();
        if (opacity !== 1) {
            if (context.globalAlpha === undefined) {
                context.globalAlpha = context => context.globalAlpha *= opacity;
            } else {
                context.globalAlpha *= opacity;
            }
        }
        if (transform) {
            context.transform.apply(context, transform);
        }
        if (labelOrImage.contextInstructions) {
            context.translate(x, y);
            context.scale(scale[0], scale[1]);
            executeLabelInstructions(labelOrImage, context);
        } else if (scale[0] < 0 || scale[1] < 0) {
            context.translate(x, y);
            context.scale(scale[0], scale[1]);
            context.drawImage(labelOrImage, originX, originY, w, h, 0, 0, w, h);
        } else {
            context.drawImage(labelOrImage, originX, originY, w, h, x, y, w * scale[0], h * scale[1]);
        }
        context.restore();
    }
    function executeLabelInstructions(label, context) {
        const contextInstructions = label.contextInstructions;
        for (let i = 0, ii = contextInstructions.length; i < ii; i += 2) {
            if (Array.isArray(contextInstructions[i + 1])) {
                context[contextInstructions[i]].apply(context, contextInstructions[i + 1]);
            } else {
                context[contextInstructions[i]] = contextInstructions[i + 1];
            }
        }
    }
    class RegularShape extends ImageStyle {
        constructor(options) {
            super({
                opacity: 1,
                rotateWithView: options.rotateWithView !== undefined ? options.rotateWithView : false,
                rotation: options.rotation !== undefined ? options.rotation : 0,
                scale: options.scale !== undefined ? options.scale : 1,
                displacement: options.displacement !== undefined ? options.displacement : [ 0, 0 ],
                declutterMode: options.declutterMode
            });
            this.canvases_;
            this.hitDetectionCanvas_ = null;
            this.fill_ = options.fill !== undefined ? options.fill : null;
            this.origin_ = [ 0, 0 ];
            this.points_ = options.points;
            this.radius_ = options.radius;
            this.radius2_ = options.radius2;
            this.angle_ = options.angle !== undefined ? options.angle : 0;
            this.stroke_ = options.stroke !== undefined ? options.stroke : null;
            this.size_;
            this.renderOptions_;
            this.imageState_ = this.fill_ && this.fill_.loading() ? ImageState.LOADING : ImageState.LOADED;
            if (this.imageState_ === ImageState.LOADING) {
                this.ready().then(() => this.imageState_ = ImageState.LOADED);
            }
            this.render();
        }
        clone() {
            const scale = this.getScale();
            const style = new RegularShape({
                fill: this.getFill() ? this.getFill().clone() : undefined,
                points: this.getPoints(),
                radius: this.getRadius(),
                radius2: this.getRadius2(),
                angle: this.getAngle(),
                stroke: this.getStroke() ? this.getStroke().clone() : undefined,
                rotation: this.getRotation(),
                rotateWithView: this.getRotateWithView(),
                scale: Array.isArray(scale) ? scale.slice() : scale,
                displacement: this.getDisplacement().slice(),
                declutterMode: this.getDeclutterMode()
            });
            style.setOpacity(this.getOpacity());
            return style;
        }
        getAnchor() {
            const size = this.size_;
            const displacement = this.getDisplacement();
            const scale = this.getScaleArray();
            return [ size[0] / 2 - displacement[0] / scale[0], size[1] / 2 + displacement[1] / scale[1] ];
        }
        getAngle() {
            return this.angle_;
        }
        getFill() {
            return this.fill_;
        }
        setFill(fill) {
            this.fill_ = fill;
            this.render();
        }
        getHitDetectionImage() {
            if (!this.hitDetectionCanvas_) {
                this.hitDetectionCanvas_ = this.createHitDetectionCanvas_(this.renderOptions_);
            }
            return this.hitDetectionCanvas_;
        }
        getImage(pixelRatio) {
            let image = this.canvases_[pixelRatio];
            if (!image) {
                const renderOptions = this.renderOptions_;
                const context = createCanvasContext2D(renderOptions.size * pixelRatio, renderOptions.size * pixelRatio);
                this.draw_(renderOptions, context, pixelRatio);
                image = context.canvas;
                this.canvases_[pixelRatio] = image;
            }
            return image;
        }
        getPixelRatio(pixelRatio) {
            return pixelRatio;
        }
        getImageSize() {
            return this.size_;
        }
        getImageState() {
            return this.imageState_;
        }
        getOrigin() {
            return this.origin_;
        }
        getPoints() {
            return this.points_;
        }
        getRadius() {
            return this.radius_;
        }
        getRadius2() {
            return this.radius2_;
        }
        getSize() {
            return this.size_;
        }
        getStroke() {
            return this.stroke_;
        }
        setStroke(stroke) {
            this.stroke_ = stroke;
            this.render();
        }
        listenImageChange(listener) {}
        load() {}
        unlistenImageChange(listener) {}
        calculateLineJoinSize_(lineJoin, strokeWidth, miterLimit) {
            if (strokeWidth === 0 || this.points_ === Infinity || lineJoin !== "bevel" && lineJoin !== "miter") {
                return strokeWidth;
            }
            let r1 = this.radius_;
            let r2 = this.radius2_ === undefined ? r1 : this.radius2_;
            if (r1 < r2) {
                const tmp = r1;
                r1 = r2;
                r2 = tmp;
            }
            const points = this.radius2_ === undefined ? this.points_ : this.points_ * 2;
            const alpha = 2 * Math.PI / points;
            const a = r2 * Math.sin(alpha);
            const b = Math.sqrt(r2 * r2 - a * a);
            const d = r1 - b;
            const e = Math.sqrt(a * a + d * d);
            const miterRatio = e / a;
            if (lineJoin === "miter" && miterRatio <= miterLimit) {
                return miterRatio * strokeWidth;
            }
            const k = strokeWidth / 2 / miterRatio;
            const l = strokeWidth / 2 * (d / e);
            const maxr = Math.sqrt((r1 + k) * (r1 + k) + l * l);
            const bevelAdd = maxr - r1;
            if (this.radius2_ === undefined || lineJoin === "bevel") {
                return bevelAdd * 2;
            }
            const aa = r1 * Math.sin(alpha);
            const bb = Math.sqrt(r1 * r1 - aa * aa);
            const dd = r2 - bb;
            const ee = Math.sqrt(aa * aa + dd * dd);
            const innerMiterRatio = ee / aa;
            if (innerMiterRatio <= miterLimit) {
                const innerLength = innerMiterRatio * strokeWidth / 2 - r2 - r1;
                return 2 * Math.max(bevelAdd, innerLength);
            }
            return bevelAdd * 2;
        }
        createRenderOptions() {
            let lineCap = defaultLineCap;
            let lineJoin = defaultLineJoin;
            let miterLimit = 0;
            let lineDash = null;
            let lineDashOffset = 0;
            let strokeStyle;
            let strokeWidth = 0;
            if (this.stroke_) {
                strokeStyle = asColorLike(this.stroke_.getColor() ?? defaultStrokeStyle);
                strokeWidth = this.stroke_.getWidth() ?? defaultLineWidth;
                lineDash = this.stroke_.getLineDash();
                lineDashOffset = this.stroke_.getLineDashOffset() ?? 0;
                lineJoin = this.stroke_.getLineJoin() ?? defaultLineJoin;
                lineCap = this.stroke_.getLineCap() ?? defaultLineCap;
                miterLimit = this.stroke_.getMiterLimit() ?? defaultMiterLimit;
            }
            const add = this.calculateLineJoinSize_(lineJoin, strokeWidth, miterLimit);
            const maxRadius = Math.max(this.radius_, this.radius2_ || 0);
            const size = Math.ceil(2 * maxRadius + add);
            return {
                strokeStyle: strokeStyle,
                strokeWidth: strokeWidth,
                size: size,
                lineCap: lineCap,
                lineDash: lineDash,
                lineDashOffset: lineDashOffset,
                lineJoin: lineJoin,
                miterLimit: miterLimit
            };
        }
        render() {
            this.renderOptions_ = this.createRenderOptions();
            const size = this.renderOptions_.size;
            this.canvases_ = {};
            this.hitDetectionCanvas_ = null;
            this.size_ = [ size, size ];
        }
        draw_(renderOptions, context, pixelRatio) {
            context.scale(pixelRatio, pixelRatio);
            context.translate(renderOptions.size / 2, renderOptions.size / 2);
            this.createPath_(context);
            if (this.fill_) {
                let color = this.fill_.getColor();
                if (color === null) {
                    color = defaultFillStyle;
                }
                context.fillStyle = asColorLike(color);
                context.fill();
            }
            if (renderOptions.strokeStyle) {
                context.strokeStyle = renderOptions.strokeStyle;
                context.lineWidth = renderOptions.strokeWidth;
                if (renderOptions.lineDash) {
                    context.setLineDash(renderOptions.lineDash);
                    context.lineDashOffset = renderOptions.lineDashOffset;
                }
                context.lineCap = renderOptions.lineCap;
                context.lineJoin = renderOptions.lineJoin;
                context.miterLimit = renderOptions.miterLimit;
                context.stroke();
            }
        }
        createHitDetectionCanvas_(renderOptions) {
            let context;
            if (this.fill_) {
                let color = this.fill_.getColor();
                let opacity = 0;
                if (typeof color === "string") {
                    color = asArray(color);
                }
                if (color === null) {
                    opacity = 1;
                } else if (Array.isArray(color)) {
                    opacity = color.length === 4 ? color[3] : 1;
                }
                if (opacity === 0) {
                    context = createCanvasContext2D(renderOptions.size, renderOptions.size);
                    this.drawHitDetectionCanvas_(renderOptions, context);
                }
            }
            return context ? context.canvas : this.getImage(1);
        }
        createPath_(context) {
            let points = this.points_;
            const radius = this.radius_;
            if (points === Infinity) {
                context.arc(0, 0, radius, 0, 2 * Math.PI);
            } else {
                const radius2 = this.radius2_ === undefined ? radius : this.radius2_;
                if (this.radius2_ !== undefined) {
                    points *= 2;
                }
                const startAngle = this.angle_ - Math.PI / 2;
                const step = 2 * Math.PI / points;
                for (let i = 0; i < points; i++) {
                    const angle0 = startAngle + i * step;
                    const radiusC = i % 2 === 0 ? radius : radius2;
                    context.lineTo(radiusC * Math.cos(angle0), radiusC * Math.sin(angle0));
                }
                context.closePath();
            }
        }
        drawHitDetectionCanvas_(renderOptions, context) {
            context.translate(renderOptions.size / 2, renderOptions.size / 2);
            this.createPath_(context);
            context.fillStyle = defaultFillStyle;
            context.fill();
            if (renderOptions.strokeStyle) {
                context.strokeStyle = renderOptions.strokeStyle;
                context.lineWidth = renderOptions.strokeWidth;
                if (renderOptions.lineDash) {
                    context.setLineDash(renderOptions.lineDash);
                    context.lineDashOffset = renderOptions.lineDashOffset;
                }
                context.lineJoin = renderOptions.lineJoin;
                context.miterLimit = renderOptions.miterLimit;
                context.stroke();
            }
        }
        ready() {
            return this.fill_ ? this.fill_.ready() : Promise.resolve();
        }
    }
    class CircleStyle extends RegularShape {
        constructor(options) {
            options = options ? options : {
                radius: 5
            };
            super({
                points: Infinity,
                fill: options.fill,
                radius: options.radius,
                stroke: options.stroke,
                scale: options.scale !== undefined ? options.scale : 1,
                rotation: options.rotation !== undefined ? options.rotation : 0,
                rotateWithView: options.rotateWithView !== undefined ? options.rotateWithView : false,
                displacement: options.displacement !== undefined ? options.displacement : [ 0, 0 ],
                declutterMode: options.declutterMode
            });
        }
        clone() {
            const scale = this.getScale();
            const style = new CircleStyle({
                fill: this.getFill() ? this.getFill().clone() : undefined,
                stroke: this.getStroke() ? this.getStroke().clone() : undefined,
                radius: this.getRadius(),
                scale: Array.isArray(scale) ? scale.slice() : scale,
                rotation: this.getRotation(),
                rotateWithView: this.getRotateWithView(),
                displacement: this.getDisplacement().slice(),
                declutterMode: this.getDeclutterMode()
            });
            style.setOpacity(this.getOpacity());
            return style;
        }
        setRadius(radius) {
            this.radius_ = radius;
            this.render();
        }
    }
    class Fill {
        constructor(options) {
            options = options || {};
            this.patternImage_ = null;
            this.color_ = null;
            if (options.color !== undefined) {
                this.setColor(options.color);
            }
        }
        clone() {
            const color = this.getColor();
            return new Fill({
                color: Array.isArray(color) ? color.slice() : color || undefined
            });
        }
        getColor() {
            return this.color_;
        }
        setColor(color) {
            if (color !== null && typeof color === "object" && "src" in color) {
                const patternImage = get(null, color.src, "anonymous", undefined, color.offset ? null : color.color ? color.color : null, !(color.offset && color.size));
                patternImage.ready().then(() => {
                    this.patternImage_ = null;
                });
                if (patternImage.getImageState() === ImageState.IDLE) {
                    patternImage.load();
                }
                if (patternImage.getImageState() === ImageState.LOADING) {
                    this.patternImage_ = patternImage;
                }
            }
            this.color_ = color;
        }
        loading() {
            return !!this.patternImage_;
        }
        ready() {
            return this.patternImage_ ? this.patternImage_.ready() : Promise.resolve();
        }
    }
    class Stroke {
        constructor(options) {
            options = options || {};
            this.color_ = options.color !== undefined ? options.color : null;
            this.lineCap_ = options.lineCap;
            this.lineDash_ = options.lineDash !== undefined ? options.lineDash : null;
            this.lineDashOffset_ = options.lineDashOffset;
            this.lineJoin_ = options.lineJoin;
            this.miterLimit_ = options.miterLimit;
            this.width_ = options.width;
        }
        clone() {
            const color = this.getColor();
            return new Stroke({
                color: Array.isArray(color) ? color.slice() : color || undefined,
                lineCap: this.getLineCap(),
                lineDash: this.getLineDash() ? this.getLineDash().slice() : undefined,
                lineDashOffset: this.getLineDashOffset(),
                lineJoin: this.getLineJoin(),
                miterLimit: this.getMiterLimit(),
                width: this.getWidth()
            });
        }
        getColor() {
            return this.color_;
        }
        getLineCap() {
            return this.lineCap_;
        }
        getLineDash() {
            return this.lineDash_;
        }
        getLineDashOffset() {
            return this.lineDashOffset_;
        }
        getLineJoin() {
            return this.lineJoin_;
        }
        getMiterLimit() {
            return this.miterLimit_;
        }
        getWidth() {
            return this.width_;
        }
        setColor(color) {
            this.color_ = color;
        }
        setLineCap(lineCap) {
            this.lineCap_ = lineCap;
        }
        setLineDash(lineDash) {
            this.lineDash_ = lineDash;
        }
        setLineDashOffset(lineDashOffset) {
            this.lineDashOffset_ = lineDashOffset;
        }
        setLineJoin(lineJoin) {
            this.lineJoin_ = lineJoin;
        }
        setMiterLimit(miterLimit) {
            this.miterLimit_ = miterLimit;
        }
        setWidth(width) {
            this.width_ = width;
        }
    }
    class Style {
        constructor(options) {
            options = options || {};
            this.geometry_ = null;
            this.geometryFunction_ = defaultGeometryFunction;
            if (options.geometry !== undefined) {
                this.setGeometry(options.geometry);
            }
            this.fill_ = options.fill !== undefined ? options.fill : null;
            this.image_ = options.image !== undefined ? options.image : null;
            this.renderer_ = options.renderer !== undefined ? options.renderer : null;
            this.hitDetectionRenderer_ = options.hitDetectionRenderer !== undefined ? options.hitDetectionRenderer : null;
            this.stroke_ = options.stroke !== undefined ? options.stroke : null;
            this.text_ = options.text !== undefined ? options.text : null;
            this.zIndex_ = options.zIndex;
        }
        clone() {
            let geometry = this.getGeometry();
            if (geometry && typeof geometry === "object") {
                geometry = geometry.clone();
            }
            return new Style({
                geometry: geometry ?? undefined,
                fill: this.getFill() ? this.getFill().clone() : undefined,
                image: this.getImage() ? this.getImage().clone() : undefined,
                renderer: this.getRenderer() ?? undefined,
                stroke: this.getStroke() ? this.getStroke().clone() : undefined,
                text: this.getText() ? this.getText().clone() : undefined,
                zIndex: this.getZIndex()
            });
        }
        getRenderer() {
            return this.renderer_;
        }
        setRenderer(renderer) {
            this.renderer_ = renderer;
        }
        setHitDetectionRenderer(renderer) {
            this.hitDetectionRenderer_ = renderer;
        }
        getHitDetectionRenderer() {
            return this.hitDetectionRenderer_;
        }
        getGeometry() {
            return this.geometry_;
        }
        getGeometryFunction() {
            return this.geometryFunction_;
        }
        getFill() {
            return this.fill_;
        }
        setFill(fill) {
            this.fill_ = fill;
        }
        getImage() {
            return this.image_;
        }
        setImage(image) {
            this.image_ = image;
        }
        getStroke() {
            return this.stroke_;
        }
        setStroke(stroke) {
            this.stroke_ = stroke;
        }
        getText() {
            return this.text_;
        }
        setText(text) {
            this.text_ = text;
        }
        getZIndex() {
            return this.zIndex_;
        }
        setGeometry(geometry) {
            if (typeof geometry === "function") {
                this.geometryFunction_ = geometry;
            } else if (typeof geometry === "string") {
                this.geometryFunction_ = function(feature) {
                    return feature.get(geometry);
                };
            } else if (!geometry) {
                this.geometryFunction_ = defaultGeometryFunction;
            } else if (geometry !== undefined) {
                this.geometryFunction_ = function() {
                    return geometry;
                };
            }
            this.geometry_ = geometry;
        }
        setZIndex(zIndex) {
            this.zIndex_ = zIndex;
        }
    }
    function toFunction(obj) {
        let styleFunction;
        if (typeof obj === "function") {
            styleFunction = obj;
        } else {
            let styles;
            if (Array.isArray(obj)) {
                styles = obj;
            } else {
                assert(typeof obj.getZIndex === "function", "Expected an `Style` or an array of `Style`");
                const style = obj;
                styles = [ style ];
            }
            styleFunction = function() {
                return styles;
            };
        }
        return styleFunction;
    }
    let defaultStyles = null;
    function createDefaultStyle(feature, resolution) {
        if (!defaultStyles) {
            const fill = new Fill({
                color: "rgba(255,255,255,0.4)"
            });
            const stroke = new Stroke({
                color: "#3399CC",
                width: 1.25
            });
            defaultStyles = [ new Style({
                image: new CircleStyle({
                    fill: fill,
                    stroke: stroke,
                    radius: 5
                }),
                fill: fill,
                stroke: stroke
            }) ];
        }
        return defaultStyles;
    }
    function defaultGeometryFunction(feature) {
        return feature.getGeometry();
    }
    function calculateScale(width, height, wantedWidth, wantedHeight) {
        if (wantedWidth !== undefined && wantedHeight !== undefined) {
            return [ wantedWidth / width, wantedHeight / height ];
        }
        if (wantedWidth !== undefined) {
            return wantedWidth / width;
        }
        if (wantedHeight !== undefined) {
            return wantedHeight / height;
        }
        return 1;
    }
    class Icon extends ImageStyle {
        constructor(options) {
            options = options || {};
            const opacity = options.opacity !== undefined ? options.opacity : 1;
            const rotation = options.rotation !== undefined ? options.rotation : 0;
            const scale = options.scale !== undefined ? options.scale : 1;
            const rotateWithView = options.rotateWithView !== undefined ? options.rotateWithView : false;
            super({
                opacity: opacity,
                rotation: rotation,
                scale: scale,
                displacement: options.displacement !== undefined ? options.displacement : [ 0, 0 ],
                rotateWithView: rotateWithView,
                declutterMode: options.declutterMode
            });
            this.anchor_ = options.anchor !== undefined ? options.anchor : [ .5, .5 ];
            this.normalizedAnchor_ = null;
            this.anchorOrigin_ = options.anchorOrigin !== undefined ? options.anchorOrigin : "top-left";
            this.anchorXUnits_ = options.anchorXUnits !== undefined ? options.anchorXUnits : "fraction";
            this.anchorYUnits_ = options.anchorYUnits !== undefined ? options.anchorYUnits : "fraction";
            this.crossOrigin_ = options.crossOrigin !== undefined ? options.crossOrigin : null;
            const image = options.img !== undefined ? options.img : null;
            let cacheKey = options.src;
            assert(!(cacheKey !== undefined && image), "`image` and `src` cannot be provided at the same time");
            if ((cacheKey === undefined || cacheKey.length === 0) && image) {
                cacheKey = image.src || getUid(image);
            }
            assert(cacheKey !== undefined && cacheKey.length > 0, "A defined and non-empty `src` or `image` must be provided");
            assert(!((options.width !== undefined || options.height !== undefined) && options.scale !== undefined), "`width` or `height` cannot be provided together with `scale`");
            let imageState;
            if (options.src !== undefined) {
                imageState = ImageState.IDLE;
            } else if (image !== undefined) {
                if ("complete" in image) {
                    if (image.complete) {
                        imageState = image.src ? ImageState.LOADED : ImageState.IDLE;
                    } else {
                        imageState = ImageState.LOADING;
                    }
                } else {
                    imageState = ImageState.LOADED;
                }
            }
            this.color_ = options.color !== undefined ? asArray(options.color) : null;
            this.iconImage_ = get(image, cacheKey, this.crossOrigin_, imageState, this.color_);
            this.offset_ = options.offset !== undefined ? options.offset : [ 0, 0 ];
            this.offsetOrigin_ = options.offsetOrigin !== undefined ? options.offsetOrigin : "top-left";
            this.origin_ = null;
            this.size_ = options.size !== undefined ? options.size : null;
            if (options.width !== undefined || options.height !== undefined) {
                let width, height;
                if (options.size) {
                    [width, height] = options.size;
                } else {
                    const image = this.getImage(1);
                    if (image.width && image.height) {
                        width = image.width;
                        height = image.height;
                    } else if (image instanceof HTMLImageElement) {
                        this.initialOptions_ = options;
                        const onload = () => {
                            this.unlistenImageChange(onload);
                            if (!this.initialOptions_) {
                                return;
                            }
                            const imageSize = this.iconImage_.getSize();
                            this.setScale(calculateScale(imageSize[0], imageSize[1], options.width, options.height));
                        };
                        this.listenImageChange(onload);
                        return;
                    }
                }
                if (width !== undefined) {
                    this.setScale(calculateScale(width, height, options.width, options.height));
                }
            }
        }
        clone() {
            let scale, width, height;
            if (this.initialOptions_) {
                width = this.initialOptions_.width;
                height = this.initialOptions_.height;
            } else {
                scale = this.getScale();
                scale = Array.isArray(scale) ? scale.slice() : scale;
            }
            return new Icon({
                anchor: this.anchor_.slice(),
                anchorOrigin: this.anchorOrigin_,
                anchorXUnits: this.anchorXUnits_,
                anchorYUnits: this.anchorYUnits_,
                color: this.color_ && this.color_.slice ? this.color_.slice() : this.color_ || undefined,
                crossOrigin: this.crossOrigin_,
                offset: this.offset_.slice(),
                offsetOrigin: this.offsetOrigin_,
                opacity: this.getOpacity(),
                rotateWithView: this.getRotateWithView(),
                rotation: this.getRotation(),
                scale: scale,
                width: width,
                height: height,
                size: this.size_ !== null ? this.size_.slice() : undefined,
                src: this.getSrc(),
                displacement: this.getDisplacement().slice(),
                declutterMode: this.getDeclutterMode()
            });
        }
        getAnchor() {
            let anchor = this.normalizedAnchor_;
            if (!anchor) {
                anchor = this.anchor_;
                const size = this.getSize();
                if (this.anchorXUnits_ == "fraction" || this.anchorYUnits_ == "fraction") {
                    if (!size) {
                        return null;
                    }
                    anchor = this.anchor_.slice();
                    if (this.anchorXUnits_ == "fraction") {
                        anchor[0] *= size[0];
                    }
                    if (this.anchorYUnits_ == "fraction") {
                        anchor[1] *= size[1];
                    }
                }
                if (this.anchorOrigin_ != "top-left") {
                    if (!size) {
                        return null;
                    }
                    if (anchor === this.anchor_) {
                        anchor = this.anchor_.slice();
                    }
                    if (this.anchorOrigin_ == "top-right" || this.anchorOrigin_ == "bottom-right") {
                        anchor[0] = -anchor[0] + size[0];
                    }
                    if (this.anchorOrigin_ == "bottom-left" || this.anchorOrigin_ == "bottom-right") {
                        anchor[1] = -anchor[1] + size[1];
                    }
                }
                this.normalizedAnchor_ = anchor;
            }
            const displacement = this.getDisplacement();
            const scale = this.getScaleArray();
            return [ anchor[0] - displacement[0] / scale[0], anchor[1] + displacement[1] / scale[1] ];
        }
        setAnchor(anchor) {
            this.anchor_ = anchor;
            this.normalizedAnchor_ = null;
        }
        getColor() {
            return this.color_;
        }
        getImage(pixelRatio) {
            return this.iconImage_.getImage(pixelRatio);
        }
        getPixelRatio(pixelRatio) {
            return this.iconImage_.getPixelRatio(pixelRatio);
        }
        getImageSize() {
            return this.iconImage_.getSize();
        }
        getImageState() {
            return this.iconImage_.getImageState();
        }
        getHitDetectionImage() {
            return this.iconImage_.getHitDetectionImage();
        }
        getOrigin() {
            if (this.origin_) {
                return this.origin_;
            }
            let offset = this.offset_;
            if (this.offsetOrigin_ != "top-left") {
                const size = this.getSize();
                const iconImageSize = this.iconImage_.getSize();
                if (!size || !iconImageSize) {
                    return null;
                }
                offset = offset.slice();
                if (this.offsetOrigin_ == "top-right" || this.offsetOrigin_ == "bottom-right") {
                    offset[0] = iconImageSize[0] - size[0] - offset[0];
                }
                if (this.offsetOrigin_ == "bottom-left" || this.offsetOrigin_ == "bottom-right") {
                    offset[1] = iconImageSize[1] - size[1] - offset[1];
                }
            }
            this.origin_ = offset;
            return this.origin_;
        }
        getSrc() {
            return this.iconImage_.getSrc();
        }
        getSize() {
            return !this.size_ ? this.iconImage_.getSize() : this.size_;
        }
        getWidth() {
            const scale = this.getScaleArray();
            if (this.size_) {
                return this.size_[0] * scale[0];
            }
            if (this.iconImage_.getImageState() == ImageState.LOADED) {
                return this.iconImage_.getSize()[0] * scale[0];
            }
            return undefined;
        }
        getHeight() {
            const scale = this.getScaleArray();
            if (this.size_) {
                return this.size_[1] * scale[1];
            }
            if (this.iconImage_.getImageState() == ImageState.LOADED) {
                return this.iconImage_.getSize()[1] * scale[1];
            }
            return undefined;
        }
        setScale(scale) {
            delete this.initialOptions_;
            super.setScale(scale);
        }
        listenImageChange(listener) {
            this.iconImage_.addEventListener(EventType.CHANGE, listener);
        }
        load() {
            this.iconImage_.load();
        }
        unlistenImageChange(listener) {
            this.iconImage_.removeEventListener(EventType.CHANGE, listener);
        }
        ready() {
            return this.iconImage_.ready();
        }
    }
    const DEFAULT_FILL_COLOR = "#333";
    class Text {
        constructor(options) {
            options = options || {};
            this.font_ = options.font;
            this.rotation_ = options.rotation;
            this.rotateWithView_ = options.rotateWithView;
            this.scale_ = options.scale;
            this.scaleArray_ = toSize(options.scale !== undefined ? options.scale : 1);
            this.text_ = options.text;
            this.textAlign_ = options.textAlign;
            this.justify_ = options.justify;
            this.repeat_ = options.repeat;
            this.textBaseline_ = options.textBaseline;
            this.fill_ = options.fill !== undefined ? options.fill : new Fill({
                color: DEFAULT_FILL_COLOR
            });
            this.maxAngle_ = options.maxAngle !== undefined ? options.maxAngle : Math.PI / 4;
            this.placement_ = options.placement !== undefined ? options.placement : "point";
            this.overflow_ = !!options.overflow;
            this.stroke_ = options.stroke !== undefined ? options.stroke : null;
            this.offsetX_ = options.offsetX !== undefined ? options.offsetX : 0;
            this.offsetY_ = options.offsetY !== undefined ? options.offsetY : 0;
            this.backgroundFill_ = options.backgroundFill ? options.backgroundFill : null;
            this.backgroundStroke_ = options.backgroundStroke ? options.backgroundStroke : null;
            this.padding_ = options.padding === undefined ? null : options.padding;
            this.declutterMode_ = options.declutterMode;
        }
        clone() {
            const scale = this.getScale();
            return new Text({
                font: this.getFont(),
                placement: this.getPlacement(),
                repeat: this.getRepeat(),
                maxAngle: this.getMaxAngle(),
                overflow: this.getOverflow(),
                rotation: this.getRotation(),
                rotateWithView: this.getRotateWithView(),
                scale: Array.isArray(scale) ? scale.slice() : scale,
                text: this.getText(),
                textAlign: this.getTextAlign(),
                justify: this.getJustify(),
                textBaseline: this.getTextBaseline(),
                fill: this.getFill() ? this.getFill().clone() : undefined,
                stroke: this.getStroke() ? this.getStroke().clone() : undefined,
                offsetX: this.getOffsetX(),
                offsetY: this.getOffsetY(),
                backgroundFill: this.getBackgroundFill() ? this.getBackgroundFill().clone() : undefined,
                backgroundStroke: this.getBackgroundStroke() ? this.getBackgroundStroke().clone() : undefined,
                padding: this.getPadding() || undefined,
                declutterMode: this.getDeclutterMode()
            });
        }
        getOverflow() {
            return this.overflow_;
        }
        getFont() {
            return this.font_;
        }
        getMaxAngle() {
            return this.maxAngle_;
        }
        getPlacement() {
            return this.placement_;
        }
        getRepeat() {
            return this.repeat_;
        }
        getOffsetX() {
            return this.offsetX_;
        }
        getOffsetY() {
            return this.offsetY_;
        }
        getFill() {
            return this.fill_;
        }
        getRotateWithView() {
            return this.rotateWithView_;
        }
        getRotation() {
            return this.rotation_;
        }
        getScale() {
            return this.scale_;
        }
        getScaleArray() {
            return this.scaleArray_;
        }
        getStroke() {
            return this.stroke_;
        }
        getText() {
            return this.text_;
        }
        getTextAlign() {
            return this.textAlign_;
        }
        getJustify() {
            return this.justify_;
        }
        getTextBaseline() {
            return this.textBaseline_;
        }
        getBackgroundFill() {
            return this.backgroundFill_;
        }
        getBackgroundStroke() {
            return this.backgroundStroke_;
        }
        getPadding() {
            return this.padding_;
        }
        getDeclutterMode() {
            return this.declutterMode_;
        }
        setOverflow(overflow) {
            this.overflow_ = overflow;
        }
        setFont(font) {
            this.font_ = font;
        }
        setMaxAngle(maxAngle) {
            this.maxAngle_ = maxAngle;
        }
        setOffsetX(offsetX) {
            this.offsetX_ = offsetX;
        }
        setOffsetY(offsetY) {
            this.offsetY_ = offsetY;
        }
        setPlacement(placement) {
            this.placement_ = placement;
        }
        setRepeat(repeat) {
            this.repeat_ = repeat;
        }
        setRotateWithView(rotateWithView) {
            this.rotateWithView_ = rotateWithView;
        }
        setFill(fill) {
            this.fill_ = fill;
        }
        setRotation(rotation) {
            this.rotation_ = rotation;
        }
        setScale(scale) {
            this.scale_ = scale;
            this.scaleArray_ = toSize(scale !== undefined ? scale : 1);
        }
        setStroke(stroke) {
            this.stroke_ = stroke;
        }
        setText(text) {
            this.text_ = text;
        }
        setTextAlign(textAlign) {
            this.textAlign_ = textAlign;
        }
        setJustify(justify) {
            this.justify_ = justify;
        }
        setTextBaseline(textBaseline) {
            this.textBaseline_ = textBaseline;
        }
        setBackgroundFill(fill) {
            this.backgroundFill_ = fill;
        }
        setBackgroundStroke(stroke) {
            this.backgroundStroke_ = stroke;
        }
        setPadding(padding) {
            this.padding_ = padding;
        }
    }
    let numTypes = 0;
    const NoneType = 0;
    const BooleanType = 1 << numTypes++;
    const NumberType = 1 << numTypes++;
    const StringType = 1 << numTypes++;
    const ColorType = 1 << numTypes++;
    const NumberArrayType = 1 << numTypes++;
    const SizeType = 1 << numTypes++;
    const AnyType = Math.pow(2, numTypes) - 1;
    const typeNames = {
        [BooleanType]: "boolean",
        [NumberType]: "number",
        [StringType]: "string",
        [ColorType]: "color",
        [NumberArrayType]: "number[]",
        [SizeType]: "size"
    };
    const namedTypes = Object.keys(typeNames).map(Number).sort(ascending);
    function typeName(type) {
        const names = [];
        for (const namedType of namedTypes) {
            if (includesType(type, namedType)) {
                names.push(typeNames[namedType]);
            }
        }
        if (names.length === 0) {
            return "untyped";
        }
        if (names.length < 3) {
            return names.join(" or ");
        }
        return names.slice(0, -1).join(", ") + ", or " + names[names.length - 1];
    }
    function includesType(broad, specific) {
        return (broad & specific) === specific;
    }
    function overlapsType(oneType, otherType) {
        return !!(oneType & otherType);
    }
    function isType(type, expected) {
        return type === expected;
    }
    class LiteralExpression {
        constructor(type, value) {
            this.type = type;
            this.value = value;
        }
    }
    class CallExpression {
        constructor(type, operator, ...args) {
            this.type = type;
            this.operator = operator;
            this.args = args;
        }
    }
    function newParsingContext() {
        return {
            variables: new Set,
            properties: new Set,
            featureId: false,
            geometryType: false,
            style: {}
        };
    }
    function getTypeFromHint(typeHint) {
        switch (typeHint) {
          case "string":
            return StringType;

          case "color":
            return ColorType;

          case "number":
            return NumberType;

          case "boolean":
            return BooleanType;

          case "number[]":
            return NumberArrayType;

          default:
            throw new Error(`Unrecognized type hint: ${typeHint}`);
        }
    }
    function parse(encoded, context, typeHint) {
        switch (typeof encoded) {
          case "boolean":
            {
                return new LiteralExpression(BooleanType, encoded);
            }

          case "number":
            {
                return new LiteralExpression(typeHint === SizeType ? SizeType : NumberType, encoded);
            }

          case "string":
            {
                let type = StringType;
                if (isStringColor(encoded)) {
                    type |= ColorType;
                }
                if (!isType(type & typeHint, NoneType)) {
                    type &= typeHint;
                }
                return new LiteralExpression(type, encoded);
            }
        }
        if (!Array.isArray(encoded)) {
            throw new Error("Expression must be an array or a primitive value");
        }
        if (encoded.length === 0) {
            throw new Error("Empty expression");
        }
        if (typeof encoded[0] === "string") {
            return parseCallExpression(encoded, context, typeHint);
        }
        for (const item of encoded) {
            if (typeof item !== "number") {
                throw new Error("Expected an array of numbers");
            }
        }
        let type = NumberArrayType;
        if (encoded.length === 2) {
            type |= SizeType;
        } else if (encoded.length === 3 || encoded.length === 4) {
            type |= ColorType;
        }
        if (typeHint) {
            type &= typeHint;
        }
        return new LiteralExpression(type, encoded);
    }
    const Ops = {
        Get: "get",
        Var: "var",
        Concat: "concat",
        GeometryType: "geometry-type",
        Any: "any",
        All: "all",
        Not: "!",
        Resolution: "resolution",
        Zoom: "zoom",
        Time: "time",
        Equal: "==",
        NotEqual: "!=",
        GreaterThan: ">",
        GreaterThanOrEqualTo: ">=",
        LessThan: "<",
        LessThanOrEqualTo: "<=",
        Multiply: "*",
        Divide: "/",
        Add: "+",
        Subtract: "-",
        Clamp: "clamp",
        Mod: "%",
        Pow: "^",
        Abs: "abs",
        Floor: "floor",
        Ceil: "ceil",
        Round: "round",
        Sin: "sin",
        Cos: "cos",
        Atan: "atan",
        Sqrt: "sqrt",
        Match: "match",
        Between: "between",
        Interpolate: "interpolate",
        Coalesce: "coalesce",
        Case: "case",
        In: "in",
        Number: "number",
        String: "string",
        Array: "array",
        Color: "color",
        Id: "id",
        Band: "band",
        Palette: "palette",
        ToString: "to-string"
    };
    const parsers = {
        [Ops.Get]: createParser(([_, typeHint]) => {
            if (typeHint !== undefined) {
                return getTypeFromHint(typeHint.value);
            }
            return AnyType;
        }, withArgsCount(1, 2), withGetArgs),
        [Ops.Var]: createParser(([firstArg]) => firstArg.type, withArgsCount(1, 1), withVarArgs),
        [Ops.Id]: createParser(NumberType | StringType, withNoArgs, usesFeatureId),
        [Ops.Concat]: createParser(StringType, withArgsCount(2, Infinity), parseArgsOfType(AnyType)),
        [Ops.GeometryType]: createParser(StringType, withNoArgs, usesGeometryType),
        [Ops.Resolution]: createParser(NumberType, withNoArgs),
        [Ops.Zoom]: createParser(NumberType, withNoArgs),
        [Ops.Time]: createParser(NumberType, withNoArgs),
        [Ops.Any]: createParser(BooleanType, withArgsCount(2, Infinity), parseArgsOfType(BooleanType)),
        [Ops.All]: createParser(BooleanType, withArgsCount(2, Infinity), parseArgsOfType(BooleanType)),
        [Ops.Not]: createParser(BooleanType, withArgsCount(1, 1), parseArgsOfType(BooleanType)),
        [Ops.Equal]: createParser(BooleanType, withArgsCount(2, 2), parseArgsOfType(AnyType), narrowArgsType),
        [Ops.NotEqual]: createParser(BooleanType, withArgsCount(2, 2), parseArgsOfType(AnyType), narrowArgsType),
        [Ops.GreaterThan]: createParser(BooleanType, withArgsCount(2, 2), parseArgsOfType(AnyType), narrowArgsType),
        [Ops.GreaterThanOrEqualTo]: createParser(BooleanType, withArgsCount(2, 2), parseArgsOfType(AnyType), narrowArgsType),
        [Ops.LessThan]: createParser(BooleanType, withArgsCount(2, 2), parseArgsOfType(AnyType), narrowArgsType),
        [Ops.LessThanOrEqualTo]: createParser(BooleanType, withArgsCount(2, 2), parseArgsOfType(AnyType), narrowArgsType),
        [Ops.Multiply]: createParser(parsedArgs => {
            let outputType = NumberType | ColorType;
            for (let i = 0; i < parsedArgs.length; i++) {
                outputType &= parsedArgs[i].type;
            }
            return outputType;
        }, withArgsCount(2, Infinity), parseArgsOfType(NumberType | ColorType), narrowArgsType),
        [Ops.Coalesce]: createParser(parsedArgs => {
            let type = AnyType;
            for (let i = 1; i < parsedArgs.length; i += 2) {
                type &= parsedArgs[i].type;
            }
            type &= parsedArgs[parsedArgs.length - 1].type;
            return type;
        }, withArgsCount(2, Infinity), parseArgsOfType(AnyType), narrowArgsType),
        [Ops.Divide]: createParser(NumberType, withArgsCount(2, 2), parseArgsOfType(NumberType)),
        [Ops.Add]: createParser(NumberType, withArgsCount(2, Infinity), parseArgsOfType(NumberType)),
        [Ops.Subtract]: createParser(NumberType, withArgsCount(2, 2), parseArgsOfType(NumberType)),
        [Ops.Clamp]: createParser(NumberType, withArgsCount(3, 3), parseArgsOfType(NumberType)),
        [Ops.Mod]: createParser(NumberType, withArgsCount(2, 2), parseArgsOfType(NumberType)),
        [Ops.Pow]: createParser(NumberType, withArgsCount(2, 2), parseArgsOfType(NumberType)),
        [Ops.Abs]: createParser(NumberType, withArgsCount(1, 1), parseArgsOfType(NumberType)),
        [Ops.Floor]: createParser(NumberType, withArgsCount(1, 1), parseArgsOfType(NumberType)),
        [Ops.Ceil]: createParser(NumberType, withArgsCount(1, 1), parseArgsOfType(NumberType)),
        [Ops.Round]: createParser(NumberType, withArgsCount(1, 1), parseArgsOfType(NumberType)),
        [Ops.Sin]: createParser(NumberType, withArgsCount(1, 1), parseArgsOfType(NumberType)),
        [Ops.Cos]: createParser(NumberType, withArgsCount(1, 1), parseArgsOfType(NumberType)),
        [Ops.Atan]: createParser(NumberType, withArgsCount(1, 2), parseArgsOfType(NumberType)),
        [Ops.Sqrt]: createParser(NumberType, withArgsCount(1, 1), parseArgsOfType(NumberType)),
        [Ops.Match]: createParser(parsedArgs => {
            let type = AnyType;
            for (let i = 2; i < parsedArgs.length; i += 2) {
                type &= parsedArgs[i].type;
            }
            type &= parsedArgs[parsedArgs.length - 1].type;
            return type;
        }, withArgsCount(4, Infinity), withEvenArgs, parseMatchArgs),
        [Ops.Between]: createParser(BooleanType, withArgsCount(3, 3), parseArgsOfType(NumberType)),
        [Ops.Interpolate]: createParser(parsedArgs => {
            let type = ColorType | NumberType;
            for (let i = 3; i < parsedArgs.length; i += 2) {
                type &= parsedArgs[i].type;
            }
            return type;
        }, withArgsCount(6, Infinity), withEvenArgs, parseInterpolateArgs),
        [Ops.Case]: createParser(parsedArgs => {
            let type = AnyType;
            for (let i = 1; i < parsedArgs.length; i += 2) {
                type &= parsedArgs[i].type;
            }
            type &= parsedArgs[parsedArgs.length - 1].type;
            return type;
        }, withArgsCount(3, Infinity), withOddArgs, parseCaseArgs),
        [Ops.In]: createParser(BooleanType, withArgsCount(2, 2), parseInArgs),
        [Ops.Number]: createParser(NumberType, withArgsCount(1, Infinity), parseArgsOfType(AnyType)),
        [Ops.String]: createParser(StringType, withArgsCount(1, Infinity), parseArgsOfType(AnyType)),
        [Ops.Array]: createParser(parsedArgs => parsedArgs.length === 2 ? NumberArrayType | SizeType : parsedArgs.length === 3 || parsedArgs.length === 4 ? NumberArrayType | ColorType : NumberArrayType, withArgsCount(1, Infinity), parseArgsOfType(NumberType)),
        [Ops.Color]: createParser(ColorType, withArgsCount(1, 4), parseArgsOfType(NumberType)),
        [Ops.Band]: createParser(NumberType, withArgsCount(1, 3), parseArgsOfType(NumberType)),
        [Ops.Palette]: createParser(ColorType, withArgsCount(2, 2), parsePaletteArgs),
        [Ops.ToString]: createParser(StringType, withArgsCount(1, 1), parseArgsOfType(BooleanType | NumberType | StringType | ColorType))
    };
    function withGetArgs(encoded, context) {
        const arg = parse(encoded[1], context);
        if (!(arg instanceof LiteralExpression)) {
            throw new Error("Expected a literal argument for get operation");
        }
        if (typeof arg.value !== "string") {
            throw new Error("Expected a string argument for get operation");
        }
        context.properties.add(arg.value);
        if (encoded.length === 3) {
            const hint = parse(encoded[2], context);
            return [ arg, hint ];
        }
        return [ arg ];
    }
    function withVarArgs(encoded, context, parsedArgs, typeHint) {
        const varName = encoded[1];
        if (typeof varName !== "string") {
            throw new Error("Expected a string argument for var operation");
        }
        context.variables.add(varName);
        if (!("variables" in context.style) || context.style.variables[varName] === undefined) {
            return [ new LiteralExpression(AnyType, varName) ];
        }
        const initialValue = context.style.variables[varName];
        const arg = parse(initialValue, context);
        arg.value = varName;
        if (typeHint && !overlapsType(typeHint, arg.type)) {
            throw new Error(`The variable ${varName} has type ${typeName(arg.type)} but the following type was expected: ${typeName(typeHint)}`);
        }
        return [ arg ];
    }
    function usesFeatureId(encoded, context) {
        context.featureId = true;
    }
    function usesGeometryType(encoded, context) {
        context.geometryType = true;
    }
    function withNoArgs(encoded, context) {
        const operation = encoded[0];
        if (encoded.length !== 1) {
            throw new Error(`Expected no arguments for ${operation} operation`);
        }
        return [];
    }
    function withArgsCount(minArgs, maxArgs) {
        return function(encoded, context) {
            const operation = encoded[0];
            const argCount = encoded.length - 1;
            if (minArgs === maxArgs) {
                if (argCount !== minArgs) {
                    const plural = minArgs === 1 ? "" : "s";
                    throw new Error(`Expected ${minArgs} argument${plural} for ${operation}, got ${argCount}`);
                }
            } else if (argCount < minArgs || argCount > maxArgs) {
                const range = maxArgs === Infinity ? `${minArgs} or more` : `${minArgs} to ${maxArgs}`;
                throw new Error(`Expected ${range} arguments for ${operation}, got ${argCount}`);
            }
        };
    }
    function parseArgsOfType(argType) {
        return function(encoded, context) {
            const operation = encoded[0];
            const argCount = encoded.length - 1;
            const args = new Array(argCount);
            for (let i = 0; i < argCount; ++i) {
                const expression = parse(encoded[i + 1], context);
                if (!overlapsType(argType, expression.type)) {
                    const gotType = typeName(argType);
                    const expectedType = typeName(expression.type);
                    throw new Error(`Unexpected type for argument ${i} of ${operation} operation` + `, got ${gotType} but expected ${expectedType}`);
                }
                expression.type &= argType;
                args[i] = expression;
            }
            return args;
        };
    }
    function narrowArgsType(encoded, context, parsedArgs) {
        const operation = encoded[0];
        const argCount = encoded.length - 1;
        let sameType = AnyType;
        for (let i = 0; i < parsedArgs.length; ++i) {
            sameType &= parsedArgs[i].type;
        }
        if (sameType === NoneType) {
            throw new Error(`No common type could be found for arguments of ${operation} operation`);
        }
        const args = new Array(argCount);
        for (let i = 0; i < argCount; ++i) {
            args[i] = parse(encoded[i + 1], context, sameType);
        }
        return args;
    }
    function withOddArgs(encoded, context) {
        const operation = encoded[0];
        const argCount = encoded.length - 1;
        if (argCount % 2 === 0) {
            throw new Error(`An odd amount of arguments was expected for operation ${operation}, got ${JSON.stringify(argCount)} instead`);
        }
    }
    function withEvenArgs(encoded, context) {
        const operation = encoded[0];
        const argCount = encoded.length - 1;
        if (argCount % 2 === 1) {
            throw new Error(`An even amount of arguments was expected for operation ${operation}, got ${JSON.stringify(argCount)} instead`);
        }
    }
    function parseMatchArgs(encoded, context, parsedArgs, typeHint) {
        const argsCount = encoded.length - 1;
        const input = parse(encoded[1], context);
        let inputType = input.type;
        const fallback = parse(encoded[encoded.length - 1], context);
        let outputType = typeHint !== undefined ? typeHint & fallback.type : fallback.type;
        const args = new Array(argsCount - 2);
        for (let i = 0; i < argsCount - 2; i += 2) {
            const match = parse(encoded[i + 2], context);
            const output = parse(encoded[i + 3], context);
            inputType &= match.type;
            outputType &= output.type;
            args[i] = match;
            args[i + 1] = output;
        }
        const expectedInputType = StringType | NumberType | BooleanType;
        if (!overlapsType(expectedInputType, inputType)) {
            throw new Error(`Expected an input of type ${typeName(expectedInputType)} for the interpolate operation` + `, got ${typeName(inputType)} instead`);
        }
        inputType &= expectedInputType;
        if (isType(outputType, NoneType)) {
            throw new Error(`Could not find a common output type for the following match operation: ` + JSON.stringify(encoded));
        }
        for (let i = 0; i < argsCount - 2; i += 2) {
            const match = parse(encoded[i + 2], context, inputType);
            const output = parse(encoded[i + 3], context, outputType);
            args[i] = match;
            args[i + 1] = output;
        }
        return [ parse(encoded[1], context, inputType), ...args, parse(encoded[encoded.length - 1], context, outputType) ];
    }
    function parseInterpolateArgs(encoded, context, parsedArgs, typeHint) {
        const interpolationType = encoded[1];
        let interpolation;
        switch (interpolationType[0]) {
          case "linear":
            interpolation = 1;
            break;

          case "exponential":
            interpolation = interpolationType[1];
            if (typeof interpolation !== "number") {
                throw new Error(`Expected a number base for exponential interpolation` + `, got ${JSON.stringify(interpolation)} instead`);
            }
            break;

          default:
            interpolation = null;
        }
        if (!interpolation) {
            throw new Error(`Invalid interpolation type: ${JSON.stringify(interpolationType)}`);
        }
        interpolation = parse(interpolation, context);
        let input = parse(encoded[2], context);
        if (!overlapsType(NumberType, input.type)) {
            throw new Error(`Expected an input of type number for the interpolate operation` + `, got ${typeName(input.type)} instead`);
        }
        input = parse(encoded[2], context, NumberType);
        const args = new Array(encoded.length - 3);
        for (let i = 0; i < args.length; i += 2) {
            let stop = parse(encoded[i + 3], context);
            if (!overlapsType(NumberType, stop.type)) {
                throw new Error(`Expected all stop input values in the interpolate operation to be of type number` + `, got ${typeName(stop.type)} at position ${i + 2} instead`);
            }
            let output = parse(encoded[i + 4], context);
            if (!overlapsType(NumberType | ColorType, output.type)) {
                throw new Error(`Expected all stop output values in the interpolate operation to be a number or color` + `, got ${typeName(output.type)} at position ${i + 3} instead`);
            }
            stop = parse(encoded[i + 3], context, NumberType);
            output = parse(encoded[i + 4], context, NumberType | ColorType);
            args[i] = stop;
            args[i + 1] = output;
        }
        return [ interpolation, input, ...args ];
    }
    function parseCaseArgs(encoded, context, parsedArgs, typeHint) {
        const fallback = parse(encoded[encoded.length - 1], context, typeHint);
        let outputType = typeHint !== undefined ? typeHint & fallback.type : fallback.type;
        const args = new Array(encoded.length - 1);
        for (let i = 0; i < args.length - 1; i += 2) {
            const condition = parse(encoded[i + 1], context);
            const output = parse(encoded[i + 2], context, typeHint);
            if (!overlapsType(BooleanType, condition.type)) {
                throw new Error(`Expected all conditions in the case operation to be of type boolean` + `, got ${typeName(condition.type)} at position ${i} instead`);
            }
            outputType &= output.type;
            args[i] = condition;
            args[i + 1] = output;
        }
        if (isType(outputType, NoneType)) {
            throw new Error(`Could not find a common output type for the following case operation: ` + JSON.stringify(encoded));
        }
        for (let i = 0; i < args.length - 1; i += 2) {
            args[i + 1] = parse(encoded[i + 2], context, outputType);
        }
        args[args.length - 1] = parse(encoded[encoded.length - 1], context, outputType);
        return args;
    }
    function parseInArgs(encoded, context) {
        let haystack = encoded[2];
        if (!Array.isArray(haystack)) {
            throw new Error(`The "in" operator was provided a literal value which was not an array as second argument.`);
        }
        if (typeof haystack[0] === "string") {
            if (haystack[0] !== "literal") {
                throw new Error(`For the "in" operator, a string array should be wrapped in a "literal" operator to disambiguate from expressions.`);
            }
            if (!Array.isArray(haystack[1])) {
                throw new Error(`The "in" operator was provided a literal value which was not an array as second argument.`);
            }
            haystack = haystack[1];
        }
        let needleType = StringType | NumberType;
        const args = new Array(haystack.length);
        for (let i = 0; i < args.length; i++) {
            const arg = parse(haystack[i], context);
            needleType &= arg.type;
            args[i] = arg;
        }
        if (isType(needleType, NoneType)) {
            throw new Error(`Could not find a common type for the following in operation: ` + JSON.stringify(encoded));
        }
        const needle = parse(encoded[1], context, needleType);
        return [ needle, ...args ];
    }
    function parsePaletteArgs(encoded, context) {
        const index = parse(encoded[1], context, NumberType);
        if (index.type !== NumberType) {
            throw new Error(`The first argument of palette must be an number, got ${typeName(index.type)} instead`);
        }
        const colors = encoded[2];
        if (!Array.isArray(colors)) {
            throw new Error("The second argument of palette must be an array");
        }
        const parsedColors = new Array(colors.length);
        for (let i = 0; i < parsedColors.length; i++) {
            const color = parse(colors[i], context, ColorType);
            if (!(color instanceof LiteralExpression)) {
                throw new Error(`The palette color at index ${i} must be a literal value`);
            }
            if (!overlapsType(color.type, ColorType)) {
                throw new Error(`The palette color at index ${i} should be of type color, got ${typeName(color.type)} instead`);
            }
            parsedColors[i] = color;
        }
        return [ index, ...parsedColors ];
    }
    function createParser(returnType, ...argValidators) {
        return function(encoded, context, typeHint) {
            const operator = encoded[0];
            let parsedArgs = [];
            for (let i = 0; i < argValidators.length; i++) {
                parsedArgs = argValidators[i](encoded, context, parsedArgs, typeHint) || parsedArgs;
            }
            let actualType = typeof returnType === "function" ? returnType(parsedArgs) : returnType;
            if (typeHint !== undefined) {
                if (!overlapsType(actualType, typeHint)) {
                    throw new Error(`The following expression was expected to return ${typeName(typeHint)}, but returns ${typeName(actualType)} instead: ${JSON.stringify(encoded)}`);
                }
                actualType &= typeHint;
            }
            if (actualType === NoneType) {
                throw new Error(`No matching type was found for the following expression: ${JSON.stringify(encoded)}`);
            }
            return new CallExpression(actualType, operator, ...parsedArgs);
        };
    }
    function parseCallExpression(encoded, context, typeHint) {
        const operator = encoded[0];
        const parser = parsers[operator];
        if (!parser) {
            throw new Error(`Unknown operator: ${operator}`);
        }
        return parser(encoded, context, typeHint);
    }
    function computeGeometryType(geometry) {
        if (!geometry) {
            return "";
        }
        const type = geometry.getType();
        switch (type) {
          case "Point":
          case "LineString":
          case "Polygon":
            return type;

          case "MultiPoint":
          case "MultiLineString":
          case "MultiPolygon":
            return type.substring(5);

          case "Circle":
            return "Polygon";

          case "GeometryCollection":
            return computeGeometryType(geometry.getGeometries()[0]);

          default:
            return "";
        }
    }
    function newEvaluationContext() {
        return {
            variables: {},
            properties: {},
            resolution: NaN,
            featureId: null,
            geometryType: ""
        };
    }
    function buildExpression(encoded, type, context) {
        const expression = parse(encoded, context);
        if (!overlapsType(type, expression.type)) {
            const expected = typeName(type);
            const actual = typeName(expression.type);
            throw new Error(`Expected expression to be of type ${expected}, got ${actual}`);
        }
        return compileExpression(expression);
    }
    function compileExpression(expression, context) {
        if (expression instanceof LiteralExpression) {
            if (expression.type === ColorType && typeof expression.value === "string") {
                const colorValue = fromString(expression.value);
                return function() {
                    return colorValue;
                };
            }
            return function() {
                return expression.value;
            };
        }
        const operator = expression.operator;
        switch (operator) {
          case Ops.Number:
          case Ops.String:
          case Ops.Coalesce:
            {
                return compileAssertionExpression(expression);
            }

          case Ops.Get:
          case Ops.Var:
            {
                return compileAccessorExpression(expression);
            }

          case Ops.Id:
            {
                return context => context.featureId;
            }

          case Ops.GeometryType:
            {
                return context => context.geometryType;
            }

          case Ops.Concat:
            {
                const args = expression.args.map(e => compileExpression(e));
                return context => "".concat(...args.map(arg => arg(context).toString()));
            }

          case Ops.Resolution:
            {
                return context => context.resolution;
            }

          case Ops.Any:
          case Ops.All:
          case Ops.Between:
          case Ops.In:
          case Ops.Not:
            {
                return compileLogicalExpression(expression);
            }

          case Ops.Equal:
          case Ops.NotEqual:
          case Ops.LessThan:
          case Ops.LessThanOrEqualTo:
          case Ops.GreaterThan:
          case Ops.GreaterThanOrEqualTo:
            {
                return compileComparisonExpression(expression);
            }

          case Ops.Multiply:
          case Ops.Divide:
          case Ops.Add:
          case Ops.Subtract:
          case Ops.Clamp:
          case Ops.Mod:
          case Ops.Pow:
          case Ops.Abs:
          case Ops.Floor:
          case Ops.Ceil:
          case Ops.Round:
          case Ops.Sin:
          case Ops.Cos:
          case Ops.Atan:
          case Ops.Sqrt:
            {
                return compileNumericExpression(expression);
            }

          case Ops.Case:
            {
                return compileCaseExpression(expression);
            }

          case Ops.Match:
            {
                return compileMatchExpression(expression);
            }

          case Ops.Interpolate:
            {
                return compileInterpolateExpression(expression);
            }

          case Ops.ToString:
            {
                return compileConvertExpression(expression);
            }

          default:
            {
                throw new Error(`Unsupported operator ${operator}`);
            }
        }
    }
    function compileAssertionExpression(expression, context) {
        const type = expression.operator;
        const length = expression.args.length;
        const args = new Array(length);
        for (let i = 0; i < length; ++i) {
            args[i] = compileExpression(expression.args[i]);
        }
        switch (type) {
          case Ops.Coalesce:
            {
                return context => {
                    for (let i = 0; i < length; ++i) {
                        const value = args[i](context);
                        if (typeof value !== "undefined" && value !== null) {
                            return value;
                        }
                    }
                    throw new Error("Expected one of the values to be non-null");
                };
            }

          case Ops.Number:
          case Ops.String:
            {
                return context => {
                    for (let i = 0; i < length; ++i) {
                        const value = args[i](context);
                        if (typeof value === type) {
                            return value;
                        }
                    }
                    throw new Error(`Expected one of the values to be a ${type}`);
                };
            }

          default:
            {
                throw new Error(`Unsupported assertion operator ${type}`);
            }
        }
    }
    function compileAccessorExpression(expression, context) {
        const nameExpression = expression.args[0];
        const name = nameExpression.value;
        switch (expression.operator) {
          case Ops.Get:
            {
                return context => context.properties[name];
            }

          case Ops.Var:
            {
                return context => context.variables[name];
            }

          default:
            {
                throw new Error(`Unsupported accessor operator ${expression.operator}`);
            }
        }
    }
    function compileComparisonExpression(expression, context) {
        const op = expression.operator;
        const left = compileExpression(expression.args[0]);
        const right = compileExpression(expression.args[1]);
        switch (op) {
          case Ops.Equal:
            {
                return context => left(context) === right(context);
            }

          case Ops.NotEqual:
            {
                return context => left(context) !== right(context);
            }

          case Ops.LessThan:
            {
                return context => left(context) < right(context);
            }

          case Ops.LessThanOrEqualTo:
            {
                return context => left(context) <= right(context);
            }

          case Ops.GreaterThan:
            {
                return context => left(context) > right(context);
            }

          case Ops.GreaterThanOrEqualTo:
            {
                return context => left(context) >= right(context);
            }

          default:
            {
                throw new Error(`Unsupported comparison operator ${op}`);
            }
        }
    }
    function compileLogicalExpression(expression, context) {
        const op = expression.operator;
        const length = expression.args.length;
        const args = new Array(length);
        for (let i = 0; i < length; ++i) {
            args[i] = compileExpression(expression.args[i]);
        }
        switch (op) {
          case Ops.Any:
            {
                return context => {
                    for (let i = 0; i < length; ++i) {
                        if (args[i](context)) {
                            return true;
                        }
                    }
                    return false;
                };
            }

          case Ops.All:
            {
                return context => {
                    for (let i = 0; i < length; ++i) {
                        if (!args[i](context)) {
                            return false;
                        }
                    }
                    return true;
                };
            }

          case Ops.Between:
            {
                return context => {
                    const value = args[0](context);
                    const min = args[1](context);
                    const max = args[2](context);
                    return value >= min && value <= max;
                };
            }

          case Ops.In:
            {
                return context => {
                    const value = args[0](context);
                    for (let i = 1; i < length; ++i) {
                        if (value === args[i](context)) {
                            return true;
                        }
                    }
                    return false;
                };
            }

          case Ops.Not:
            {
                return context => !args[0](context);
            }

          default:
            {
                throw new Error(`Unsupported logical operator ${op}`);
            }
        }
    }
    function compileNumericExpression(expression, context) {
        const op = expression.operator;
        const length = expression.args.length;
        const args = new Array(length);
        for (let i = 0; i < length; ++i) {
            args[i] = compileExpression(expression.args[i]);
        }
        switch (op) {
          case Ops.Multiply:
            {
                return context => {
                    let value = 1;
                    for (let i = 0; i < length; ++i) {
                        value *= args[i](context);
                    }
                    return value;
                };
            }

          case Ops.Divide:
            {
                return context => args[0](context) / args[1](context);
            }

          case Ops.Add:
            {
                return context => {
                    let value = 0;
                    for (let i = 0; i < length; ++i) {
                        value += args[i](context);
                    }
                    return value;
                };
            }

          case Ops.Subtract:
            {
                return context => args[0](context) - args[1](context);
            }

          case Ops.Clamp:
            {
                return context => {
                    const value = args[0](context);
                    const min = args[1](context);
                    if (value < min) {
                        return min;
                    }
                    const max = args[2](context);
                    if (value > max) {
                        return max;
                    }
                    return value;
                };
            }

          case Ops.Mod:
            {
                return context => args[0](context) % args[1](context);
            }

          case Ops.Pow:
            {
                return context => Math.pow(args[0](context), args[1](context));
            }

          case Ops.Abs:
            {
                return context => Math.abs(args[0](context));
            }

          case Ops.Floor:
            {
                return context => Math.floor(args[0](context));
            }

          case Ops.Ceil:
            {
                return context => Math.ceil(args[0](context));
            }

          case Ops.Round:
            {
                return context => Math.round(args[0](context));
            }

          case Ops.Sin:
            {
                return context => Math.sin(args[0](context));
            }

          case Ops.Cos:
            {
                return context => Math.cos(args[0](context));
            }

          case Ops.Atan:
            {
                if (length === 2) {
                    return context => Math.atan2(args[0](context), args[1](context));
                }
                return context => Math.atan(args[0](context));
            }

          case Ops.Sqrt:
            {
                return context => Math.sqrt(args[0](context));
            }

          default:
            {
                throw new Error(`Unsupported numeric operator ${op}`);
            }
        }
    }
    function compileCaseExpression(expression, context) {
        const length = expression.args.length;
        const args = new Array(length);
        for (let i = 0; i < length; ++i) {
            args[i] = compileExpression(expression.args[i]);
        }
        return context => {
            for (let i = 0; i < length - 1; i += 2) {
                const condition = args[i](context);
                if (condition) {
                    return args[i + 1](context);
                }
            }
            return args[length - 1](context);
        };
    }
    function compileMatchExpression(expression, context) {
        const length = expression.args.length;
        const args = new Array(length);
        for (let i = 0; i < length; ++i) {
            args[i] = compileExpression(expression.args[i]);
        }
        return context => {
            const value = args[0](context);
            for (let i = 1; i < length; i += 2) {
                if (value === args[i](context)) {
                    return args[i + 1](context);
                }
            }
            return args[length - 1](context);
        };
    }
    function compileInterpolateExpression(expression, context) {
        const length = expression.args.length;
        const args = new Array(length);
        for (let i = 0; i < length; ++i) {
            args[i] = compileExpression(expression.args[i]);
        }
        return context => {
            const base = args[0](context);
            const value = args[1](context);
            let previousInput;
            let previousOutput;
            for (let i = 2; i < length; i += 2) {
                const input = args[i](context);
                let output = args[i + 1](context);
                const isColor = Array.isArray(output);
                if (isColor) {
                    output = withAlpha(output);
                }
                if (input >= value) {
                    if (i === 2) {
                        return output;
                    }
                    if (isColor) {
                        return interpolateColor(base, value, previousInput, previousOutput, input, output);
                    }
                    return interpolateNumber(base, value, previousInput, previousOutput, input, output);
                }
                previousInput = input;
                previousOutput = output;
            }
            return previousOutput;
        };
    }
    function compileConvertExpression(expression, context) {
        const op = expression.operator;
        const length = expression.args.length;
        const args = new Array(length);
        for (let i = 0; i < length; ++i) {
            args[i] = compileExpression(expression.args[i]);
        }
        switch (op) {
          case Ops.ToString:
            {
                return context => {
                    const value = args[0](context);
                    if (expression.args[0].type === ColorType) {
                        return toString(value);
                    }
                    return value.toString();
                };
            }

          default:
            {
                throw new Error(`Unsupported convert operator ${op}`);
            }
        }
    }
    function interpolateNumber(base, value, input1, output1, input2, output2) {
        const delta = input2 - input1;
        if (delta === 0) {
            return output1;
        }
        const along = value - input1;
        const factor = base === 1 ? along / delta : (Math.pow(base, along) - 1) / (Math.pow(base, delta) - 1);
        return output1 + factor * (output2 - output1);
    }
    function interpolateColor(base, value, input1, rgba1, input2, rgba2) {
        const delta = input2 - input1;
        if (delta === 0) {
            return rgba1;
        }
        const lcha1 = rgbaToLcha(rgba1);
        const lcha2 = rgbaToLcha(rgba2);
        let deltaHue = lcha2[2] - lcha1[2];
        if (deltaHue > 180) {
            deltaHue -= 360;
        } else if (deltaHue < -180) {
            deltaHue += 360;
        }
        const lcha = [ interpolateNumber(base, value, input1, lcha1[0], input2, lcha2[0]), interpolateNumber(base, value, input1, lcha1[1], input2, lcha2[1]), lcha1[2] + interpolateNumber(base, value, input1, 0, input2, deltaHue), interpolateNumber(base, value, input1, rgba1[3], input2, rgba2[3]) ];
        return normalize(lchaToRgba(lcha));
    }
    function always$1(context) {
        return true;
    }
    function rulesToStyleFunction(rules) {
        const parsingContext = newParsingContext();
        const evaluator = buildRuleSet(rules, parsingContext);
        const evaluationContext = newEvaluationContext();
        return function(feature, resolution) {
            evaluationContext.properties = feature.getPropertiesInternal();
            evaluationContext.resolution = resolution;
            if (parsingContext.featureId) {
                const id = feature.getId();
                if (id !== undefined) {
                    evaluationContext.featureId = id;
                } else {
                    evaluationContext.featureId = null;
                }
            }
            if (parsingContext.geometryType) {
                evaluationContext.geometryType = computeGeometryType(feature.getGeometry());
            }
            return evaluator(evaluationContext);
        };
    }
    function flatStylesToStyleFunction(flatStyles) {
        const parsingContext = newParsingContext();
        const length = flatStyles.length;
        const evaluators = new Array(length);
        for (let i = 0; i < length; ++i) {
            evaluators[i] = buildStyle(flatStyles[i], parsingContext);
        }
        const evaluationContext = newEvaluationContext();
        const styles = new Array(length);
        return function(feature, resolution) {
            evaluationContext.properties = feature.getPropertiesInternal();
            evaluationContext.resolution = resolution;
            if (parsingContext.featureId) {
                const id = feature.getId();
                if (id !== undefined) {
                    evaluationContext.featureId = id;
                } else {
                    evaluationContext.featureId = null;
                }
            }
            let nonNullCount = 0;
            for (let i = 0; i < length; ++i) {
                const style = evaluators[i](evaluationContext);
                if (style) {
                    styles[nonNullCount] = style;
                    nonNullCount += 1;
                }
            }
            styles.length = nonNullCount;
            return styles;
        };
    }
    function buildRuleSet(rules, context) {
        const length = rules.length;
        const compiledRules = new Array(length);
        for (let i = 0; i < length; ++i) {
            const rule = rules[i];
            const filter = "filter" in rule ? buildExpression(rule.filter, BooleanType, context) : always$1;
            let styles;
            if (Array.isArray(rule.style)) {
                const styleLength = rule.style.length;
                styles = new Array(styleLength);
                for (let j = 0; j < styleLength; ++j) {
                    styles[j] = buildStyle(rule.style[j], context);
                }
            } else {
                styles = [ buildStyle(rule.style, context) ];
            }
            compiledRules[i] = {
                filter: filter,
                styles: styles
            };
        }
        return function(context) {
            const styles = [];
            let someMatched = false;
            for (let i = 0; i < length; ++i) {
                const filterEvaluator = compiledRules[i].filter;
                if (!filterEvaluator(context)) {
                    continue;
                }
                if (rules[i].else && someMatched) {
                    continue;
                }
                someMatched = true;
                for (const styleEvaluator of compiledRules[i].styles) {
                    const style = styleEvaluator(context);
                    if (!style) {
                        continue;
                    }
                    styles.push(style);
                }
            }
            return styles;
        };
    }
    function buildStyle(flatStyle, context) {
        const evaluateFill = buildFill(flatStyle, "", context);
        const evaluateStroke = buildStroke(flatStyle, "", context);
        const evaluateText = buildText(flatStyle, context);
        const evaluateImage = buildImage(flatStyle, context);
        const evaluateZIndex = numberEvaluator(flatStyle, "z-index", context);
        if (!evaluateFill && !evaluateStroke && !evaluateText && !evaluateImage && !isEmpty$1(flatStyle)) {
            throw new Error("No fill, stroke, point, or text symbolizer properties in style: " + JSON.stringify(flatStyle));
        }
        const style = new Style;
        return function(context) {
            let empty = true;
            if (evaluateFill) {
                const fill = evaluateFill(context);
                if (fill) {
                    empty = false;
                }
                style.setFill(fill);
            }
            if (evaluateStroke) {
                const stroke = evaluateStroke(context);
                if (stroke) {
                    empty = false;
                }
                style.setStroke(stroke);
            }
            if (evaluateText) {
                const text = evaluateText(context);
                if (text) {
                    empty = false;
                }
                style.setText(text);
            }
            if (evaluateImage) {
                const image = evaluateImage(context);
                if (image) {
                    empty = false;
                }
                style.setImage(image);
            }
            if (evaluateZIndex) {
                style.setZIndex(evaluateZIndex(context));
            }
            if (empty) {
                return null;
            }
            return style;
        };
    }
    function buildFill(flatStyle, prefix, context) {
        let evaluateColor;
        if (prefix + "fill-pattern-src" in flatStyle) {
            evaluateColor = patternEvaluator(flatStyle, prefix + "fill-", context);
        } else {
            evaluateColor = colorLikeEvaluator(flatStyle, prefix + "fill-color", context);
        }
        if (!evaluateColor) {
            return null;
        }
        const fill = new Fill;
        return function(context) {
            const color = evaluateColor(context);
            if (color === "none") {
                return null;
            }
            fill.setColor(color);
            return fill;
        };
    }
    function buildStroke(flatStyle, prefix, context) {
        const evaluateWidth = numberEvaluator(flatStyle, prefix + "stroke-width", context);
        const evaluateColor = colorLikeEvaluator(flatStyle, prefix + "stroke-color", context);
        if (!evaluateWidth && !evaluateColor) {
            return null;
        }
        const evaluateLineCap = stringEvaluator(flatStyle, prefix + "stroke-line-cap", context);
        const evaluateLineJoin = stringEvaluator(flatStyle, prefix + "stroke-line-join", context);
        const evaluateLineDash = numberArrayEvaluator(flatStyle, prefix + "stroke-line-dash", context);
        const evaluateLineDashOffset = numberEvaluator(flatStyle, prefix + "stroke-line-dash-offset", context);
        const evaluateMiterLimit = numberEvaluator(flatStyle, prefix + "stroke-miter-limit", context);
        const stroke = new Stroke;
        return function(context) {
            if (evaluateColor) {
                const color = evaluateColor(context);
                if (color === "none") {
                    return null;
                }
                stroke.setColor(color);
            }
            if (evaluateWidth) {
                stroke.setWidth(evaluateWidth(context));
            }
            if (evaluateLineCap) {
                const lineCap = evaluateLineCap(context);
                if (lineCap !== "butt" && lineCap !== "round" && lineCap !== "square") {
                    throw new Error("Expected butt, round, or square line cap");
                }
                stroke.setLineCap(lineCap);
            }
            if (evaluateLineJoin) {
                const lineJoin = evaluateLineJoin(context);
                if (lineJoin !== "bevel" && lineJoin !== "round" && lineJoin !== "miter") {
                    throw new Error("Expected bevel, round, or miter line join");
                }
                stroke.setLineJoin(lineJoin);
            }
            if (evaluateLineDash) {
                stroke.setLineDash(evaluateLineDash(context));
            }
            if (evaluateLineDashOffset) {
                stroke.setLineDashOffset(evaluateLineDashOffset(context));
            }
            if (evaluateMiterLimit) {
                stroke.setMiterLimit(evaluateMiterLimit(context));
            }
            return stroke;
        };
    }
    function buildText(flatStyle, context) {
        const prefix = "text-";
        const evaluateValue = stringEvaluator(flatStyle, prefix + "value", context);
        if (!evaluateValue) {
            return null;
        }
        const evaluateFill = buildFill(flatStyle, prefix, context);
        const evaluateBackgroundFill = buildFill(flatStyle, prefix + "background-", context);
        const evaluateStroke = buildStroke(flatStyle, prefix, context);
        const evaluateBackgroundStroke = buildStroke(flatStyle, prefix + "background-", context);
        const evaluateFont = stringEvaluator(flatStyle, prefix + "font", context);
        const evaluateMaxAngle = numberEvaluator(flatStyle, prefix + "max-angle", context);
        const evaluateOffsetX = numberEvaluator(flatStyle, prefix + "offset-x", context);
        const evaluateOffsetY = numberEvaluator(flatStyle, prefix + "offset-y", context);
        const evaluateOverflow = booleanEvaluator(flatStyle, prefix + "overflow", context);
        const evaluatePlacement = stringEvaluator(flatStyle, prefix + "placement", context);
        const evaluateRepeat = numberEvaluator(flatStyle, prefix + "repeat", context);
        const evaluateScale = sizeLikeEvaluator(flatStyle, prefix + "scale", context);
        const evaluateRotateWithView = booleanEvaluator(flatStyle, prefix + "rotate-with-view", context);
        const evaluateRotation = numberEvaluator(flatStyle, prefix + "rotation", context);
        const evaluateAlign = stringEvaluator(flatStyle, prefix + "align", context);
        const evaluateJustify = stringEvaluator(flatStyle, prefix + "justify", context);
        const evaluateBaseline = stringEvaluator(flatStyle, prefix + "baseline", context);
        const evaluatePadding = numberArrayEvaluator(flatStyle, prefix + "padding", context);
        const declutterMode = optionalDeclutterMode(flatStyle, prefix + "declutter-mode");
        const text = new Text({
            declutterMode: declutterMode
        });
        return function(context) {
            text.setText(evaluateValue(context));
            if (evaluateFill) {
                text.setFill(evaluateFill(context));
            }
            if (evaluateBackgroundFill) {
                text.setBackgroundFill(evaluateBackgroundFill(context));
            }
            if (evaluateStroke) {
                text.setStroke(evaluateStroke(context));
            }
            if (evaluateBackgroundStroke) {
                text.setBackgroundStroke(evaluateBackgroundStroke(context));
            }
            if (evaluateFont) {
                text.setFont(evaluateFont(context));
            }
            if (evaluateMaxAngle) {
                text.setMaxAngle(evaluateMaxAngle(context));
            }
            if (evaluateOffsetX) {
                text.setOffsetX(evaluateOffsetX(context));
            }
            if (evaluateOffsetY) {
                text.setOffsetY(evaluateOffsetY(context));
            }
            if (evaluateOverflow) {
                text.setOverflow(evaluateOverflow(context));
            }
            if (evaluatePlacement) {
                const placement = evaluatePlacement(context);
                if (placement !== "point" && placement !== "line") {
                    throw new Error("Expected point or line for text-placement");
                }
                text.setPlacement(placement);
            }
            if (evaluateRepeat) {
                text.setRepeat(evaluateRepeat(context));
            }
            if (evaluateScale) {
                text.setScale(evaluateScale(context));
            }
            if (evaluateRotateWithView) {
                text.setRotateWithView(evaluateRotateWithView(context));
            }
            if (evaluateRotation) {
                text.setRotation(evaluateRotation(context));
            }
            if (evaluateAlign) {
                const textAlign = evaluateAlign(context);
                if (textAlign !== "left" && textAlign !== "center" && textAlign !== "right" && textAlign !== "end" && textAlign !== "start") {
                    throw new Error("Expected left, right, center, start, or end for text-align");
                }
                text.setTextAlign(textAlign);
            }
            if (evaluateJustify) {
                const justify = evaluateJustify(context);
                if (justify !== "left" && justify !== "right" && justify !== "center") {
                    throw new Error("Expected left, right, or center for text-justify");
                }
                text.setJustify(justify);
            }
            if (evaluateBaseline) {
                const textBaseline = evaluateBaseline(context);
                if (textBaseline !== "bottom" && textBaseline !== "top" && textBaseline !== "middle" && textBaseline !== "alphabetic" && textBaseline !== "hanging") {
                    throw new Error("Expected bottom, top, middle, alphabetic, or hanging for text-baseline");
                }
                text.setTextBaseline(textBaseline);
            }
            if (evaluatePadding) {
                text.setPadding(evaluatePadding(context));
            }
            return text;
        };
    }
    function buildImage(flatStyle, context) {
        if ("icon-src" in flatStyle) {
            return buildIcon(flatStyle, context);
        }
        if ("shape-points" in flatStyle) {
            return buildShape(flatStyle, context);
        }
        if ("circle-radius" in flatStyle) {
            return buildCircle(flatStyle, context);
        }
        return null;
    }
    function buildIcon(flatStyle, context) {
        const prefix = "icon-";
        const srcName = prefix + "src";
        const src = requireString(flatStyle[srcName], srcName);
        const evaluateAnchor = coordinateEvaluator(flatStyle, prefix + "anchor", context);
        const evaluateScale = sizeLikeEvaluator(flatStyle, prefix + "scale", context);
        const evaluateOpacity = numberEvaluator(flatStyle, prefix + "opacity", context);
        const evaluateDisplacement = coordinateEvaluator(flatStyle, prefix + "displacement", context);
        const evaluateRotation = numberEvaluator(flatStyle, prefix + "rotation", context);
        const evaluateRotateWithView = booleanEvaluator(flatStyle, prefix + "rotate-with-view", context);
        const anchorOrigin = optionalIconOrigin(flatStyle, prefix + "anchor-origin");
        const anchorXUnits = optionalIconAnchorUnits(flatStyle, prefix + "anchor-x-units");
        const anchorYUnits = optionalIconAnchorUnits(flatStyle, prefix + "anchor-y-units");
        const color = optionalColorLike(flatStyle, prefix + "color");
        const crossOrigin = optionalString(flatStyle, prefix + "cross-origin");
        const offset = optionalNumberArray(flatStyle, prefix + "offset");
        const offsetOrigin = optionalIconOrigin(flatStyle, prefix + "offset-origin");
        const width = optionalNumber(flatStyle, prefix + "width");
        const height = optionalNumber(flatStyle, prefix + "height");
        const size = optionalSize(flatStyle, prefix + "size");
        const declutterMode = optionalDeclutterMode(flatStyle, prefix + "declutter-mode");
        const icon = new Icon({
            src: src,
            anchorOrigin: anchorOrigin,
            anchorXUnits: anchorXUnits,
            anchorYUnits: anchorYUnits,
            color: color,
            crossOrigin: crossOrigin,
            offset: offset,
            offsetOrigin: offsetOrigin,
            height: height,
            width: width,
            size: size,
            declutterMode: declutterMode
        });
        return function(context) {
            if (evaluateOpacity) {
                icon.setOpacity(evaluateOpacity(context));
            }
            if (evaluateDisplacement) {
                icon.setDisplacement(evaluateDisplacement(context));
            }
            if (evaluateRotation) {
                icon.setRotation(evaluateRotation(context));
            }
            if (evaluateRotateWithView) {
                icon.setRotateWithView(evaluateRotateWithView(context));
            }
            if (evaluateScale) {
                icon.setScale(evaluateScale(context));
            }
            if (evaluateAnchor) {
                icon.setAnchor(evaluateAnchor(context));
            }
            return icon;
        };
    }
    function buildShape(flatStyle, context) {
        const prefix = "shape-";
        const pointsName = prefix + "points";
        const radiusName = prefix + "radius";
        const points = requireNumber(flatStyle[pointsName], pointsName);
        const radius = requireNumber(flatStyle[radiusName], radiusName);
        const evaluateFill = buildFill(flatStyle, prefix, context);
        const evaluateStroke = buildStroke(flatStyle, prefix, context);
        const evaluateScale = sizeLikeEvaluator(flatStyle, prefix + "scale", context);
        const evaluateDisplacement = coordinateEvaluator(flatStyle, prefix + "displacement", context);
        const evaluateRotation = numberEvaluator(flatStyle, prefix + "rotation", context);
        const evaluateRotateWithView = booleanEvaluator(flatStyle, prefix + "rotate-with-view", context);
        const radius2 = optionalNumber(flatStyle, prefix + "radius2");
        const angle = optionalNumber(flatStyle, prefix + "angle");
        const declutterMode = optionalDeclutterMode(flatStyle, prefix + "declutter-mode");
        const shape = new RegularShape({
            points: points,
            radius: radius,
            radius2: radius2,
            angle: angle,
            declutterMode: declutterMode
        });
        return function(context) {
            if (evaluateFill) {
                shape.setFill(evaluateFill(context));
            }
            if (evaluateStroke) {
                shape.setStroke(evaluateStroke(context));
            }
            if (evaluateDisplacement) {
                shape.setDisplacement(evaluateDisplacement(context));
            }
            if (evaluateRotation) {
                shape.setRotation(evaluateRotation(context));
            }
            if (evaluateRotateWithView) {
                shape.setRotateWithView(evaluateRotateWithView(context));
            }
            if (evaluateScale) {
                shape.setScale(evaluateScale(context));
            }
            return shape;
        };
    }
    function buildCircle(flatStyle, context) {
        const prefix = "circle-";
        const evaluateFill = buildFill(flatStyle, prefix, context);
        const evaluateStroke = buildStroke(flatStyle, prefix, context);
        const evaluateRadius = numberEvaluator(flatStyle, prefix + "radius", context);
        const evaluateScale = sizeLikeEvaluator(flatStyle, prefix + "scale", context);
        const evaluateDisplacement = coordinateEvaluator(flatStyle, prefix + "displacement", context);
        const evaluateRotation = numberEvaluator(flatStyle, prefix + "rotation", context);
        const evaluateRotateWithView = booleanEvaluator(flatStyle, prefix + "rotate-with-view", context);
        const declutterMode = optionalDeclutterMode(flatStyle, prefix + "declutter-mode");
        const circle = new CircleStyle({
            radius: 5,
            declutterMode: declutterMode
        });
        return function(context) {
            if (evaluateRadius) {
                circle.setRadius(evaluateRadius(context));
            }
            if (evaluateFill) {
                circle.setFill(evaluateFill(context));
            }
            if (evaluateStroke) {
                circle.setStroke(evaluateStroke(context));
            }
            if (evaluateDisplacement) {
                circle.setDisplacement(evaluateDisplacement(context));
            }
            if (evaluateRotation) {
                circle.setRotation(evaluateRotation(context));
            }
            if (evaluateRotateWithView) {
                circle.setRotateWithView(evaluateRotateWithView(context));
            }
            if (evaluateScale) {
                circle.setScale(evaluateScale(context));
            }
            return circle;
        };
    }
    function numberEvaluator(flatStyle, name, context) {
        if (!(name in flatStyle)) {
            return undefined;
        }
        const evaluator = buildExpression(flatStyle[name], NumberType, context);
        return function(context) {
            return requireNumber(evaluator(context), name);
        };
    }
    function stringEvaluator(flatStyle, name, context) {
        if (!(name in flatStyle)) {
            return null;
        }
        const evaluator = buildExpression(flatStyle[name], StringType, context);
        return function(context) {
            return requireString(evaluator(context), name);
        };
    }
    function patternEvaluator(flatStyle, prefix, context) {
        const srcEvaluator = stringEvaluator(flatStyle, prefix + "pattern-src", context);
        const offsetEvaluator = sizeEvaluator(flatStyle, prefix + "pattern-offset", context);
        const patternSizeEvaluator = sizeEvaluator(flatStyle, prefix + "pattern-size", context);
        const colorEvaluator = colorLikeEvaluator(flatStyle, prefix + "color", context);
        return function(context) {
            return {
                src: srcEvaluator(context),
                offset: offsetEvaluator && offsetEvaluator(context),
                size: patternSizeEvaluator && patternSizeEvaluator(context),
                color: colorEvaluator && colorEvaluator(context)
            };
        };
    }
    function booleanEvaluator(flatStyle, name, context) {
        if (!(name in flatStyle)) {
            return null;
        }
        const evaluator = buildExpression(flatStyle[name], BooleanType, context);
        return function(context) {
            const value = evaluator(context);
            if (typeof value !== "boolean") {
                throw new Error(`Expected a boolean for ${name}`);
            }
            return value;
        };
    }
    function colorLikeEvaluator(flatStyle, name, context) {
        if (!(name in flatStyle)) {
            return null;
        }
        const evaluator = buildExpression(flatStyle[name], ColorType | StringType, context);
        return function(context) {
            return requireColorLike(evaluator(context), name);
        };
    }
    function numberArrayEvaluator(flatStyle, name, context) {
        if (!(name in flatStyle)) {
            return null;
        }
        const evaluator = buildExpression(flatStyle[name], NumberArrayType, context);
        return function(context) {
            return requireNumberArray(evaluator(context), name);
        };
    }
    function coordinateEvaluator(flatStyle, name, context) {
        if (!(name in flatStyle)) {
            return null;
        }
        const evaluator = buildExpression(flatStyle[name], NumberArrayType, context);
        return function(context) {
            const array = requireNumberArray(evaluator(context), name);
            if (array.length !== 2) {
                throw new Error(`Expected two numbers for ${name}`);
            }
            return array;
        };
    }
    function sizeEvaluator(flatStyle, name, context) {
        if (!(name in flatStyle)) {
            return null;
        }
        const evaluator = buildExpression(flatStyle[name], NumberArrayType, context);
        return function(context) {
            return requireSize(evaluator(context), name);
        };
    }
    function sizeLikeEvaluator(flatStyle, name, context) {
        if (!(name in flatStyle)) {
            return null;
        }
        const evaluator = buildExpression(flatStyle[name], NumberArrayType | NumberType, context);
        return function(context) {
            return requireSizeLike(evaluator(context), name);
        };
    }
    function optionalNumber(flatStyle, property) {
        const value = flatStyle[property];
        if (value === undefined) {
            return undefined;
        }
        if (typeof value !== "number") {
            throw new Error(`Expected a number for ${property}`);
        }
        return value;
    }
    function optionalSize(flatStyle, property) {
        const encoded = flatStyle[property];
        if (encoded === undefined) {
            return undefined;
        }
        if (typeof encoded === "number") {
            return toSize(encoded);
        }
        if (!Array.isArray(encoded)) {
            throw new Error(`Expected a number or size array for ${property}`);
        }
        if (encoded.length !== 2 || typeof encoded[0] !== "number" || typeof encoded[1] !== "number") {
            throw new Error(`Expected a number or size array for ${property}`);
        }
        return encoded;
    }
    function optionalString(flatStyle, property) {
        const encoded = flatStyle[property];
        if (encoded === undefined) {
            return undefined;
        }
        if (typeof encoded !== "string") {
            throw new Error(`Expected a string for ${property}`);
        }
        return encoded;
    }
    function optionalIconOrigin(flatStyle, property) {
        const encoded = flatStyle[property];
        if (encoded === undefined) {
            return undefined;
        }
        if (encoded !== "bottom-left" && encoded !== "bottom-right" && encoded !== "top-left" && encoded !== "top-right") {
            throw new Error(`Expected bottom-left, bottom-right, top-left, or top-right for ${property}`);
        }
        return encoded;
    }
    function optionalIconAnchorUnits(flatStyle, property) {
        const encoded = flatStyle[property];
        if (encoded === undefined) {
            return undefined;
        }
        if (encoded !== "pixels" && encoded !== "fraction") {
            throw new Error(`Expected pixels or fraction for ${property}`);
        }
        return encoded;
    }
    function optionalNumberArray(flatStyle, property) {
        const encoded = flatStyle[property];
        if (encoded === undefined) {
            return undefined;
        }
        return requireNumberArray(encoded, property);
    }
    function optionalDeclutterMode(flatStyle, property) {
        const encoded = flatStyle[property];
        if (encoded === undefined) {
            return undefined;
        }
        if (typeof encoded !== "string") {
            throw new Error(`Expected a string for ${property}`);
        }
        if (encoded !== "declutter" && encoded !== "obstacle" && encoded !== "none") {
            throw new Error(`Expected declutter, obstacle, or none for ${property}`);
        }
        return encoded;
    }
    function optionalColorLike(flatStyle, property) {
        const encoded = flatStyle[property];
        if (encoded === undefined) {
            return undefined;
        }
        return requireColorLike(encoded, property);
    }
    function requireNumberArray(value, property) {
        if (!Array.isArray(value)) {
            throw new Error(`Expected an array for ${property}`);
        }
        const length = value.length;
        for (let i = 0; i < length; ++i) {
            if (typeof value[i] !== "number") {
                throw new Error(`Expected an array of numbers for ${property}`);
            }
        }
        return value;
    }
    function requireString(value, property) {
        if (typeof value !== "string") {
            throw new Error(`Expected a string for ${property}`);
        }
        return value;
    }
    function requireNumber(value, property) {
        if (typeof value !== "number") {
            throw new Error(`Expected a number for ${property}`);
        }
        return value;
    }
    function requireColorLike(value, property) {
        if (typeof value === "string") {
            return value;
        }
        const array = requireNumberArray(value, property);
        const length = array.length;
        if (length < 3 || length > 4) {
            throw new Error(`Expected a color with 3 or 4 values for ${property}`);
        }
        return array;
    }
    function requireSize(value, property) {
        const size = requireNumberArray(value, property);
        if (size.length !== 2) {
            throw new Error(`Expected an array of two numbers for ${property}`);
        }
        return size;
    }
    function requireSizeLike(value, property) {
        if (typeof value === "number") {
            return value;
        }
        return requireSize(value, property);
    }
    const Property$1 = {
        RENDER_ORDER: "renderOrder"
    };
    class BaseVectorLayer extends Layer {
        constructor(options) {
            options = options ? options : {};
            const baseOptions = Object.assign({}, options);
            delete baseOptions.style;
            delete baseOptions.renderBuffer;
            delete baseOptions.updateWhileAnimating;
            delete baseOptions.updateWhileInteracting;
            super(baseOptions);
            this.declutter_ = options.declutter ? String(options.declutter) : undefined;
            this.renderBuffer_ = options.renderBuffer !== undefined ? options.renderBuffer : 100;
            this.style_ = null;
            this.styleFunction_ = undefined;
            this.setStyle(options.style);
            this.updateWhileAnimating_ = options.updateWhileAnimating !== undefined ? options.updateWhileAnimating : false;
            this.updateWhileInteracting_ = options.updateWhileInteracting !== undefined ? options.updateWhileInteracting : false;
        }
        getDeclutter() {
            return this.declutter_;
        }
        getFeatures(pixel) {
            return super.getFeatures(pixel);
        }
        getRenderBuffer() {
            return this.renderBuffer_;
        }
        getRenderOrder() {
            return this.get(Property$1.RENDER_ORDER);
        }
        getStyle() {
            return this.style_;
        }
        getStyleFunction() {
            return this.styleFunction_;
        }
        getUpdateWhileAnimating() {
            return this.updateWhileAnimating_;
        }
        getUpdateWhileInteracting() {
            return this.updateWhileInteracting_;
        }
        renderDeclutter(frameState, layerState) {
            const declutterGroup = this.getDeclutter();
            if (declutterGroup in frameState.declutter === false) {
                frameState.declutter[declutterGroup] = new RBush$1(9);
            }
            this.getRenderer().renderDeclutter(frameState, layerState);
        }
        setRenderOrder(renderOrder) {
            this.set(Property$1.RENDER_ORDER, renderOrder);
        }
        setStyle(style) {
            this.style_ = style === undefined ? createDefaultStyle : style;
            const styleLike = toStyleLike(style);
            this.styleFunction_ = style === null ? undefined : toFunction(styleLike);
            this.changed();
        }
    }
    function toStyleLike(style) {
        if (style === undefined) {
            return createDefaultStyle;
        }
        if (!style) {
            return null;
        }
        if (typeof style === "function") {
            return style;
        }
        if (style instanceof Style) {
            return style;
        }
        if (!Array.isArray(style)) {
            return flatStylesToStyleFunction([ style ]);
        }
        if (style.length === 0) {
            return [];
        }
        const length = style.length;
        const first = style[0];
        if (first instanceof Style) {
            const styles = new Array(length);
            for (let i = 0; i < length; ++i) {
                const candidate = style[i];
                if (!(candidate instanceof Style)) {
                    throw new Error("Expected a list of style instances");
                }
                styles[i] = candidate;
            }
            return styles;
        }
        if ("style" in first) {
            const rules = new Array(length);
            for (let i = 0; i < length; ++i) {
                const candidate = style[i];
                if (!("style" in candidate)) {
                    throw new Error("Expected a list of rules with a style property");
                }
                rules[i] = candidate;
            }
            return rulesToStyleFunction(rules);
        }
        const flatStyles = style;
        return flatStylesToStyleFunction(flatStyles);
    }
    class MapRenderer extends Disposable {
        constructor(map) {
            super();
            this.map_ = map;
        }
        dispatchRenderEvent(type, frameState) {
            abstract();
        }
        calculateMatrices2D(frameState) {
            const viewState = frameState.viewState;
            const coordinateToPixelTransform = frameState.coordinateToPixelTransform;
            const pixelToCoordinateTransform = frameState.pixelToCoordinateTransform;
            compose(coordinateToPixelTransform, frameState.size[0] / 2, frameState.size[1] / 2, 1 / viewState.resolution, -1 / viewState.resolution, -viewState.rotation, -viewState.center[0], -viewState.center[1]);
            makeInverse(pixelToCoordinateTransform, coordinateToPixelTransform);
        }
        forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, checkWrapped, callback, thisArg, layerFilter, thisArg2) {
            let result;
            const viewState = frameState.viewState;
            function forEachFeatureAtCoordinate(managed, feature, layer, geometry) {
                return callback.call(thisArg, feature, managed ? layer : null, geometry);
            }
            const projection = viewState.projection;
            const translatedCoordinate = wrapX$1(coordinate.slice(), projection);
            const offsets = [ [ 0, 0 ] ];
            if (projection.canWrapX() && checkWrapped) {
                const projectionExtent = projection.getExtent();
                const worldWidth = getWidth(projectionExtent);
                offsets.push([ -worldWidth, 0 ], [ worldWidth, 0 ]);
            }
            const layerStates = frameState.layerStatesArray;
            const numLayers = layerStates.length;
            const matches = [];
            const tmpCoord = [];
            for (let i = 0; i < offsets.length; i++) {
                for (let j = numLayers - 1; j >= 0; --j) {
                    const layerState = layerStates[j];
                    const layer = layerState.layer;
                    if (layer.hasRenderer() && inView(layerState, viewState) && layerFilter.call(thisArg2, layer)) {
                        const layerRenderer = layer.getRenderer();
                        const source = layer.getSource();
                        if (layerRenderer && source) {
                            const coordinates = source.getWrapX() ? translatedCoordinate : coordinate;
                            const callback = forEachFeatureAtCoordinate.bind(null, layerState.managed);
                            tmpCoord[0] = coordinates[0] + offsets[i][0];
                            tmpCoord[1] = coordinates[1] + offsets[i][1];
                            result = layerRenderer.forEachFeatureAtCoordinate(tmpCoord, frameState, hitTolerance, callback, matches);
                        }
                        if (result) {
                            return result;
                        }
                    }
                }
            }
            if (matches.length === 0) {
                return undefined;
            }
            const order = 1 / matches.length;
            matches.forEach((m, i) => m.distanceSq += i * order);
            matches.sort((a, b) => a.distanceSq - b.distanceSq);
            matches.some(m => result = m.callback(m.feature, m.layer, m.geometry));
            return result;
        }
        hasFeatureAtCoordinate(coordinate, frameState, hitTolerance, checkWrapped, layerFilter, thisArg) {
            const hasFeature = this.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, checkWrapped, TRUE, this, layerFilter, thisArg);
            return hasFeature !== undefined;
        }
        getMap() {
            return this.map_;
        }
        renderFrame(frameState) {
            abstract();
        }
        scheduleExpireIconCache(frameState) {
            if (shared.canExpireCache()) {
                frameState.postRenderFunctions.push(expireIconCache);
            }
        }
    }
    function expireIconCache(map, frameState) {
        shared.expire();
    }
    class RenderEvent extends BaseEvent {
        constructor(type, inversePixelTransform, frameState, context) {
            super(type);
            this.inversePixelTransform = inversePixelTransform;
            this.frameState = frameState;
            this.context = context;
        }
    }
    class CompositeMapRenderer extends MapRenderer {
        constructor(map) {
            super(map);
            this.fontChangeListenerKey_ = listen(checkedFonts, ObjectEventType.PROPERTYCHANGE, map.redrawText.bind(map));
            this.element_ = document.createElement("div");
            const style = this.element_.style;
            style.position = "absolute";
            style.width = "100%";
            style.height = "100%";
            style.zIndex = "0";
            this.element_.className = CLASS_UNSELECTABLE + " ol-layers";
            const container = map.getViewport();
            container.insertBefore(this.element_, container.firstChild || null);
            this.children_ = [];
            this.renderedVisible_ = true;
        }
        dispatchRenderEvent(type, frameState) {
            const map = this.getMap();
            if (map.hasListener(type)) {
                const event = new RenderEvent(type, undefined, frameState);
                map.dispatchEvent(event);
            }
        }
        disposeInternal() {
            unlistenByKey(this.fontChangeListenerKey_);
            this.element_.parentNode.removeChild(this.element_);
            super.disposeInternal();
        }
        renderFrame(frameState) {
            if (!frameState) {
                if (this.renderedVisible_) {
                    this.element_.style.display = "none";
                    this.renderedVisible_ = false;
                }
                return;
            }
            this.calculateMatrices2D(frameState);
            this.dispatchRenderEvent(RenderEventType.PRECOMPOSE, frameState);
            const layerStatesArray = frameState.layerStatesArray.sort(function(a, b) {
                return a.zIndex - b.zIndex;
            });
            const declutter = layerStatesArray.some(layerState => layerState.layer instanceof BaseVectorLayer && layerState.layer.getDeclutter());
            if (declutter) {
                frameState.declutter = {};
            }
            const viewState = frameState.viewState;
            this.children_.length = 0;
            const renderedLayerStates = [];
            let previousElement = null;
            for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
                const layerState = layerStatesArray[i];
                frameState.layerIndex = i;
                const layer = layerState.layer;
                const sourceState = layer.getSourceState();
                if (!inView(layerState, viewState) || sourceState != "ready" && sourceState != "undefined") {
                    layer.unrender();
                    continue;
                }
                const element = layer.render(frameState, previousElement);
                if (!element) {
                    continue;
                }
                if (element !== previousElement) {
                    this.children_.push(element);
                    previousElement = element;
                }
                renderedLayerStates.push(layerState);
            }
            this.declutter(frameState, renderedLayerStates);
            replaceChildren(this.element_, this.children_);
            this.dispatchRenderEvent(RenderEventType.POSTCOMPOSE, frameState);
            if (!this.renderedVisible_) {
                this.element_.style.display = "";
                this.renderedVisible_ = true;
            }
            this.scheduleExpireIconCache(frameState);
        }
        declutter(frameState, layerStates) {
            if (!frameState.declutter) {
                return;
            }
            for (let i = layerStates.length - 1; i >= 0; --i) {
                const layerState = layerStates[i];
                const layer = layerState.layer;
                if (layer.getDeclutter()) {
                    layer.renderDeclutter(frameState, layerState);
                }
            }
            layerStates.forEach(layerState => layerState.layer.renderDeferred(frameState));
        }
    }
    class GroupEvent extends BaseEvent {
        constructor(type, layer) {
            super(type);
            this.layer = layer;
        }
    }
    const Property = {
        LAYERS: "layers"
    };
    class LayerGroup extends BaseLayer {
        constructor(options) {
            options = options || {};
            const baseOptions = Object.assign({}, options);
            delete baseOptions.layers;
            let layers = options.layers;
            super(baseOptions);
            this.on;
            this.once;
            this.un;
            this.layersListenerKeys_ = [];
            this.listenerKeys_ = {};
            this.addChangeListener(Property.LAYERS, this.handleLayersChanged_);
            if (layers) {
                if (Array.isArray(layers)) {
                    layers = new Collection(layers.slice(), {
                        unique: true
                    });
                } else {
                    assert(typeof layers.getArray === "function", "Expected `layers` to be an array or a `Collection`");
                }
            } else {
                layers = new Collection(undefined, {
                    unique: true
                });
            }
            this.setLayers(layers);
        }
        handleLayerChange_() {
            this.changed();
        }
        handleLayersChanged_() {
            this.layersListenerKeys_.forEach(unlistenByKey);
            this.layersListenerKeys_.length = 0;
            const layers = this.getLayers();
            this.layersListenerKeys_.push(listen(layers, CollectionEventType.ADD, this.handleLayersAdd_, this), listen(layers, CollectionEventType.REMOVE, this.handleLayersRemove_, this));
            for (const id in this.listenerKeys_) {
                this.listenerKeys_[id].forEach(unlistenByKey);
            }
            clear(this.listenerKeys_);
            const layersArray = layers.getArray();
            for (let i = 0, ii = layersArray.length; i < ii; i++) {
                const layer = layersArray[i];
                this.registerLayerListeners_(layer);
                this.dispatchEvent(new GroupEvent("addlayer", layer));
            }
            this.changed();
        }
        registerLayerListeners_(layer) {
            const listenerKeys = [ listen(layer, ObjectEventType.PROPERTYCHANGE, this.handleLayerChange_, this), listen(layer, EventType.CHANGE, this.handleLayerChange_, this) ];
            if (layer instanceof LayerGroup) {
                listenerKeys.push(listen(layer, "addlayer", this.handleLayerGroupAdd_, this), listen(layer, "removelayer", this.handleLayerGroupRemove_, this));
            }
            this.listenerKeys_[getUid(layer)] = listenerKeys;
        }
        handleLayerGroupAdd_(event) {
            this.dispatchEvent(new GroupEvent("addlayer", event.layer));
        }
        handleLayerGroupRemove_(event) {
            this.dispatchEvent(new GroupEvent("removelayer", event.layer));
        }
        handleLayersAdd_(collectionEvent) {
            const layer = collectionEvent.element;
            this.registerLayerListeners_(layer);
            this.dispatchEvent(new GroupEvent("addlayer", layer));
            this.changed();
        }
        handleLayersRemove_(collectionEvent) {
            const layer = collectionEvent.element;
            const key = getUid(layer);
            this.listenerKeys_[key].forEach(unlistenByKey);
            delete this.listenerKeys_[key];
            this.dispatchEvent(new GroupEvent("removelayer", layer));
            this.changed();
        }
        getLayers() {
            return this.get(Property.LAYERS);
        }
        setLayers(layers) {
            const collection = this.getLayers();
            if (collection) {
                const currentLayers = collection.getArray();
                for (let i = 0, ii = currentLayers.length; i < ii; ++i) {
                    this.dispatchEvent(new GroupEvent("removelayer", currentLayers[i]));
                }
            }
            this.set(Property.LAYERS, layers);
        }
        getLayersArray(array) {
            array = array !== undefined ? array : [];
            this.getLayers().forEach(function(layer) {
                layer.getLayersArray(array);
            });
            return array;
        }
        getLayerStatesArray(dest) {
            const states = dest !== undefined ? dest : [];
            const pos = states.length;
            this.getLayers().forEach(function(layer) {
                layer.getLayerStatesArray(states);
            });
            const ownLayerState = this.getLayerState();
            let defaultZIndex = ownLayerState.zIndex;
            if (!dest && ownLayerState.zIndex === undefined) {
                defaultZIndex = 0;
            }
            for (let i = pos, ii = states.length; i < ii; i++) {
                const layerState = states[i];
                layerState.opacity *= ownLayerState.opacity;
                layerState.visible = layerState.visible && ownLayerState.visible;
                layerState.maxResolution = Math.min(layerState.maxResolution, ownLayerState.maxResolution);
                layerState.minResolution = Math.max(layerState.minResolution, ownLayerState.minResolution);
                layerState.minZoom = Math.max(layerState.minZoom, ownLayerState.minZoom);
                layerState.maxZoom = Math.min(layerState.maxZoom, ownLayerState.maxZoom);
                if (ownLayerState.extent !== undefined) {
                    if (layerState.extent !== undefined) {
                        layerState.extent = getIntersection(layerState.extent, ownLayerState.extent);
                    } else {
                        layerState.extent = ownLayerState.extent;
                    }
                }
                if (layerState.zIndex === undefined) {
                    layerState.zIndex = defaultZIndex;
                }
            }
            return states;
        }
        getSourceState() {
            return "ready";
        }
    }
    class MapEvent extends BaseEvent {
        constructor(type, map, frameState) {
            super(type);
            this.map = map;
            this.frameState = frameState !== undefined ? frameState : null;
        }
    }
    class MapBrowserEvent extends MapEvent {
        constructor(type, map, originalEvent, dragging, frameState, activePointers) {
            super(type, map, frameState);
            this.originalEvent = originalEvent;
            this.pixel_ = null;
            this.coordinate_ = null;
            this.dragging = dragging !== undefined ? dragging : false;
            this.activePointers = activePointers;
        }
        get pixel() {
            if (!this.pixel_) {
                this.pixel_ = this.map.getEventPixel(this.originalEvent);
            }
            return this.pixel_;
        }
        set pixel(pixel) {
            this.pixel_ = pixel;
        }
        get coordinate() {
            if (!this.coordinate_) {
                this.coordinate_ = this.map.getCoordinateFromPixel(this.pixel);
            }
            return this.coordinate_;
        }
        set coordinate(coordinate) {
            this.coordinate_ = coordinate;
        }
        preventDefault() {
            super.preventDefault();
            if ("preventDefault" in this.originalEvent) {
                this.originalEvent.preventDefault();
            }
        }
        stopPropagation() {
            super.stopPropagation();
            if ("stopPropagation" in this.originalEvent) {
                this.originalEvent.stopPropagation();
            }
        }
    }
    var MapBrowserEventType = {
        SINGLECLICK: "singleclick",
        CLICK: EventType.CLICK,
        DBLCLICK: EventType.DBLCLICK,
        POINTERDRAG: "pointerdrag",
        POINTERMOVE: "pointermove",
        POINTERDOWN: "pointerdown",
        POINTERUP: "pointerup",
        POINTEROVER: "pointerover",
        POINTEROUT: "pointerout",
        POINTERENTER: "pointerenter",
        POINTERLEAVE: "pointerleave",
        POINTERCANCEL: "pointercancel"
    };
    var PointerEventType = {
        POINTERMOVE: "pointermove",
        POINTERDOWN: "pointerdown"
    };
    class MapBrowserEventHandler extends Target {
        constructor(map, moveTolerance) {
            super(map);
            this.map_ = map;
            this.clickTimeoutId_;
            this.emulateClicks_ = false;
            this.dragging_ = false;
            this.dragListenerKeys_ = [];
            this.moveTolerance_ = moveTolerance === undefined ? 1 : moveTolerance;
            this.down_ = null;
            const element = this.map_.getViewport();
            this.activePointers_ = [];
            this.trackedTouches_ = {};
            this.element_ = element;
            this.pointerdownListenerKey_ = listen(element, PointerEventType.POINTERDOWN, this.handlePointerDown_, this);
            this.originalPointerMoveEvent_;
            this.relayedListenerKey_ = listen(element, PointerEventType.POINTERMOVE, this.relayMoveEvent_, this);
            this.boundHandleTouchMove_ = this.handleTouchMove_.bind(this);
            this.element_.addEventListener(EventType.TOUCHMOVE, this.boundHandleTouchMove_, PASSIVE_EVENT_LISTENERS ? {
                passive: false
            } : false);
        }
        emulateClick_(pointerEvent) {
            let newEvent = new MapBrowserEvent(MapBrowserEventType.CLICK, this.map_, pointerEvent);
            this.dispatchEvent(newEvent);
            if (this.clickTimeoutId_ !== undefined) {
                clearTimeout(this.clickTimeoutId_);
                this.clickTimeoutId_ = undefined;
                newEvent = new MapBrowserEvent(MapBrowserEventType.DBLCLICK, this.map_, pointerEvent);
                this.dispatchEvent(newEvent);
            } else {
                this.clickTimeoutId_ = setTimeout(() => {
                    this.clickTimeoutId_ = undefined;
                    const newEvent = new MapBrowserEvent(MapBrowserEventType.SINGLECLICK, this.map_, pointerEvent);
                    this.dispatchEvent(newEvent);
                }, 250);
            }
        }
        updateActivePointers_(pointerEvent) {
            const event = pointerEvent;
            const id = event.pointerId;
            if (event.type == MapBrowserEventType.POINTERUP || event.type == MapBrowserEventType.POINTERCANCEL) {
                delete this.trackedTouches_[id];
                for (const pointerId in this.trackedTouches_) {
                    if (this.trackedTouches_[pointerId].target !== event.target) {
                        delete this.trackedTouches_[pointerId];
                        break;
                    }
                }
            } else if (event.type == MapBrowserEventType.POINTERDOWN || event.type == MapBrowserEventType.POINTERMOVE) {
                this.trackedTouches_[id] = event;
            }
            this.activePointers_ = Object.values(this.trackedTouches_);
        }
        handlePointerUp_(pointerEvent) {
            this.updateActivePointers_(pointerEvent);
            const newEvent = new MapBrowserEvent(MapBrowserEventType.POINTERUP, this.map_, pointerEvent, undefined, undefined, this.activePointers_);
            this.dispatchEvent(newEvent);
            if (this.emulateClicks_ && !newEvent.defaultPrevented && !this.dragging_ && this.isMouseActionButton_(pointerEvent)) {
                this.emulateClick_(this.down_);
            }
            if (this.activePointers_.length === 0) {
                this.dragListenerKeys_.forEach(unlistenByKey);
                this.dragListenerKeys_.length = 0;
                this.dragging_ = false;
                this.down_ = null;
            }
        }
        isMouseActionButton_(pointerEvent) {
            return pointerEvent.button === 0;
        }
        handlePointerDown_(pointerEvent) {
            this.emulateClicks_ = this.activePointers_.length === 0;
            this.updateActivePointers_(pointerEvent);
            const newEvent = new MapBrowserEvent(MapBrowserEventType.POINTERDOWN, this.map_, pointerEvent, undefined, undefined, this.activePointers_);
            this.dispatchEvent(newEvent);
            this.down_ = new PointerEvent(pointerEvent.type, pointerEvent);
            Object.defineProperty(this.down_, "target", {
                writable: false,
                value: pointerEvent.target
            });
            if (this.dragListenerKeys_.length === 0) {
                const doc = this.map_.getOwnerDocument();
                this.dragListenerKeys_.push(listen(doc, MapBrowserEventType.POINTERMOVE, this.handlePointerMove_, this), listen(doc, MapBrowserEventType.POINTERUP, this.handlePointerUp_, this), listen(this.element_, MapBrowserEventType.POINTERCANCEL, this.handlePointerUp_, this));
                if (this.element_.getRootNode && this.element_.getRootNode() !== doc) {
                    this.dragListenerKeys_.push(listen(this.element_.getRootNode(), MapBrowserEventType.POINTERUP, this.handlePointerUp_, this));
                }
            }
        }
        handlePointerMove_(pointerEvent) {
            if (this.isMoving_(pointerEvent)) {
                this.updateActivePointers_(pointerEvent);
                this.dragging_ = true;
                const newEvent = new MapBrowserEvent(MapBrowserEventType.POINTERDRAG, this.map_, pointerEvent, this.dragging_, undefined, this.activePointers_);
                this.dispatchEvent(newEvent);
            }
        }
        relayMoveEvent_(pointerEvent) {
            this.originalPointerMoveEvent_ = pointerEvent;
            const dragging = !!(this.down_ && this.isMoving_(pointerEvent));
            this.dispatchEvent(new MapBrowserEvent(MapBrowserEventType.POINTERMOVE, this.map_, pointerEvent, dragging));
        }
        handleTouchMove_(event) {
            const originalEvent = this.originalPointerMoveEvent_;
            if ((!originalEvent || originalEvent.defaultPrevented) && (typeof event.cancelable !== "boolean" || event.cancelable === true)) {
                event.preventDefault();
            }
        }
        isMoving_(pointerEvent) {
            return this.dragging_ || Math.abs(pointerEvent.clientX - this.down_.clientX) > this.moveTolerance_ || Math.abs(pointerEvent.clientY - this.down_.clientY) > this.moveTolerance_;
        }
        disposeInternal() {
            if (this.relayedListenerKey_) {
                unlistenByKey(this.relayedListenerKey_);
                this.relayedListenerKey_ = null;
            }
            this.element_.removeEventListener(EventType.TOUCHMOVE, this.boundHandleTouchMove_);
            if (this.pointerdownListenerKey_) {
                unlistenByKey(this.pointerdownListenerKey_);
                this.pointerdownListenerKey_ = null;
            }
            this.dragListenerKeys_.forEach(unlistenByKey);
            this.dragListenerKeys_.length = 0;
            this.element_ = null;
            super.disposeInternal();
        }
    }
    var MapEventType = {
        POSTRENDER: "postrender",
        MOVESTART: "movestart",
        MOVEEND: "moveend",
        LOADSTART: "loadstart",
        LOADEND: "loadend"
    };
    var MapProperty = {
        LAYERGROUP: "layergroup",
        SIZE: "size",
        TARGET: "target",
        VIEW: "view"
    };
    const DROP = Infinity;
    class PriorityQueue {
        constructor(priorityFunction, keyFunction) {
            this.priorityFunction_ = priorityFunction;
            this.keyFunction_ = keyFunction;
            this.elements_ = [];
            this.priorities_ = [];
            this.queuedElements_ = {};
        }
        clear() {
            this.elements_.length = 0;
            this.priorities_.length = 0;
            clear(this.queuedElements_);
        }
        dequeue() {
            const elements = this.elements_;
            const priorities = this.priorities_;
            const element = elements[0];
            if (elements.length == 1) {
                elements.length = 0;
                priorities.length = 0;
            } else {
                elements[0] = elements.pop();
                priorities[0] = priorities.pop();
                this.siftUp_(0);
            }
            const elementKey = this.keyFunction_(element);
            delete this.queuedElements_[elementKey];
            return element;
        }
        enqueue(element) {
            assert(!(this.keyFunction_(element) in this.queuedElements_), "Tried to enqueue an `element` that was already added to the queue");
            const priority = this.priorityFunction_(element);
            if (priority != DROP) {
                this.elements_.push(element);
                this.priorities_.push(priority);
                this.queuedElements_[this.keyFunction_(element)] = true;
                this.siftDown_(0, this.elements_.length - 1);
                return true;
            }
            return false;
        }
        getCount() {
            return this.elements_.length;
        }
        getLeftChildIndex_(index) {
            return index * 2 + 1;
        }
        getRightChildIndex_(index) {
            return index * 2 + 2;
        }
        getParentIndex_(index) {
            return index - 1 >> 1;
        }
        heapify_() {
            let i;
            for (i = (this.elements_.length >> 1) - 1; i >= 0; i--) {
                this.siftUp_(i);
            }
        }
        isEmpty() {
            return this.elements_.length === 0;
        }
        isKeyQueued(key) {
            return key in this.queuedElements_;
        }
        isQueued(element) {
            return this.isKeyQueued(this.keyFunction_(element));
        }
        siftUp_(index) {
            const elements = this.elements_;
            const priorities = this.priorities_;
            const count = elements.length;
            const element = elements[index];
            const priority = priorities[index];
            const startIndex = index;
            while (index < count >> 1) {
                const lIndex = this.getLeftChildIndex_(index);
                const rIndex = this.getRightChildIndex_(index);
                const smallerChildIndex = rIndex < count && priorities[rIndex] < priorities[lIndex] ? rIndex : lIndex;
                elements[index] = elements[smallerChildIndex];
                priorities[index] = priorities[smallerChildIndex];
                index = smallerChildIndex;
            }
            elements[index] = element;
            priorities[index] = priority;
            this.siftDown_(startIndex, index);
        }
        siftDown_(startIndex, index) {
            const elements = this.elements_;
            const priorities = this.priorities_;
            const element = elements[index];
            const priority = priorities[index];
            while (index > startIndex) {
                const parentIndex = this.getParentIndex_(index);
                if (priorities[parentIndex] > priority) {
                    elements[index] = elements[parentIndex];
                    priorities[index] = priorities[parentIndex];
                    index = parentIndex;
                } else {
                    break;
                }
            }
            elements[index] = element;
            priorities[index] = priority;
        }
        reprioritize() {
            const priorityFunction = this.priorityFunction_;
            const elements = this.elements_;
            const priorities = this.priorities_;
            let index = 0;
            const n = elements.length;
            let element, i, priority;
            for (i = 0; i < n; ++i) {
                element = elements[i];
                priority = priorityFunction(element);
                if (priority == DROP) {
                    delete this.queuedElements_[this.keyFunction_(element)];
                } else {
                    priorities[index] = priority;
                    elements[index++] = element;
                }
            }
            elements.length = index;
            priorities.length = index;
            this.heapify_();
        }
    }
    var TileState = {
        IDLE: 0,
        LOADING: 1,
        LOADED: 2,
        ERROR: 3,
        EMPTY: 4
    };
    class TileQueue extends PriorityQueue {
        constructor(tilePriorityFunction, tileChangeCallback) {
            super(function(element) {
                return tilePriorityFunction.apply(null, element);
            }, function(element) {
                return element[0].getKey();
            });
            this.boundHandleTileChange_ = this.handleTileChange.bind(this);
            this.tileChangeCallback_ = tileChangeCallback;
            this.tilesLoading_ = 0;
            this.tilesLoadingKeys_ = {};
        }
        enqueue(element) {
            const added = super.enqueue(element);
            if (added) {
                const tile = element[0];
                tile.addEventListener(EventType.CHANGE, this.boundHandleTileChange_);
            }
            return added;
        }
        getTilesLoading() {
            return this.tilesLoading_;
        }
        handleTileChange(event) {
            const tile = event.target;
            const state = tile.getState();
            if (state === TileState.LOADED || state === TileState.ERROR || state === TileState.EMPTY) {
                if (state !== TileState.ERROR) {
                    tile.removeEventListener(EventType.CHANGE, this.boundHandleTileChange_);
                }
                const tileKey = tile.getKey();
                if (tileKey in this.tilesLoadingKeys_) {
                    delete this.tilesLoadingKeys_[tileKey];
                    --this.tilesLoading_;
                }
                this.tileChangeCallback_();
            }
        }
        loadMoreTiles(maxTotalLoading, maxNewLoads) {
            let newLoads = 0;
            let state, tile, tileKey;
            while (this.tilesLoading_ < maxTotalLoading && newLoads < maxNewLoads && this.getCount() > 0) {
                tile = this.dequeue()[0];
                tileKey = tile.getKey();
                state = tile.getState();
                if (state === TileState.IDLE && !(tileKey in this.tilesLoadingKeys_)) {
                    this.tilesLoadingKeys_[tileKey] = true;
                    ++this.tilesLoading_;
                    ++newLoads;
                    tile.load();
                }
            }
        }
    }
    function getTilePriority(frameState, tile, tileSourceKey, tileCenter, tileResolution) {
        if (!frameState || !(tileSourceKey in frameState.wantedTiles)) {
            return DROP;
        }
        if (!frameState.wantedTiles[tileSourceKey][tile.getKey()]) {
            return DROP;
        }
        const center = frameState.viewState.center;
        const deltaX = tileCenter[0] - center[0];
        const deltaY = tileCenter[1] - center[1];
        return 65536 * Math.log(tileResolution) + Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution;
    }
    class Control extends BaseObject {
        constructor(options) {
            super();
            const element = options.element;
            if (element && !options.target && !element.style.pointerEvents) {
                element.style.pointerEvents = "auto";
            }
            this.element = element ? element : null;
            this.target_ = null;
            this.map_ = null;
            this.listenerKeys = [];
            if (options.render) {
                this.render = options.render;
            }
            if (options.target) {
                this.setTarget(options.target);
            }
        }
        disposeInternal() {
            removeNode(this.element);
            super.disposeInternal();
        }
        getMap() {
            return this.map_;
        }
        setMap(map) {
            if (this.map_) {
                removeNode(this.element);
            }
            for (let i = 0, ii = this.listenerKeys.length; i < ii; ++i) {
                unlistenByKey(this.listenerKeys[i]);
            }
            this.listenerKeys.length = 0;
            this.map_ = map;
            if (map) {
                const target = this.target_ ? this.target_ : map.getOverlayContainerStopEvent();
                target.appendChild(this.element);
                if (this.render !== VOID) {
                    this.listenerKeys.push(listen(map, MapEventType.POSTRENDER, this.render, this));
                }
                map.render();
            }
        }
        render(mapEvent) {}
        setTarget(target) {
            this.target_ = typeof target === "string" ? document.getElementById(target) : target;
        }
    }
    class Attribution extends Control {
        constructor(options) {
            options = options ? options : {};
            super({
                element: document.createElement("div"),
                render: options.render,
                target: options.target
            });
            this.ulElement_ = document.createElement("ul");
            this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true;
            this.userCollapsed_ = this.collapsed_;
            this.overrideCollapsible_ = options.collapsible !== undefined;
            this.collapsible_ = options.collapsible !== undefined ? options.collapsible : true;
            if (!this.collapsible_) {
                this.collapsed_ = false;
            }
            const className = options.className !== undefined ? options.className : "ol-attribution";
            const tipLabel = options.tipLabel !== undefined ? options.tipLabel : "Attributions";
            const expandClassName = options.expandClassName !== undefined ? options.expandClassName : className + "-expand";
            const collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : "›";
            const collapseClassName = options.collapseClassName !== undefined ? options.collapseClassName : className + "-collapse";
            if (typeof collapseLabel === "string") {
                this.collapseLabel_ = document.createElement("span");
                this.collapseLabel_.textContent = collapseLabel;
                this.collapseLabel_.className = collapseClassName;
            } else {
                this.collapseLabel_ = collapseLabel;
            }
            const label = options.label !== undefined ? options.label : "i";
            if (typeof label === "string") {
                this.label_ = document.createElement("span");
                this.label_.textContent = label;
                this.label_.className = expandClassName;
            } else {
                this.label_ = label;
            }
            const activeLabel = this.collapsible_ && !this.collapsed_ ? this.collapseLabel_ : this.label_;
            this.toggleButton_ = document.createElement("button");
            this.toggleButton_.setAttribute("type", "button");
            this.toggleButton_.setAttribute("aria-expanded", String(!this.collapsed_));
            this.toggleButton_.title = tipLabel;
            this.toggleButton_.appendChild(activeLabel);
            this.toggleButton_.addEventListener(EventType.CLICK, this.handleClick_.bind(this), false);
            const cssClasses = className + " " + CLASS_UNSELECTABLE + " " + CLASS_CONTROL + (this.collapsed_ && this.collapsible_ ? " " + CLASS_COLLAPSED : "") + (this.collapsible_ ? "" : " ol-uncollapsible");
            const element = this.element;
            element.className = cssClasses;
            element.appendChild(this.toggleButton_);
            element.appendChild(this.ulElement_);
            this.renderedAttributions_ = [];
            this.renderedVisible_ = true;
        }
        collectSourceAttributions_(frameState) {
            const visibleAttributions = Array.from(new Set(this.getMap().getAllLayers().flatMap(layer => layer.getAttributions(frameState))));
            const collapsible = !this.getMap().getAllLayers().some(layer => layer.getSource() && layer.getSource().getAttributionsCollapsible() === false);
            if (!this.overrideCollapsible_) {
                this.setCollapsible(collapsible);
            }
            return visibleAttributions;
        }
        async updateElement_(frameState) {
            if (!frameState) {
                if (this.renderedVisible_) {
                    this.element.style.display = "none";
                    this.renderedVisible_ = false;
                }
                return;
            }
            const attributions = await Promise.all(this.collectSourceAttributions_(frameState).map(attribution => toPromise(() => attribution)));
            const visible = attributions.length > 0;
            if (this.renderedVisible_ != visible) {
                this.element.style.display = visible ? "" : "none";
                this.renderedVisible_ = visible;
            }
            if (equals$2(attributions, this.renderedAttributions_)) {
                return;
            }
            removeChildren(this.ulElement_);
            for (let i = 0, ii = attributions.length; i < ii; ++i) {
                const element = document.createElement("li");
                element.innerHTML = attributions[i];
                this.ulElement_.appendChild(element);
            }
            this.renderedAttributions_ = attributions;
        }
        handleClick_(event) {
            event.preventDefault();
            this.handleToggle_();
            this.userCollapsed_ = this.collapsed_;
        }
        handleToggle_() {
            this.element.classList.toggle(CLASS_COLLAPSED);
            if (this.collapsed_) {
                replaceNode(this.collapseLabel_, this.label_);
            } else {
                replaceNode(this.label_, this.collapseLabel_);
            }
            this.collapsed_ = !this.collapsed_;
            this.toggleButton_.setAttribute("aria-expanded", String(!this.collapsed_));
        }
        getCollapsible() {
            return this.collapsible_;
        }
        setCollapsible(collapsible) {
            if (this.collapsible_ === collapsible) {
                return;
            }
            this.collapsible_ = collapsible;
            this.element.classList.toggle("ol-uncollapsible");
            if (this.userCollapsed_) {
                this.handleToggle_();
            }
        }
        setCollapsed(collapsed) {
            this.userCollapsed_ = collapsed;
            if (!this.collapsible_ || this.collapsed_ === collapsed) {
                return;
            }
            this.handleToggle_();
        }
        getCollapsed() {
            return this.collapsed_;
        }
        render(mapEvent) {
            this.updateElement_(mapEvent.frameState);
        }
    }
    class Rotate extends Control {
        constructor(options) {
            options = options ? options : {};
            super({
                element: document.createElement("div"),
                render: options.render,
                target: options.target
            });
            const className = options.className !== undefined ? options.className : "ol-rotate";
            const label = options.label !== undefined ? options.label : "⇧";
            const compassClassName = options.compassClassName !== undefined ? options.compassClassName : "ol-compass";
            this.label_ = null;
            if (typeof label === "string") {
                this.label_ = document.createElement("span");
                this.label_.className = compassClassName;
                this.label_.textContent = label;
            } else {
                this.label_ = label;
                this.label_.classList.add(compassClassName);
            }
            const tipLabel = options.tipLabel ? options.tipLabel : "Reset rotation";
            const button = document.createElement("button");
            button.className = className + "-reset";
            button.setAttribute("type", "button");
            button.title = tipLabel;
            button.appendChild(this.label_);
            button.addEventListener(EventType.CLICK, this.handleClick_.bind(this), false);
            const cssClasses = className + " " + CLASS_UNSELECTABLE + " " + CLASS_CONTROL;
            const element = this.element;
            element.className = cssClasses;
            element.appendChild(button);
            this.callResetNorth_ = options.resetNorth ? options.resetNorth : undefined;
            this.duration_ = options.duration !== undefined ? options.duration : 250;
            this.autoHide_ = options.autoHide !== undefined ? options.autoHide : true;
            this.rotation_ = undefined;
            if (this.autoHide_) {
                this.element.classList.add(CLASS_HIDDEN);
            }
        }
        handleClick_(event) {
            event.preventDefault();
            if (this.callResetNorth_ !== undefined) {
                this.callResetNorth_();
            } else {
                this.resetNorth_();
            }
        }
        resetNorth_() {
            const map = this.getMap();
            const view = map.getView();
            if (!view) {
                return;
            }
            const rotation = view.getRotation();
            if (rotation !== undefined) {
                if (this.duration_ > 0 && rotation % (2 * Math.PI) !== 0) {
                    view.animate({
                        rotation: 0,
                        duration: this.duration_,
                        easing: easeOut
                    });
                } else {
                    view.setRotation(0);
                }
            }
        }
        render(mapEvent) {
            const frameState = mapEvent.frameState;
            if (!frameState) {
                return;
            }
            const rotation = frameState.viewState.rotation;
            if (rotation != this.rotation_) {
                const transform = "rotate(" + rotation + "rad)";
                if (this.autoHide_) {
                    const contains = this.element.classList.contains(CLASS_HIDDEN);
                    if (!contains && rotation === 0) {
                        this.element.classList.add(CLASS_HIDDEN);
                    } else if (contains && rotation !== 0) {
                        this.element.classList.remove(CLASS_HIDDEN);
                    }
                }
                this.label_.style.transform = transform;
            }
            this.rotation_ = rotation;
        }
    }
    class Zoom extends Control {
        constructor(options) {
            options = options ? options : {};
            super({
                element: document.createElement("div"),
                target: options.target
            });
            const className = options.className !== undefined ? options.className : "ol-zoom";
            const delta = options.delta !== undefined ? options.delta : 1;
            const zoomInClassName = options.zoomInClassName !== undefined ? options.zoomInClassName : className + "-in";
            const zoomOutClassName = options.zoomOutClassName !== undefined ? options.zoomOutClassName : className + "-out";
            const zoomInLabel = options.zoomInLabel !== undefined ? options.zoomInLabel : "+";
            const zoomOutLabel = options.zoomOutLabel !== undefined ? options.zoomOutLabel : "–";
            const zoomInTipLabel = options.zoomInTipLabel !== undefined ? options.zoomInTipLabel : "Zoom in";
            const zoomOutTipLabel = options.zoomOutTipLabel !== undefined ? options.zoomOutTipLabel : "Zoom out";
            const inElement = document.createElement("button");
            inElement.className = zoomInClassName;
            inElement.setAttribute("type", "button");
            inElement.title = zoomInTipLabel;
            inElement.appendChild(typeof zoomInLabel === "string" ? document.createTextNode(zoomInLabel) : zoomInLabel);
            inElement.addEventListener(EventType.CLICK, this.handleClick_.bind(this, delta), false);
            const outElement = document.createElement("button");
            outElement.className = zoomOutClassName;
            outElement.setAttribute("type", "button");
            outElement.title = zoomOutTipLabel;
            outElement.appendChild(typeof zoomOutLabel === "string" ? document.createTextNode(zoomOutLabel) : zoomOutLabel);
            outElement.addEventListener(EventType.CLICK, this.handleClick_.bind(this, -delta), false);
            const cssClasses = className + " " + CLASS_UNSELECTABLE + " " + CLASS_CONTROL;
            const element = this.element;
            element.className = cssClasses;
            element.appendChild(inElement);
            element.appendChild(outElement);
            this.duration_ = options.duration !== undefined ? options.duration : 250;
        }
        handleClick_(delta, event) {
            event.preventDefault();
            this.zoomByDelta_(delta);
        }
        zoomByDelta_(delta) {
            const map = this.getMap();
            const view = map.getView();
            if (!view) {
                return;
            }
            const currentZoom = view.getZoom();
            if (currentZoom !== undefined) {
                const newZoom = view.getConstrainedZoom(currentZoom + delta);
                if (this.duration_ > 0) {
                    if (view.getAnimating()) {
                        view.cancelAnimations();
                    }
                    view.animate({
                        zoom: newZoom,
                        duration: this.duration_,
                        easing: easeOut
                    });
                } else {
                    view.setZoom(newZoom);
                }
            }
        }
    }
    function defaults$1(options) {
        options = options ? options : {};
        const controls = new Collection;
        const zoomControl = options.zoom !== undefined ? options.zoom : true;
        if (zoomControl) {
            controls.push(new Zoom(options.zoomOptions));
        }
        const rotateControl = options.rotate !== undefined ? options.rotate : true;
        if (rotateControl) {
            controls.push(new Rotate(options.rotateOptions));
        }
        const attributionControl = options.attribution !== undefined ? options.attribution : true;
        if (attributionControl) {
            controls.push(new Attribution(options.attributionOptions));
        }
        return controls;
    }
    var InteractionProperty = {
        ACTIVE: "active"
    };
    class Interaction extends BaseObject {
        constructor(options) {
            super();
            this.on;
            this.once;
            this.un;
            if (options && options.handleEvent) {
                this.handleEvent = options.handleEvent;
            }
            this.map_ = null;
            this.setActive(true);
        }
        getActive() {
            return this.get(InteractionProperty.ACTIVE);
        }
        getMap() {
            return this.map_;
        }
        handleEvent(mapBrowserEvent) {
            return true;
        }
        setActive(active) {
            this.set(InteractionProperty.ACTIVE, active);
        }
        setMap(map) {
            this.map_ = map;
        }
    }
    function pan(view, delta, duration) {
        const currentCenter = view.getCenterInternal();
        if (currentCenter) {
            const center = [ currentCenter[0] + delta[0], currentCenter[1] + delta[1] ];
            view.animateInternal({
                duration: duration !== undefined ? duration : 250,
                easing: linear,
                center: view.getConstrainedCenter(center)
            });
        }
    }
    function zoomByDelta(view, delta, anchor, duration) {
        const currentZoom = view.getZoom();
        if (currentZoom === undefined) {
            return;
        }
        const newZoom = view.getConstrainedZoom(currentZoom + delta);
        const newResolution = view.getResolutionForZoom(newZoom);
        if (view.getAnimating()) {
            view.cancelAnimations();
        }
        view.animate({
            resolution: newResolution,
            anchor: anchor,
            duration: duration !== undefined ? duration : 250,
            easing: easeOut
        });
    }
    class DoubleClickZoom extends Interaction {
        constructor(options) {
            super();
            options = options ? options : {};
            this.delta_ = options.delta ? options.delta : 1;
            this.duration_ = options.duration !== undefined ? options.duration : 250;
        }
        handleEvent(mapBrowserEvent) {
            let stopEvent = false;
            if (mapBrowserEvent.type == MapBrowserEventType.DBLCLICK) {
                const browserEvent = mapBrowserEvent.originalEvent;
                const map = mapBrowserEvent.map;
                const anchor = mapBrowserEvent.coordinate;
                const delta = browserEvent.shiftKey ? -this.delta_ : this.delta_;
                const view = map.getView();
                zoomByDelta(view, delta, anchor, this.duration_);
                browserEvent.preventDefault();
                stopEvent = true;
            }
            return !stopEvent;
        }
    }
    class PointerInteraction extends Interaction {
        constructor(options) {
            options = options ? options : {};
            super(options);
            if (options.handleDownEvent) {
                this.handleDownEvent = options.handleDownEvent;
            }
            if (options.handleDragEvent) {
                this.handleDragEvent = options.handleDragEvent;
            }
            if (options.handleMoveEvent) {
                this.handleMoveEvent = options.handleMoveEvent;
            }
            if (options.handleUpEvent) {
                this.handleUpEvent = options.handleUpEvent;
            }
            if (options.stopDown) {
                this.stopDown = options.stopDown;
            }
            this.handlingDownUpSequence = false;
            this.targetPointers = [];
        }
        getPointerCount() {
            return this.targetPointers.length;
        }
        handleDownEvent(mapBrowserEvent) {
            return false;
        }
        handleDragEvent(mapBrowserEvent) {}
        handleEvent(mapBrowserEvent) {
            if (!mapBrowserEvent.originalEvent) {
                return true;
            }
            let stopEvent = false;
            this.updateTrackedPointers_(mapBrowserEvent);
            if (this.handlingDownUpSequence) {
                if (mapBrowserEvent.type == MapBrowserEventType.POINTERDRAG) {
                    this.handleDragEvent(mapBrowserEvent);
                    mapBrowserEvent.originalEvent.preventDefault();
                } else if (mapBrowserEvent.type == MapBrowserEventType.POINTERUP) {
                    const handledUp = this.handleUpEvent(mapBrowserEvent);
                    this.handlingDownUpSequence = handledUp && this.targetPointers.length > 0;
                }
            } else {
                if (mapBrowserEvent.type == MapBrowserEventType.POINTERDOWN) {
                    const handled = this.handleDownEvent(mapBrowserEvent);
                    this.handlingDownUpSequence = handled;
                    stopEvent = this.stopDown(handled);
                } else if (mapBrowserEvent.type == MapBrowserEventType.POINTERMOVE) {
                    this.handleMoveEvent(mapBrowserEvent);
                }
            }
            return !stopEvent;
        }
        handleMoveEvent(mapBrowserEvent) {}
        handleUpEvent(mapBrowserEvent) {
            return false;
        }
        stopDown(handled) {
            return handled;
        }
        updateTrackedPointers_(mapBrowserEvent) {
            if (mapBrowserEvent.activePointers) {
                this.targetPointers = mapBrowserEvent.activePointers;
            }
        }
    }
    function centroid(pointerEvents) {
        const length = pointerEvents.length;
        let clientX = 0;
        let clientY = 0;
        for (let i = 0; i < length; i++) {
            clientX += pointerEvents[i].clientX;
            clientY += pointerEvents[i].clientY;
        }
        return {
            clientX: clientX / length,
            clientY: clientY / length
        };
    }
    function all$1(var_args) {
        const conditions = arguments;
        return function(event) {
            let pass = true;
            for (let i = 0, ii = conditions.length; i < ii; ++i) {
                pass = pass && conditions[i](event);
                if (!pass) {
                    break;
                }
            }
            return pass;
        };
    }
    const altShiftKeysOnly = function(mapBrowserEvent) {
        const originalEvent = mapBrowserEvent.originalEvent;
        return originalEvent.altKey && !(originalEvent.metaKey || originalEvent.ctrlKey) && originalEvent.shiftKey;
    };
    const focus = function(event) {
        const targetElement = event.map.getTargetElement();
        const activeElement = event.map.getOwnerDocument().activeElement;
        return targetElement.contains(activeElement);
    };
    const focusWithTabindex = function(event) {
        return event.map.getTargetElement().hasAttribute("tabindex") ? focus(event) : true;
    };
    const always = TRUE;
    const mouseActionButton = function(mapBrowserEvent) {
        const originalEvent = mapBrowserEvent.originalEvent;
        return originalEvent.button == 0 && !(WEBKIT && MAC && originalEvent.ctrlKey);
    };
    const noModifierKeys = function(mapBrowserEvent) {
        const originalEvent = mapBrowserEvent.originalEvent;
        return !originalEvent.altKey && !(originalEvent.metaKey || originalEvent.ctrlKey) && !originalEvent.shiftKey;
    };
    const platformModifierKey = function(mapBrowserEvent) {
        const originalEvent = mapBrowserEvent.originalEvent;
        return MAC ? originalEvent.metaKey : originalEvent.ctrlKey;
    };
    const shiftKeyOnly = function(mapBrowserEvent) {
        const originalEvent = mapBrowserEvent.originalEvent;
        return !originalEvent.altKey && !(originalEvent.metaKey || originalEvent.ctrlKey) && originalEvent.shiftKey;
    };
    const targetNotEditable = function(mapBrowserEvent) {
        const originalEvent = mapBrowserEvent.originalEvent;
        const tagName = originalEvent.target.tagName;
        return tagName !== "INPUT" && tagName !== "SELECT" && tagName !== "TEXTAREA" && !originalEvent.target.isContentEditable;
    };
    const mouseOnly = function(mapBrowserEvent) {
        const pointerEvent = mapBrowserEvent.originalEvent;
        assert(pointerEvent !== undefined, "mapBrowserEvent must originate from a pointer event");
        return pointerEvent.pointerType == "mouse";
    };
    const primaryAction = function(mapBrowserEvent) {
        const pointerEvent = mapBrowserEvent.originalEvent;
        assert(pointerEvent !== undefined, "mapBrowserEvent must originate from a pointer event");
        return pointerEvent.isPrimary && pointerEvent.button === 0;
    };
    class DragPan extends PointerInteraction {
        constructor(options) {
            super({
                stopDown: FALSE
            });
            options = options ? options : {};
            this.kinetic_ = options.kinetic;
            this.lastCentroid = null;
            this.lastPointersCount_;
            this.panning_ = false;
            const condition = options.condition ? options.condition : all$1(noModifierKeys, primaryAction);
            this.condition_ = options.onFocusOnly ? all$1(focusWithTabindex, condition) : condition;
            this.noKinetic_ = false;
        }
        handleDragEvent(mapBrowserEvent) {
            const map = mapBrowserEvent.map;
            if (!this.panning_) {
                this.panning_ = true;
                map.getView().beginInteraction();
            }
            const targetPointers = this.targetPointers;
            const centroid$1 = map.getEventPixel(centroid(targetPointers));
            if (targetPointers.length == this.lastPointersCount_) {
                if (this.kinetic_) {
                    this.kinetic_.update(centroid$1[0], centroid$1[1]);
                }
                if (this.lastCentroid) {
                    const delta = [ this.lastCentroid[0] - centroid$1[0], centroid$1[1] - this.lastCentroid[1] ];
                    const map = mapBrowserEvent.map;
                    const view = map.getView();
                    scale$2(delta, view.getResolution());
                    rotate$1(delta, view.getRotation());
                    view.adjustCenterInternal(delta);
                }
            } else if (this.kinetic_) {
                this.kinetic_.begin();
            }
            this.lastCentroid = centroid$1;
            this.lastPointersCount_ = targetPointers.length;
            mapBrowserEvent.originalEvent.preventDefault();
        }
        handleUpEvent(mapBrowserEvent) {
            const map = mapBrowserEvent.map;
            const view = map.getView();
            if (this.targetPointers.length === 0) {
                if (!this.noKinetic_ && this.kinetic_ && this.kinetic_.end()) {
                    const distance = this.kinetic_.getDistance();
                    const angle = this.kinetic_.getAngle();
                    const center = view.getCenterInternal();
                    const centerpx = map.getPixelFromCoordinateInternal(center);
                    const dest = map.getCoordinateFromPixelInternal([ centerpx[0] - distance * Math.cos(angle), centerpx[1] - distance * Math.sin(angle) ]);
                    view.animateInternal({
                        center: view.getConstrainedCenter(dest),
                        duration: 500,
                        easing: easeOut
                    });
                }
                if (this.panning_) {
                    this.panning_ = false;
                    view.endInteraction();
                }
                return false;
            }
            if (this.kinetic_) {
                this.kinetic_.begin();
            }
            this.lastCentroid = null;
            return true;
        }
        handleDownEvent(mapBrowserEvent) {
            if (this.targetPointers.length > 0 && this.condition_(mapBrowserEvent)) {
                const map = mapBrowserEvent.map;
                const view = map.getView();
                this.lastCentroid = null;
                if (view.getAnimating()) {
                    view.cancelAnimations();
                }
                if (this.kinetic_) {
                    this.kinetic_.begin();
                }
                this.noKinetic_ = this.targetPointers.length > 1;
                return true;
            }
            return false;
        }
    }
    class DragRotate extends PointerInteraction {
        constructor(options) {
            options = options ? options : {};
            super({
                stopDown: FALSE
            });
            this.condition_ = options.condition ? options.condition : altShiftKeysOnly;
            this.lastAngle_ = undefined;
            this.duration_ = options.duration !== undefined ? options.duration : 250;
        }
        handleDragEvent(mapBrowserEvent) {
            if (!mouseOnly(mapBrowserEvent)) {
                return;
            }
            const map = mapBrowserEvent.map;
            const view = map.getView();
            if (view.getConstraints().rotation === disable) {
                return;
            }
            const size = map.getSize();
            const offset = mapBrowserEvent.pixel;
            const theta = Math.atan2(size[1] / 2 - offset[1], offset[0] - size[0] / 2);
            if (this.lastAngle_ !== undefined) {
                const delta = theta - this.lastAngle_;
                view.adjustRotationInternal(-delta);
            }
            this.lastAngle_ = theta;
        }
        handleUpEvent(mapBrowserEvent) {
            if (!mouseOnly(mapBrowserEvent)) {
                return true;
            }
            const map = mapBrowserEvent.map;
            const view = map.getView();
            view.endInteraction(this.duration_);
            return false;
        }
        handleDownEvent(mapBrowserEvent) {
            if (!mouseOnly(mapBrowserEvent)) {
                return false;
            }
            if (mouseActionButton(mapBrowserEvent) && this.condition_(mapBrowserEvent)) {
                const map = mapBrowserEvent.map;
                map.getView().beginInteraction();
                this.lastAngle_ = undefined;
                return true;
            }
            return false;
        }
    }
    class RenderBox extends Disposable {
        constructor(className) {
            super();
            this.geometry_ = null;
            this.element_ = document.createElement("div");
            this.element_.style.position = "absolute";
            this.element_.style.pointerEvents = "auto";
            this.element_.className = "ol-box " + className;
            this.map_ = null;
            this.startPixel_ = null;
            this.endPixel_ = null;
        }
        disposeInternal() {
            this.setMap(null);
        }
        render_() {
            const startPixel = this.startPixel_;
            const endPixel = this.endPixel_;
            const px = "px";
            const style = this.element_.style;
            style.left = Math.min(startPixel[0], endPixel[0]) + px;
            style.top = Math.min(startPixel[1], endPixel[1]) + px;
            style.width = Math.abs(endPixel[0] - startPixel[0]) + px;
            style.height = Math.abs(endPixel[1] - startPixel[1]) + px;
        }
        setMap(map) {
            if (this.map_) {
                this.map_.getOverlayContainer().removeChild(this.element_);
                const style = this.element_.style;
                style.left = "inherit";
                style.top = "inherit";
                style.width = "inherit";
                style.height = "inherit";
            }
            this.map_ = map;
            if (this.map_) {
                this.map_.getOverlayContainer().appendChild(this.element_);
            }
        }
        setPixels(startPixel, endPixel) {
            this.startPixel_ = startPixel;
            this.endPixel_ = endPixel;
            this.createOrUpdateGeometry();
            this.render_();
        }
        createOrUpdateGeometry() {
            if (!this.map_) {
                return;
            }
            const startPixel = this.startPixel_;
            const endPixel = this.endPixel_;
            const pixels = [ startPixel, [ startPixel[0], endPixel[1] ], endPixel, [ endPixel[0], startPixel[1] ] ];
            const coordinates = pixels.map(this.map_.getCoordinateFromPixelInternal, this.map_);
            coordinates[4] = coordinates[0].slice();
            if (!this.geometry_) {
                this.geometry_ = new Polygon([ coordinates ]);
            } else {
                this.geometry_.setCoordinates([ coordinates ]);
            }
        }
        getGeometry() {
            return this.geometry_;
        }
    }
    const DragBoxEventType = {
        BOXSTART: "boxstart",
        BOXDRAG: "boxdrag",
        BOXEND: "boxend",
        BOXCANCEL: "boxcancel"
    };
    class DragBoxEvent extends BaseEvent {
        constructor(type, coordinate, mapBrowserEvent) {
            super(type);
            this.coordinate = coordinate;
            this.mapBrowserEvent = mapBrowserEvent;
        }
    }
    class DragBox extends PointerInteraction {
        constructor(options) {
            super();
            this.on;
            this.once;
            this.un;
            options = options ? options : {};
            this.box_ = new RenderBox(options.className || "ol-dragbox");
            this.minArea_ = options.minArea !== undefined ? options.minArea : 64;
            if (options.onBoxEnd) {
                this.onBoxEnd = options.onBoxEnd;
            }
            this.startPixel_ = null;
            this.condition_ = options.condition ? options.condition : mouseActionButton;
            this.boxEndCondition_ = options.boxEndCondition ? options.boxEndCondition : this.defaultBoxEndCondition;
        }
        defaultBoxEndCondition(mapBrowserEvent, startPixel, endPixel) {
            const width = endPixel[0] - startPixel[0];
            const height = endPixel[1] - startPixel[1];
            return width * width + height * height >= this.minArea_;
        }
        getGeometry() {
            return this.box_.getGeometry();
        }
        handleDragEvent(mapBrowserEvent) {
            if (!this.startPixel_) {
                return;
            }
            this.box_.setPixels(this.startPixel_, mapBrowserEvent.pixel);
            this.dispatchEvent(new DragBoxEvent(DragBoxEventType.BOXDRAG, mapBrowserEvent.coordinate, mapBrowserEvent));
        }
        handleUpEvent(mapBrowserEvent) {
            if (!this.startPixel_) {
                return false;
            }
            this.box_.setMap(null);
            const completeBox = this.boxEndCondition_(mapBrowserEvent, this.startPixel_, mapBrowserEvent.pixel);
            if (completeBox) {
                this.onBoxEnd(mapBrowserEvent);
            }
            this.dispatchEvent(new DragBoxEvent(completeBox ? DragBoxEventType.BOXEND : DragBoxEventType.BOXCANCEL, mapBrowserEvent.coordinate, mapBrowserEvent));
            return false;
        }
        handleDownEvent(mapBrowserEvent) {
            if (this.condition_(mapBrowserEvent)) {
                this.startPixel_ = mapBrowserEvent.pixel;
                this.box_.setMap(mapBrowserEvent.map);
                this.box_.setPixels(this.startPixel_, this.startPixel_);
                this.dispatchEvent(new DragBoxEvent(DragBoxEventType.BOXSTART, mapBrowserEvent.coordinate, mapBrowserEvent));
                return true;
            }
            return false;
        }
        onBoxEnd(event) {}
        setActive(active) {
            if (!active) {
                this.box_.setMap(null);
                if (this.startPixel_) {
                    this.dispatchEvent(new DragBoxEvent(DragBoxEventType.BOXCANCEL, this.startPixel_, null));
                    this.startPixel_ = null;
                }
            }
            super.setActive(active);
        }
    }
    class DragZoom extends DragBox {
        constructor(options) {
            options = options ? options : {};
            const condition = options.condition ? options.condition : shiftKeyOnly;
            super({
                condition: condition,
                className: options.className || "ol-dragzoom",
                minArea: options.minArea
            });
            this.duration_ = options.duration !== undefined ? options.duration : 200;
            this.out_ = options.out !== undefined ? options.out : false;
        }
        onBoxEnd(event) {
            const map = this.getMap();
            const view = map.getView();
            let geometry = this.getGeometry();
            if (this.out_) {
                const rotatedExtent = view.rotatedExtentForGeometry(geometry);
                const resolution = view.getResolutionForExtentInternal(rotatedExtent);
                const factor = view.getResolution() / resolution;
                geometry = geometry.clone();
                geometry.scale(factor * factor);
            }
            view.fitInternal(geometry, {
                duration: this.duration_,
                easing: easeOut
            });
        }
    }
    var Key = {
        LEFT: "ArrowLeft",
        UP: "ArrowUp",
        RIGHT: "ArrowRight",
        DOWN: "ArrowDown"
    };
    class KeyboardPan extends Interaction {
        constructor(options) {
            super();
            options = options || {};
            this.defaultCondition_ = function(mapBrowserEvent) {
                return noModifierKeys(mapBrowserEvent) && targetNotEditable(mapBrowserEvent);
            };
            this.condition_ = options.condition !== undefined ? options.condition : this.defaultCondition_;
            this.duration_ = options.duration !== undefined ? options.duration : 100;
            this.pixelDelta_ = options.pixelDelta !== undefined ? options.pixelDelta : 128;
        }
        handleEvent(mapBrowserEvent) {
            let stopEvent = false;
            if (mapBrowserEvent.type == EventType.KEYDOWN) {
                const keyEvent = mapBrowserEvent.originalEvent;
                const key = keyEvent.key;
                if (this.condition_(mapBrowserEvent) && (key == Key.DOWN || key == Key.LEFT || key == Key.RIGHT || key == Key.UP)) {
                    const map = mapBrowserEvent.map;
                    const view = map.getView();
                    const mapUnitsDelta = view.getResolution() * this.pixelDelta_;
                    let deltaX = 0, deltaY = 0;
                    if (key == Key.DOWN) {
                        deltaY = -mapUnitsDelta;
                    } else if (key == Key.LEFT) {
                        deltaX = -mapUnitsDelta;
                    } else if (key == Key.RIGHT) {
                        deltaX = mapUnitsDelta;
                    } else {
                        deltaY = mapUnitsDelta;
                    }
                    const delta = [ deltaX, deltaY ];
                    rotate$1(delta, view.getRotation());
                    pan(view, delta, this.duration_);
                    keyEvent.preventDefault();
                    stopEvent = true;
                }
            }
            return !stopEvent;
        }
    }
    class KeyboardZoom extends Interaction {
        constructor(options) {
            super();
            options = options ? options : {};
            this.condition_ = options.condition ? options.condition : function(mapBrowserEvent) {
                return !platformModifierKey(mapBrowserEvent) && targetNotEditable(mapBrowserEvent);
            };
            this.delta_ = options.delta ? options.delta : 1;
            this.duration_ = options.duration !== undefined ? options.duration : 100;
        }
        handleEvent(mapBrowserEvent) {
            let stopEvent = false;
            if (mapBrowserEvent.type == EventType.KEYDOWN || mapBrowserEvent.type == EventType.KEYPRESS) {
                const keyEvent = mapBrowserEvent.originalEvent;
                const key = keyEvent.key;
                if (this.condition_(mapBrowserEvent) && (key === "+" || key === "-")) {
                    const map = mapBrowserEvent.map;
                    const delta = key === "+" ? this.delta_ : -this.delta_;
                    const view = map.getView();
                    zoomByDelta(view, delta, undefined, this.duration_);
                    keyEvent.preventDefault();
                    stopEvent = true;
                }
            }
            return !stopEvent;
        }
    }
    class Kinetic {
        constructor(decay, minVelocity, delay) {
            this.decay_ = decay;
            this.minVelocity_ = minVelocity;
            this.delay_ = delay;
            this.points_ = [];
            this.angle_ = 0;
            this.initialVelocity_ = 0;
        }
        begin() {
            this.points_.length = 0;
            this.angle_ = 0;
            this.initialVelocity_ = 0;
        }
        update(x, y) {
            this.points_.push(x, y, Date.now());
        }
        end() {
            if (this.points_.length < 6) {
                return false;
            }
            const delay = Date.now() - this.delay_;
            const lastIndex = this.points_.length - 3;
            if (this.points_[lastIndex + 2] < delay) {
                return false;
            }
            let firstIndex = lastIndex - 3;
            while (firstIndex > 0 && this.points_[firstIndex + 2] > delay) {
                firstIndex -= 3;
            }
            const duration = this.points_[lastIndex + 2] - this.points_[firstIndex + 2];
            if (duration < 1e3 / 60) {
                return false;
            }
            const dx = this.points_[lastIndex] - this.points_[firstIndex];
            const dy = this.points_[lastIndex + 1] - this.points_[firstIndex + 1];
            this.angle_ = Math.atan2(dy, dx);
            this.initialVelocity_ = Math.sqrt(dx * dx + dy * dy) / duration;
            return this.initialVelocity_ > this.minVelocity_;
        }
        getDistance() {
            return (this.minVelocity_ - this.initialVelocity_) / this.decay_;
        }
        getAngle() {
            return this.angle_;
        }
    }
    class MouseWheelZoom extends Interaction {
        constructor(options) {
            options = options ? options : {};
            super(options);
            this.totalDelta_ = 0;
            this.lastDelta_ = 0;
            this.maxDelta_ = options.maxDelta !== undefined ? options.maxDelta : 1;
            this.duration_ = options.duration !== undefined ? options.duration : 250;
            this.timeout_ = options.timeout !== undefined ? options.timeout : 80;
            this.useAnchor_ = options.useAnchor !== undefined ? options.useAnchor : true;
            this.constrainResolution_ = options.constrainResolution !== undefined ? options.constrainResolution : false;
            const condition = options.condition ? options.condition : always;
            this.condition_ = options.onFocusOnly ? all$1(focusWithTabindex, condition) : condition;
            this.lastAnchor_ = null;
            this.startTime_ = undefined;
            this.timeoutId_;
            this.mode_ = undefined;
            this.trackpadEventGap_ = 400;
            this.trackpadTimeoutId_;
            this.deltaPerZoom_ = 300;
        }
        endInteraction_() {
            this.trackpadTimeoutId_ = undefined;
            const map = this.getMap();
            if (!map) {
                return;
            }
            const view = map.getView();
            view.endInteraction(undefined, this.lastDelta_ ? this.lastDelta_ > 0 ? 1 : -1 : 0, this.lastAnchor_);
        }
        handleEvent(mapBrowserEvent) {
            if (!this.condition_(mapBrowserEvent)) {
                return true;
            }
            const type = mapBrowserEvent.type;
            if (type !== EventType.WHEEL) {
                return true;
            }
            const map = mapBrowserEvent.map;
            const wheelEvent = mapBrowserEvent.originalEvent;
            wheelEvent.preventDefault();
            if (this.useAnchor_) {
                this.lastAnchor_ = mapBrowserEvent.coordinate;
            }
            let delta;
            if (mapBrowserEvent.type == EventType.WHEEL) {
                delta = wheelEvent.deltaY;
                if (FIREFOX && wheelEvent.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
                    delta /= DEVICE_PIXEL_RATIO;
                }
                if (wheelEvent.deltaMode === WheelEvent.DOM_DELTA_LINE) {
                    delta *= 40;
                }
            }
            if (delta === 0) {
                return false;
            }
            this.lastDelta_ = delta;
            const now = Date.now();
            if (this.startTime_ === undefined) {
                this.startTime_ = now;
            }
            if (!this.mode_ || now - this.startTime_ > this.trackpadEventGap_) {
                this.mode_ = Math.abs(delta) < 4 ? "trackpad" : "wheel";
            }
            const view = map.getView();
            if (this.mode_ === "trackpad" && !(view.getConstrainResolution() || this.constrainResolution_)) {
                if (this.trackpadTimeoutId_) {
                    clearTimeout(this.trackpadTimeoutId_);
                } else {
                    if (view.getAnimating()) {
                        view.cancelAnimations();
                    }
                    view.beginInteraction();
                }
                this.trackpadTimeoutId_ = setTimeout(this.endInteraction_.bind(this), this.timeout_);
                view.adjustZoom(-delta / this.deltaPerZoom_, this.lastAnchor_);
                this.startTime_ = now;
                return false;
            }
            this.totalDelta_ += delta;
            const timeLeft = Math.max(this.timeout_ - (now - this.startTime_), 0);
            clearTimeout(this.timeoutId_);
            this.timeoutId_ = setTimeout(this.handleWheelZoom_.bind(this, map), timeLeft);
            return false;
        }
        handleWheelZoom_(map) {
            const view = map.getView();
            if (view.getAnimating()) {
                view.cancelAnimations();
            }
            let delta = -clamp(this.totalDelta_, -this.maxDelta_ * this.deltaPerZoom_, this.maxDelta_ * this.deltaPerZoom_) / this.deltaPerZoom_;
            if (view.getConstrainResolution() || this.constrainResolution_) {
                delta = delta ? delta > 0 ? 1 : -1 : 0;
            }
            zoomByDelta(view, delta, this.lastAnchor_, this.duration_);
            this.mode_ = undefined;
            this.totalDelta_ = 0;
            this.lastAnchor_ = null;
            this.startTime_ = undefined;
            this.timeoutId_ = undefined;
        }
        setMouseAnchor(useAnchor) {
            this.useAnchor_ = useAnchor;
            if (!useAnchor) {
                this.lastAnchor_ = null;
            }
        }
    }
    class PinchRotate extends PointerInteraction {
        constructor(options) {
            options = options ? options : {};
            const pointerOptions = options;
            if (!pointerOptions.stopDown) {
                pointerOptions.stopDown = FALSE;
            }
            super(pointerOptions);
            this.anchor_ = null;
            this.lastAngle_ = undefined;
            this.rotating_ = false;
            this.rotationDelta_ = 0;
            this.threshold_ = options.threshold !== undefined ? options.threshold : .3;
            this.duration_ = options.duration !== undefined ? options.duration : 250;
        }
        handleDragEvent(mapBrowserEvent) {
            let rotationDelta = 0;
            const touch0 = this.targetPointers[0];
            const touch1 = this.targetPointers[1];
            const angle = Math.atan2(touch1.clientY - touch0.clientY, touch1.clientX - touch0.clientX);
            if (this.lastAngle_ !== undefined) {
                const delta = angle - this.lastAngle_;
                this.rotationDelta_ += delta;
                if (!this.rotating_ && Math.abs(this.rotationDelta_) > this.threshold_) {
                    this.rotating_ = true;
                }
                rotationDelta = delta;
            }
            this.lastAngle_ = angle;
            const map = mapBrowserEvent.map;
            const view = map.getView();
            if (view.getConstraints().rotation === disable) {
                return;
            }
            this.anchor_ = map.getCoordinateFromPixelInternal(map.getEventPixel(centroid(this.targetPointers)));
            if (this.rotating_) {
                map.render();
                view.adjustRotationInternal(rotationDelta, this.anchor_);
            }
        }
        handleUpEvent(mapBrowserEvent) {
            if (this.targetPointers.length < 2) {
                const map = mapBrowserEvent.map;
                const view = map.getView();
                view.endInteraction(this.duration_);
                return false;
            }
            return true;
        }
        handleDownEvent(mapBrowserEvent) {
            if (this.targetPointers.length >= 2) {
                const map = mapBrowserEvent.map;
                this.anchor_ = null;
                this.lastAngle_ = undefined;
                this.rotating_ = false;
                this.rotationDelta_ = 0;
                if (!this.handlingDownUpSequence) {
                    map.getView().beginInteraction();
                }
                return true;
            }
            return false;
        }
    }
    class PinchZoom extends PointerInteraction {
        constructor(options) {
            options = options ? options : {};
            const pointerOptions = options;
            if (!pointerOptions.stopDown) {
                pointerOptions.stopDown = FALSE;
            }
            super(pointerOptions);
            this.anchor_ = null;
            this.duration_ = options.duration !== undefined ? options.duration : 400;
            this.lastDistance_ = undefined;
            this.lastScaleDelta_ = 1;
        }
        handleDragEvent(mapBrowserEvent) {
            let scaleDelta = 1;
            const touch0 = this.targetPointers[0];
            const touch1 = this.targetPointers[1];
            const dx = touch0.clientX - touch1.clientX;
            const dy = touch0.clientY - touch1.clientY;
            const distance = Math.sqrt(dx * dx + dy * dy);
            if (this.lastDistance_ !== undefined) {
                scaleDelta = this.lastDistance_ / distance;
            }
            this.lastDistance_ = distance;
            const map = mapBrowserEvent.map;
            const view = map.getView();
            if (scaleDelta != 1) {
                this.lastScaleDelta_ = scaleDelta;
            }
            this.anchor_ = map.getCoordinateFromPixelInternal(map.getEventPixel(centroid(this.targetPointers)));
            map.render();
            view.adjustResolutionInternal(scaleDelta, this.anchor_);
        }
        handleUpEvent(mapBrowserEvent) {
            if (this.targetPointers.length < 2) {
                const map = mapBrowserEvent.map;
                const view = map.getView();
                const direction = this.lastScaleDelta_ > 1 ? 1 : -1;
                view.endInteraction(this.duration_, direction);
                return false;
            }
            return true;
        }
        handleDownEvent(mapBrowserEvent) {
            if (this.targetPointers.length >= 2) {
                const map = mapBrowserEvent.map;
                this.anchor_ = null;
                this.lastDistance_ = undefined;
                this.lastScaleDelta_ = 1;
                if (!this.handlingDownUpSequence) {
                    map.getView().beginInteraction();
                }
                return true;
            }
            return false;
        }
    }
    function defaults(options) {
        options = options ? options : {};
        const interactions = new Collection;
        const kinetic = new Kinetic(-.005, .05, 100);
        const altShiftDragRotate = options.altShiftDragRotate !== undefined ? options.altShiftDragRotate : true;
        if (altShiftDragRotate) {
            interactions.push(new DragRotate);
        }
        const doubleClickZoom = options.doubleClickZoom !== undefined ? options.doubleClickZoom : true;
        if (doubleClickZoom) {
            interactions.push(new DoubleClickZoom({
                delta: options.zoomDelta,
                duration: options.zoomDuration
            }));
        }
        const dragPan = options.dragPan !== undefined ? options.dragPan : true;
        if (dragPan) {
            interactions.push(new DragPan({
                onFocusOnly: options.onFocusOnly,
                kinetic: kinetic
            }));
        }
        const pinchRotate = options.pinchRotate !== undefined ? options.pinchRotate : true;
        if (pinchRotate) {
            interactions.push(new PinchRotate);
        }
        const pinchZoom = options.pinchZoom !== undefined ? options.pinchZoom : true;
        if (pinchZoom) {
            interactions.push(new PinchZoom({
                duration: options.zoomDuration
            }));
        }
        const keyboard = options.keyboard !== undefined ? options.keyboard : true;
        if (keyboard) {
            interactions.push(new KeyboardPan);
            interactions.push(new KeyboardZoom({
                delta: options.zoomDelta,
                duration: options.zoomDuration
            }));
        }
        const mouseWheelZoom = options.mouseWheelZoom !== undefined ? options.mouseWheelZoom : true;
        if (mouseWheelZoom) {
            interactions.push(new MouseWheelZoom({
                onFocusOnly: options.onFocusOnly,
                duration: options.zoomDuration
            }));
        }
        const shiftDragZoom = options.shiftDragZoom !== undefined ? options.shiftDragZoom : true;
        if (shiftDragZoom) {
            interactions.push(new DragZoom({
                duration: options.zoomDuration
            }));
        }
        return interactions;
    }
    function removeLayerMapProperty(layer) {
        if (layer instanceof Layer) {
            layer.setMapInternal(null);
            return;
        }
        if (layer instanceof LayerGroup) {
            layer.getLayers().forEach(removeLayerMapProperty);
        }
    }
    function setLayerMapProperty(layer, map) {
        if (layer instanceof Layer) {
            layer.setMapInternal(map);
            return;
        }
        if (layer instanceof LayerGroup) {
            const layers = layer.getLayers().getArray();
            for (let i = 0, ii = layers.length; i < ii; ++i) {
                setLayerMapProperty(layers[i], map);
            }
        }
    }
    class Map extends BaseObject {
        constructor(options) {
            super();
            options = options || {};
            this.on;
            this.once;
            this.un;
            const optionsInternal = createOptionsInternal(options);
            this.renderComplete_;
            this.loaded_ = true;
            this.boundHandleBrowserEvent_ = this.handleBrowserEvent.bind(this);
            this.maxTilesLoading_ = options.maxTilesLoading !== undefined ? options.maxTilesLoading : 16;
            this.pixelRatio_ = options.pixelRatio !== undefined ? options.pixelRatio : DEVICE_PIXEL_RATIO;
            this.postRenderTimeoutHandle_;
            this.animationDelayKey_;
            this.animationDelay_ = this.animationDelay_.bind(this);
            this.coordinateToPixelTransform_ = create();
            this.pixelToCoordinateTransform_ = create();
            this.frameIndex_ = 0;
            this.frameState_ = null;
            this.previousExtent_ = null;
            this.viewPropertyListenerKey_ = null;
            this.viewChangeListenerKey_ = null;
            this.layerGroupPropertyListenerKeys_ = null;
            this.viewport_ = document.createElement("div");
            this.viewport_.className = "ol-viewport" + ("ontouchstart" in window ? " ol-touch" : "");
            this.viewport_.style.position = "relative";
            this.viewport_.style.overflow = "hidden";
            this.viewport_.style.width = "100%";
            this.viewport_.style.height = "100%";
            this.overlayContainer_ = document.createElement("div");
            this.overlayContainer_.style.position = "absolute";
            this.overlayContainer_.style.zIndex = "0";
            this.overlayContainer_.style.width = "100%";
            this.overlayContainer_.style.height = "100%";
            this.overlayContainer_.style.pointerEvents = "none";
            this.overlayContainer_.className = "ol-overlaycontainer";
            this.viewport_.appendChild(this.overlayContainer_);
            this.overlayContainerStopEvent_ = document.createElement("div");
            this.overlayContainerStopEvent_.style.position = "absolute";
            this.overlayContainerStopEvent_.style.zIndex = "0";
            this.overlayContainerStopEvent_.style.width = "100%";
            this.overlayContainerStopEvent_.style.height = "100%";
            this.overlayContainerStopEvent_.style.pointerEvents = "none";
            this.overlayContainerStopEvent_.className = "ol-overlaycontainer-stopevent";
            this.viewport_.appendChild(this.overlayContainerStopEvent_);
            this.mapBrowserEventHandler_ = null;
            this.moveTolerance_ = options.moveTolerance;
            this.keyboardEventTarget_ = optionsInternal.keyboardEventTarget;
            this.targetChangeHandlerKeys_ = null;
            this.targetElement_ = null;
            this.resizeObserver_ = new ResizeObserver(() => this.updateSize());
            this.controls = optionsInternal.controls || defaults$1();
            this.interactions = optionsInternal.interactions || defaults({
                onFocusOnly: true
            });
            this.overlays_ = optionsInternal.overlays;
            this.overlayIdIndex_ = {};
            this.renderer_ = null;
            this.postRenderFunctions_ = [];
            this.tileQueue_ = new TileQueue(this.getTilePriority.bind(this), this.handleTileChange_.bind(this));
            this.addChangeListener(MapProperty.LAYERGROUP, this.handleLayerGroupChanged_);
            this.addChangeListener(MapProperty.VIEW, this.handleViewChanged_);
            this.addChangeListener(MapProperty.SIZE, this.handleSizeChanged_);
            this.addChangeListener(MapProperty.TARGET, this.handleTargetChanged_);
            this.setProperties(optionsInternal.values);
            const map = this;
            if (options.view && !(options.view instanceof View)) {
                options.view.then(function(viewOptions) {
                    map.setView(new View(viewOptions));
                });
            }
            this.controls.addEventListener(CollectionEventType.ADD, event => {
                event.element.setMap(this);
            });
            this.controls.addEventListener(CollectionEventType.REMOVE, event => {
                event.element.setMap(null);
            });
            this.interactions.addEventListener(CollectionEventType.ADD, event => {
                event.element.setMap(this);
            });
            this.interactions.addEventListener(CollectionEventType.REMOVE, event => {
                event.element.setMap(null);
            });
            this.overlays_.addEventListener(CollectionEventType.ADD, event => {
                this.addOverlayInternal_(event.element);
            });
            this.overlays_.addEventListener(CollectionEventType.REMOVE, event => {
                const id = event.element.getId();
                if (id !== undefined) {
                    delete this.overlayIdIndex_[id.toString()];
                }
                event.element.setMap(null);
            });
            this.controls.forEach(control => {
                control.setMap(this);
            });
            this.interactions.forEach(interaction => {
                interaction.setMap(this);
            });
            this.overlays_.forEach(this.addOverlayInternal_.bind(this));
        }
        addControl(control) {
            this.getControls().push(control);
        }
        addInteraction(interaction) {
            this.getInteractions().push(interaction);
        }
        addLayer(layer) {
            const layers = this.getLayerGroup().getLayers();
            layers.push(layer);
        }
        handleLayerAdd_(event) {
            setLayerMapProperty(event.layer, this);
        }
        addOverlay(overlay) {
            this.getOverlays().push(overlay);
        }
        addOverlayInternal_(overlay) {
            const id = overlay.getId();
            if (id !== undefined) {
                this.overlayIdIndex_[id.toString()] = overlay;
            }
            overlay.setMap(this);
        }
        disposeInternal() {
            this.controls.clear();
            this.interactions.clear();
            this.overlays_.clear();
            this.resizeObserver_.disconnect();
            this.setTarget(null);
            super.disposeInternal();
        }
        forEachFeatureAtPixel(pixel, callback, options) {
            if (!this.frameState_ || !this.renderer_) {
                return;
            }
            const coordinate = this.getCoordinateFromPixelInternal(pixel);
            options = options !== undefined ? options : {};
            const hitTolerance = options.hitTolerance !== undefined ? options.hitTolerance : 0;
            const layerFilter = options.layerFilter !== undefined ? options.layerFilter : TRUE;
            const checkWrapped = options.checkWrapped !== false;
            return this.renderer_.forEachFeatureAtCoordinate(coordinate, this.frameState_, hitTolerance, checkWrapped, callback, null, layerFilter, null);
        }
        getFeaturesAtPixel(pixel, options) {
            const features = [];
            this.forEachFeatureAtPixel(pixel, function(feature) {
                features.push(feature);
            }, options);
            return features;
        }
        getAllLayers() {
            const layers = [];
            function addLayersFrom(layerGroup) {
                layerGroup.forEach(function(layer) {
                    if (layer instanceof LayerGroup) {
                        addLayersFrom(layer.getLayers());
                    } else {
                        layers.push(layer);
                    }
                });
            }
            addLayersFrom(this.getLayers());
            return layers;
        }
        hasFeatureAtPixel(pixel, options) {
            if (!this.frameState_ || !this.renderer_) {
                return false;
            }
            const coordinate = this.getCoordinateFromPixelInternal(pixel);
            options = options !== undefined ? options : {};
            const layerFilter = options.layerFilter !== undefined ? options.layerFilter : TRUE;
            const hitTolerance = options.hitTolerance !== undefined ? options.hitTolerance : 0;
            const checkWrapped = options.checkWrapped !== false;
            return this.renderer_.hasFeatureAtCoordinate(coordinate, this.frameState_, hitTolerance, checkWrapped, layerFilter, null);
        }
        getEventCoordinate(event) {
            return this.getCoordinateFromPixel(this.getEventPixel(event));
        }
        getEventCoordinateInternal(event) {
            return this.getCoordinateFromPixelInternal(this.getEventPixel(event));
        }
        getEventPixel(event) {
            const viewport = this.viewport_;
            const viewportPosition = viewport.getBoundingClientRect();
            const viewportSize = this.getSize();
            const scaleX = viewportPosition.width / viewportSize[0];
            const scaleY = viewportPosition.height / viewportSize[1];
            const eventPosition = "changedTouches" in event ? event.changedTouches[0] : event;
            return [ (eventPosition.clientX - viewportPosition.left) / scaleX, (eventPosition.clientY - viewportPosition.top) / scaleY ];
        }
        getTarget() {
            return this.get(MapProperty.TARGET);
        }
        getTargetElement() {
            return this.targetElement_;
        }
        getCoordinateFromPixel(pixel) {
            return toUserCoordinate(this.getCoordinateFromPixelInternal(pixel), this.getView().getProjection());
        }
        getCoordinateFromPixelInternal(pixel) {
            const frameState = this.frameState_;
            if (!frameState) {
                return null;
            }
            return apply(frameState.pixelToCoordinateTransform, pixel.slice());
        }
        getControls() {
            return this.controls;
        }
        getOverlays() {
            return this.overlays_;
        }
        getOverlayById(id) {
            const overlay = this.overlayIdIndex_[id.toString()];
            return overlay !== undefined ? overlay : null;
        }
        getInteractions() {
            return this.interactions;
        }
        getLayerGroup() {
            return this.get(MapProperty.LAYERGROUP);
        }
        setLayers(layers) {
            const group = this.getLayerGroup();
            if (layers instanceof Collection) {
                group.setLayers(layers);
                return;
            }
            const collection = group.getLayers();
            collection.clear();
            collection.extend(layers);
        }
        getLayers() {
            const layers = this.getLayerGroup().getLayers();
            return layers;
        }
        getLoadingOrNotReady() {
            const layerStatesArray = this.getLayerGroup().getLayerStatesArray();
            for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
                const state = layerStatesArray[i];
                if (!state.visible) {
                    continue;
                }
                const renderer = state.layer.getRenderer();
                if (renderer && !renderer.ready) {
                    return true;
                }
                const source = state.layer.getSource();
                if (source && source.loading) {
                    return true;
                }
            }
            return false;
        }
        getPixelFromCoordinate(coordinate) {
            const viewCoordinate = fromUserCoordinate(coordinate, this.getView().getProjection());
            return this.getPixelFromCoordinateInternal(viewCoordinate);
        }
        getPixelFromCoordinateInternal(coordinate) {
            const frameState = this.frameState_;
            if (!frameState) {
                return null;
            }
            return apply(frameState.coordinateToPixelTransform, coordinate.slice(0, 2));
        }
        getRenderer() {
            return this.renderer_;
        }
        getSize() {
            return this.get(MapProperty.SIZE);
        }
        getView() {
            return this.get(MapProperty.VIEW);
        }
        getViewport() {
            return this.viewport_;
        }
        getOverlayContainer() {
            return this.overlayContainer_;
        }
        getOverlayContainerStopEvent() {
            return this.overlayContainerStopEvent_;
        }
        getOwnerDocument() {
            const targetElement = this.getTargetElement();
            return targetElement ? targetElement.ownerDocument : document;
        }
        getTilePriority(tile, tileSourceKey, tileCenter, tileResolution) {
            return getTilePriority(this.frameState_, tile, tileSourceKey, tileCenter, tileResolution);
        }
        handleBrowserEvent(browserEvent, type) {
            type = type || browserEvent.type;
            const mapBrowserEvent = new MapBrowserEvent(type, this, browserEvent);
            this.handleMapBrowserEvent(mapBrowserEvent);
        }
        handleMapBrowserEvent(mapBrowserEvent) {
            if (!this.frameState_) {
                return;
            }
            const originalEvent = mapBrowserEvent.originalEvent;
            const eventType = originalEvent.type;
            if (eventType === PointerEventType.POINTERDOWN || eventType === EventType.WHEEL || eventType === EventType.KEYDOWN) {
                const doc = this.getOwnerDocument();
                const rootNode = this.viewport_.getRootNode ? this.viewport_.getRootNode() : doc;
                const target = originalEvent.target;
                if (this.overlayContainerStopEvent_.contains(target) || !(rootNode === doc ? doc.documentElement : rootNode).contains(target)) {
                    return;
                }
            }
            mapBrowserEvent.frameState = this.frameState_;
            if (this.dispatchEvent(mapBrowserEvent) !== false) {
                const interactionsArray = this.getInteractions().getArray().slice();
                for (let i = interactionsArray.length - 1; i >= 0; i--) {
                    const interaction = interactionsArray[i];
                    if (interaction.getMap() !== this || !interaction.getActive() || !this.getTargetElement()) {
                        continue;
                    }
                    const cont = interaction.handleEvent(mapBrowserEvent);
                    if (!cont || mapBrowserEvent.propagationStopped) {
                        break;
                    }
                }
            }
        }
        handlePostRender() {
            const frameState = this.frameState_;
            const tileQueue = this.tileQueue_;
            if (!tileQueue.isEmpty()) {
                let maxTotalLoading = this.maxTilesLoading_;
                let maxNewLoads = maxTotalLoading;
                if (frameState) {
                    const hints = frameState.viewHints;
                    if (hints[ViewHint.ANIMATING] || hints[ViewHint.INTERACTING]) {
                        const lowOnFrameBudget = Date.now() - frameState.time > 8;
                        maxTotalLoading = lowOnFrameBudget ? 0 : 8;
                        maxNewLoads = lowOnFrameBudget ? 0 : 2;
                    }
                }
                if (tileQueue.getTilesLoading() < maxTotalLoading) {
                    tileQueue.reprioritize();
                    tileQueue.loadMoreTiles(maxTotalLoading, maxNewLoads);
                }
            }
            if (frameState && this.renderer_ && !frameState.animate) {
                if (this.renderComplete_ === true) {
                    if (this.hasListener(RenderEventType.RENDERCOMPLETE)) {
                        this.renderer_.dispatchRenderEvent(RenderEventType.RENDERCOMPLETE, frameState);
                    }
                    if (this.loaded_ === false) {
                        this.loaded_ = true;
                        this.dispatchEvent(new MapEvent(MapEventType.LOADEND, this, frameState));
                    }
                } else if (this.loaded_ === true) {
                    this.loaded_ = false;
                    this.dispatchEvent(new MapEvent(MapEventType.LOADSTART, this, frameState));
                }
            }
            const postRenderFunctions = this.postRenderFunctions_;
            for (let i = 0, ii = postRenderFunctions.length; i < ii; ++i) {
                postRenderFunctions[i](this, frameState);
            }
            postRenderFunctions.length = 0;
        }
        handleSizeChanged_() {
            if (this.getView() && !this.getView().getAnimating()) {
                this.getView().resolveConstraints(0);
            }
            this.render();
        }
        handleTargetChanged_() {
            if (this.mapBrowserEventHandler_) {
                for (let i = 0, ii = this.targetChangeHandlerKeys_.length; i < ii; ++i) {
                    unlistenByKey(this.targetChangeHandlerKeys_[i]);
                }
                this.targetChangeHandlerKeys_ = null;
                this.viewport_.removeEventListener(EventType.CONTEXTMENU, this.boundHandleBrowserEvent_);
                this.viewport_.removeEventListener(EventType.WHEEL, this.boundHandleBrowserEvent_);
                this.mapBrowserEventHandler_.dispose();
                this.mapBrowserEventHandler_ = null;
                removeNode(this.viewport_);
            }
            if (this.targetElement_) {
                this.resizeObserver_.unobserve(this.targetElement_);
                const rootNode = this.targetElement_.getRootNode();
                if (rootNode instanceof ShadowRoot) {
                    this.resizeObserver_.unobserve(rootNode.host);
                }
                this.setSize(undefined);
            }
            const target = this.getTarget();
            const targetElement = typeof target === "string" ? document.getElementById(target) : target;
            this.targetElement_ = targetElement;
            if (!targetElement) {
                if (this.renderer_) {
                    clearTimeout(this.postRenderTimeoutHandle_);
                    this.postRenderTimeoutHandle_ = undefined;
                    this.postRenderFunctions_.length = 0;
                    this.renderer_.dispose();
                    this.renderer_ = null;
                }
                if (this.animationDelayKey_) {
                    cancelAnimationFrame(this.animationDelayKey_);
                    this.animationDelayKey_ = undefined;
                }
            } else {
                targetElement.appendChild(this.viewport_);
                if (!this.renderer_) {
                    this.renderer_ = new CompositeMapRenderer(this);
                }
                this.mapBrowserEventHandler_ = new MapBrowserEventHandler(this, this.moveTolerance_);
                for (const key in MapBrowserEventType) {
                    this.mapBrowserEventHandler_.addEventListener(MapBrowserEventType[key], this.handleMapBrowserEvent.bind(this));
                }
                this.viewport_.addEventListener(EventType.CONTEXTMENU, this.boundHandleBrowserEvent_, false);
                this.viewport_.addEventListener(EventType.WHEEL, this.boundHandleBrowserEvent_, PASSIVE_EVENT_LISTENERS ? {
                    passive: false
                } : false);
                const keyboardEventTarget = !this.keyboardEventTarget_ ? targetElement : this.keyboardEventTarget_;
                this.targetChangeHandlerKeys_ = [ listen(keyboardEventTarget, EventType.KEYDOWN, this.handleBrowserEvent, this), listen(keyboardEventTarget, EventType.KEYPRESS, this.handleBrowserEvent, this) ];
                const rootNode = targetElement.getRootNode();
                if (rootNode instanceof ShadowRoot) {
                    this.resizeObserver_.observe(rootNode.host);
                }
                this.resizeObserver_.observe(targetElement);
            }
            this.updateSize();
        }
        handleTileChange_() {
            this.render();
        }
        handleViewPropertyChanged_() {
            this.render();
        }
        handleViewChanged_() {
            if (this.viewPropertyListenerKey_) {
                unlistenByKey(this.viewPropertyListenerKey_);
                this.viewPropertyListenerKey_ = null;
            }
            if (this.viewChangeListenerKey_) {
                unlistenByKey(this.viewChangeListenerKey_);
                this.viewChangeListenerKey_ = null;
            }
            const view = this.getView();
            if (view) {
                this.updateViewportSize_(this.getSize());
                this.viewPropertyListenerKey_ = listen(view, ObjectEventType.PROPERTYCHANGE, this.handleViewPropertyChanged_, this);
                this.viewChangeListenerKey_ = listen(view, EventType.CHANGE, this.handleViewPropertyChanged_, this);
                view.resolveConstraints(0);
            }
            this.render();
        }
        handleLayerGroupChanged_() {
            if (this.layerGroupPropertyListenerKeys_) {
                this.layerGroupPropertyListenerKeys_.forEach(unlistenByKey);
                this.layerGroupPropertyListenerKeys_ = null;
            }
            const layerGroup = this.getLayerGroup();
            if (layerGroup) {
                this.handleLayerAdd_(new GroupEvent("addlayer", layerGroup));
                this.layerGroupPropertyListenerKeys_ = [ listen(layerGroup, ObjectEventType.PROPERTYCHANGE, this.render, this), listen(layerGroup, EventType.CHANGE, this.render, this), listen(layerGroup, "addlayer", this.handleLayerAdd_, this), listen(layerGroup, "removelayer", this.handleLayerRemove_, this) ];
            }
            this.render();
        }
        isRendered() {
            return !!this.frameState_;
        }
        animationDelay_() {
            this.animationDelayKey_ = undefined;
            this.renderFrame_(Date.now());
        }
        renderSync() {
            if (this.animationDelayKey_) {
                cancelAnimationFrame(this.animationDelayKey_);
            }
            this.animationDelay_();
        }
        redrawText() {
            const layerStates = this.getLayerGroup().getLayerStatesArray();
            for (let i = 0, ii = layerStates.length; i < ii; ++i) {
                const layer = layerStates[i].layer;
                if (layer.hasRenderer()) {
                    layer.getRenderer().handleFontsChanged();
                }
            }
        }
        render() {
            if (this.renderer_ && this.animationDelayKey_ === undefined) {
                this.animationDelayKey_ = requestAnimationFrame(this.animationDelay_);
            }
        }
        removeControl(control) {
            return this.getControls().remove(control);
        }
        removeInteraction(interaction) {
            return this.getInteractions().remove(interaction);
        }
        removeLayer(layer) {
            const layers = this.getLayerGroup().getLayers();
            return layers.remove(layer);
        }
        handleLayerRemove_(event) {
            removeLayerMapProperty(event.layer);
        }
        removeOverlay(overlay) {
            return this.getOverlays().remove(overlay);
        }
        renderFrame_(time) {
            const size = this.getSize();
            const view = this.getView();
            const previousFrameState = this.frameState_;
            let frameState = null;
            if (size !== undefined && hasArea(size) && view && view.isDef()) {
                const viewHints = view.getHints(this.frameState_ ? this.frameState_.viewHints : undefined);
                const viewState = view.getState();
                frameState = {
                    animate: false,
                    coordinateToPixelTransform: this.coordinateToPixelTransform_,
                    declutter: null,
                    extent: getForViewAndSize(viewState.center, viewState.resolution, viewState.rotation, size),
                    index: this.frameIndex_++,
                    layerIndex: 0,
                    layerStatesArray: this.getLayerGroup().getLayerStatesArray(),
                    pixelRatio: this.pixelRatio_,
                    pixelToCoordinateTransform: this.pixelToCoordinateTransform_,
                    postRenderFunctions: [],
                    size: size,
                    tileQueue: this.tileQueue_,
                    time: time,
                    usedTiles: {},
                    viewState: viewState,
                    viewHints: viewHints,
                    wantedTiles: {},
                    mapId: getUid(this),
                    renderTargets: {}
                };
                if (viewState.nextCenter && viewState.nextResolution) {
                    const rotation = isNaN(viewState.nextRotation) ? viewState.rotation : viewState.nextRotation;
                    frameState.nextExtent = getForViewAndSize(viewState.nextCenter, viewState.nextResolution, rotation, size);
                }
            }
            this.frameState_ = frameState;
            this.renderer_.renderFrame(frameState);
            if (frameState) {
                if (frameState.animate) {
                    this.render();
                }
                Array.prototype.push.apply(this.postRenderFunctions_, frameState.postRenderFunctions);
                if (previousFrameState) {
                    const moveStart = !this.previousExtent_ || !isEmpty(this.previousExtent_) && !equals$1(frameState.extent, this.previousExtent_);
                    if (moveStart) {
                        this.dispatchEvent(new MapEvent(MapEventType.MOVESTART, this, previousFrameState));
                        this.previousExtent_ = createOrUpdateEmpty(this.previousExtent_);
                    }
                }
                const idle = this.previousExtent_ && !frameState.viewHints[ViewHint.ANIMATING] && !frameState.viewHints[ViewHint.INTERACTING] && !equals$1(frameState.extent, this.previousExtent_);
                if (idle) {
                    this.dispatchEvent(new MapEvent(MapEventType.MOVEEND, this, frameState));
                    clone(frameState.extent, this.previousExtent_);
                }
            }
            this.dispatchEvent(new MapEvent(MapEventType.POSTRENDER, this, frameState));
            this.renderComplete_ = this.hasListener(MapEventType.LOADSTART) || this.hasListener(MapEventType.LOADEND) || this.hasListener(RenderEventType.RENDERCOMPLETE) ? !this.tileQueue_.getTilesLoading() && !this.tileQueue_.getCount() && !this.getLoadingOrNotReady() : undefined;
            if (!this.postRenderTimeoutHandle_) {
                this.postRenderTimeoutHandle_ = setTimeout(() => {
                    this.postRenderTimeoutHandle_ = undefined;
                    this.handlePostRender();
                }, 0);
            }
        }
        setLayerGroup(layerGroup) {
            const oldLayerGroup = this.getLayerGroup();
            if (oldLayerGroup) {
                this.handleLayerRemove_(new GroupEvent("removelayer", oldLayerGroup));
            }
            this.set(MapProperty.LAYERGROUP, layerGroup);
        }
        setSize(size) {
            this.set(MapProperty.SIZE, size);
        }
        setTarget(target) {
            this.set(MapProperty.TARGET, target);
        }
        setView(view) {
            if (!view || view instanceof View) {
                this.set(MapProperty.VIEW, view);
                return;
            }
            this.set(MapProperty.VIEW, new View);
            const map = this;
            view.then(function(viewOptions) {
                map.setView(new View(viewOptions));
            });
        }
        updateSize() {
            const targetElement = this.getTargetElement();
            let size = undefined;
            if (targetElement) {
                const computedStyle = getComputedStyle(targetElement);
                const width = targetElement.offsetWidth - parseFloat(computedStyle["borderLeftWidth"]) - parseFloat(computedStyle["paddingLeft"]) - parseFloat(computedStyle["paddingRight"]) - parseFloat(computedStyle["borderRightWidth"]);
                const height = targetElement.offsetHeight - parseFloat(computedStyle["borderTopWidth"]) - parseFloat(computedStyle["paddingTop"]) - parseFloat(computedStyle["paddingBottom"]) - parseFloat(computedStyle["borderBottomWidth"]);
                if (!isNaN(width) && !isNaN(height)) {
                    size = [ width, height ];
                    if (!hasArea(size) && !!(targetElement.offsetWidth || targetElement.offsetHeight || targetElement.getClientRects().length)) {
                        warn("No map visible because the map container's width or height are 0.");
                    }
                }
            }
            const oldSize = this.getSize();
            if (size && (!oldSize || !equals$2(size, oldSize))) {
                this.setSize(size);
                this.updateViewportSize_(size);
            }
        }
        updateViewportSize_(size) {
            const view = this.getView();
            if (view) {
                view.setViewportSize(size);
            }
        }
    }
    function createOptionsInternal(options) {
        let keyboardEventTarget = null;
        if (options.keyboardEventTarget !== undefined) {
            keyboardEventTarget = typeof options.keyboardEventTarget === "string" ? document.getElementById(options.keyboardEventTarget) : options.keyboardEventTarget;
        }
        const values = {};
        const layerGroup = options.layers && typeof options.layers.getLayers === "function" ? options.layers : new LayerGroup({
            layers: options.layers
        });
        values[MapProperty.LAYERGROUP] = layerGroup;
        values[MapProperty.TARGET] = options.target;
        values[MapProperty.VIEW] = options.view instanceof View ? options.view : new View;
        let controls;
        if (options.controls !== undefined) {
            if (Array.isArray(options.controls)) {
                controls = new Collection(options.controls.slice());
            } else {
                assert(typeof options.controls.getArray === "function", "Expected `controls` to be an array or an `ol/Collection.js`");
                controls = options.controls;
            }
        }
        let interactions;
        if (options.interactions !== undefined) {
            if (Array.isArray(options.interactions)) {
                interactions = new Collection(options.interactions.slice());
            } else {
                assert(typeof options.interactions.getArray === "function", "Expected `interactions` to be an array or an `ol/Collection.js`");
                interactions = options.interactions;
            }
        }
        let overlays;
        if (options.overlays !== undefined) {
            if (Array.isArray(options.overlays)) {
                overlays = new Collection(options.overlays.slice());
            } else {
                assert(typeof options.overlays.getArray === "function", "Expected `overlays` to be an array or an `ol/Collection.js`");
                overlays = options.overlays;
            }
        } else {
            overlays = new Collection;
        }
        return {
            controls: controls,
            interactions: interactions,
            keyboardEventTarget: keyboardEventTarget,
            overlays: overlays,
            values: values
        };
    }
    var TileProperty = {
        PRELOAD: "preload",
        USE_INTERIM_TILES_ON_ERROR: "useInterimTilesOnError"
    };
    class BaseTileLayer extends Layer {
        constructor(options) {
            options = options ? options : {};
            const baseOptions = Object.assign({}, options);
            delete baseOptions.preload;
            delete baseOptions.useInterimTilesOnError;
            super(baseOptions);
            this.on;
            this.once;
            this.un;
            this.setPreload(options.preload !== undefined ? options.preload : 0);
            this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? options.useInterimTilesOnError : true);
        }
        getPreload() {
            return this.get(TileProperty.PRELOAD);
        }
        setPreload(preload) {
            this.set(TileProperty.PRELOAD, preload);
        }
        getUseInterimTilesOnError() {
            return this.get(TileProperty.USE_INTERIM_TILES_ON_ERROR);
        }
        setUseInterimTilesOnError(useInterimTilesOnError) {
            this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError);
        }
        getData(pixel) {
            return super.getData(pixel);
        }
    }
    class LayerRenderer extends Observable {
        constructor(layer) {
            super();
            this.ready = true;
            this.boundHandleImageChange_ = this.handleImageChange_.bind(this);
            this.layer_ = layer;
        }
        getFeatures(pixel) {
            return abstract();
        }
        getData(pixel) {
            return null;
        }
        prepareFrame(frameState) {
            return abstract();
        }
        renderFrame(frameState, target) {
            return abstract();
        }
        loadedTileCallback(tiles, zoom, tile) {
            if (!tiles[zoom]) {
                tiles[zoom] = {};
            }
            tiles[zoom][tile.tileCoord.toString()] = tile;
            return undefined;
        }
        createLoadedTileFinder(source, projection, tiles) {
            return (zoom, tileRange) => {
                const callback = this.loadedTileCallback.bind(this, tiles, zoom);
                return source.forEachLoadedTile(projection, zoom, tileRange, callback);
            };
        }
        forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, matches) {
            return undefined;
        }
        getLayer() {
            return this.layer_;
        }
        handleFontsChanged() {}
        handleImageChange_(event) {
            const image = event.target;
            if (image.getState() === ImageState.LOADED || image.getState() === ImageState.ERROR) {
                this.renderIfReadyAndVisible();
            }
        }
        loadImage(image) {
            let imageState = image.getState();
            if (imageState != ImageState.LOADED && imageState != ImageState.ERROR) {
                image.addEventListener(EventType.CHANGE, this.boundHandleImageChange_);
            }
            if (imageState == ImageState.IDLE) {
                image.load();
                imageState = image.getState();
            }
            return imageState == ImageState.LOADED;
        }
        renderIfReadyAndVisible() {
            const layer = this.getLayer();
            if (layer && layer.getVisible() && layer.getSourceState() === "ready") {
                layer.changed();
            }
        }
        renderDeferred(frameState) {}
        disposeInternal() {
            delete this.layer_;
            super.disposeInternal();
        }
    }
    class ZIndexContext {
        constructor() {
            this.instructions_ = [];
            this.zIndex = 0;
            this.offset_ = 0;
            this.context_ = new Proxy(getSharedCanvasContext2D(), {
                get: (target, property) => {
                    if (typeof getSharedCanvasContext2D()[property] !== "function") {
                        return undefined;
                    }
                    if (!this.instructions_[this.zIndex + this.offset_]) {
                        this.instructions_[this.zIndex + this.offset_] = [];
                    }
                    this.instructions_[this.zIndex + this.offset_].push(property);
                    return this.pushMethodArgs_;
                },
                set: (target, property, value) => {
                    if (!this.instructions_[this.zIndex + this.offset_]) {
                        this.instructions_[this.zIndex + this.offset_] = [];
                    }
                    this.instructions_[this.zIndex + this.offset_].push(property, value);
                    return true;
                }
            });
        }
        pushMethodArgs_=(...args) => {
            this.instructions_[this.zIndex + this.offset_].push(args);
            return this;
        };
        pushFunction(render) {
            this.instructions_[this.zIndex + this.offset_].push(render);
        }
        getContext() {
            return this.context_;
        }
        draw(context) {
            this.instructions_.forEach(instructionsAtIndex => {
                for (let i = 0, ii = instructionsAtIndex.length; i < ii; ++i) {
                    const property = instructionsAtIndex[i];
                    if (typeof property === "function") {
                        property(context);
                        continue;
                    }
                    const instructionAtIndex = instructionsAtIndex[++i];
                    if (typeof context[property] === "function") {
                        context[property](...instructionAtIndex);
                    } else {
                        if (typeof instructionAtIndex === "function") {
                            context[property] = instructionAtIndex(context);
                            continue;
                        }
                        context[property] = instructionAtIndex;
                    }
                }
            });
        }
        clear() {
            this.instructions_.length = 0;
            this.zIndex = 0;
            this.offset_ = 0;
        }
        offset() {
            this.offset_ = this.instructions_.length;
            this.zIndex = 0;
        }
    }
    const canvasPool$1 = [];
    let pixelContext = null;
    function createPixelContext() {
        pixelContext = createCanvasContext2D(1, 1, undefined, {
            willReadFrequently: true
        });
    }
    class CanvasLayerRenderer extends LayerRenderer {
        constructor(layer) {
            super(layer);
            this.container = null;
            this.renderedResolution;
            this.tempTransform = create();
            this.pixelTransform = create();
            this.inversePixelTransform = create();
            this.context = null;
            this.deferredContext_ = null;
            this.containerReused = false;
            this.pixelContext_ = null;
            this.frameState = null;
        }
        getImageData(image, col, row) {
            if (!pixelContext) {
                createPixelContext();
            }
            pixelContext.clearRect(0, 0, 1, 1);
            let data;
            try {
                pixelContext.drawImage(image, col, row, 1, 1, 0, 0, 1, 1);
                data = pixelContext.getImageData(0, 0, 1, 1).data;
            } catch (err) {
                pixelContext = null;
                return null;
            }
            return data;
        }
        getBackground(frameState) {
            const layer = this.getLayer();
            let background = layer.getBackground();
            if (typeof background === "function") {
                background = background(frameState.viewState.resolution);
            }
            return background || undefined;
        }
        useContainer(target, transform, backgroundColor) {
            const layerClassName = this.getLayer().getClassName();
            let container, context;
            if (target && target.className === layerClassName && (!backgroundColor || target && target.style.backgroundColor && equals$2(asArray(target.style.backgroundColor), asArray(backgroundColor)))) {
                const canvas = target.firstElementChild;
                if (canvas instanceof HTMLCanvasElement) {
                    context = canvas.getContext("2d");
                }
            }
            if (context && context.canvas.style.transform === transform) {
                this.container = target;
                this.context = context;
                this.containerReused = true;
            } else if (this.containerReused) {
                this.container = null;
                this.context = null;
                this.containerReused = false;
            } else if (this.container) {
                this.container.style.backgroundColor = null;
            }
            if (!this.container) {
                container = document.createElement("div");
                container.className = layerClassName;
                let style = container.style;
                style.position = "absolute";
                style.width = "100%";
                style.height = "100%";
                context = createCanvasContext2D();
                const canvas = context.canvas;
                container.appendChild(canvas);
                style = canvas.style;
                style.position = "absolute";
                style.left = "0";
                style.transformOrigin = "top left";
                this.container = container;
                this.context = context;
            }
            if (!this.containerReused && backgroundColor && !this.container.style.backgroundColor) {
                this.container.style.backgroundColor = backgroundColor;
            }
        }
        clipUnrotated(context, frameState, extent) {
            const topLeft = getTopLeft(extent);
            const topRight = getTopRight(extent);
            const bottomRight = getBottomRight(extent);
            const bottomLeft = getBottomLeft(extent);
            apply(frameState.coordinateToPixelTransform, topLeft);
            apply(frameState.coordinateToPixelTransform, topRight);
            apply(frameState.coordinateToPixelTransform, bottomRight);
            apply(frameState.coordinateToPixelTransform, bottomLeft);
            const inverted = this.inversePixelTransform;
            apply(inverted, topLeft);
            apply(inverted, topRight);
            apply(inverted, bottomRight);
            apply(inverted, bottomLeft);
            context.save();
            context.beginPath();
            context.moveTo(Math.round(topLeft[0]), Math.round(topLeft[1]));
            context.lineTo(Math.round(topRight[0]), Math.round(topRight[1]));
            context.lineTo(Math.round(bottomRight[0]), Math.round(bottomRight[1]));
            context.lineTo(Math.round(bottomLeft[0]), Math.round(bottomLeft[1]));
            context.clip();
        }
        prepareContainer(frameState, target) {
            const extent = frameState.extent;
            const resolution = frameState.viewState.resolution;
            const rotation = frameState.viewState.rotation;
            const pixelRatio = frameState.pixelRatio;
            const width = Math.round(getWidth(extent) / resolution * pixelRatio);
            const height = Math.round(getHeight(extent) / resolution * pixelRatio);
            compose(this.pixelTransform, frameState.size[0] / 2, frameState.size[1] / 2, 1 / pixelRatio, 1 / pixelRatio, rotation, -width / 2, -height / 2);
            makeInverse(this.inversePixelTransform, this.pixelTransform);
            const canvasTransform = toString$1(this.pixelTransform);
            this.useContainer(target, canvasTransform, this.getBackground(frameState));
            if (!this.containerReused) {
                const canvas = this.context.canvas;
                if (canvas.width != width || canvas.height != height) {
                    canvas.width = width;
                    canvas.height = height;
                } else {
                    this.context.clearRect(0, 0, width, height);
                }
                if (canvasTransform !== canvas.style.transform) {
                    canvas.style.transform = canvasTransform;
                }
            }
        }
        dispatchRenderEvent_(type, context, frameState) {
            const layer = this.getLayer();
            if (layer.hasListener(type)) {
                const event = new RenderEvent(type, this.inversePixelTransform, frameState, context);
                layer.dispatchEvent(event);
            }
        }
        preRender(context, frameState) {
            this.frameState = frameState;
            if (frameState.declutter) {
                return;
            }
            this.dispatchRenderEvent_(RenderEventType.PRERENDER, context, frameState);
        }
        postRender(context, frameState) {
            if (frameState.declutter) {
                return;
            }
            this.dispatchRenderEvent_(RenderEventType.POSTRENDER, context, frameState);
        }
        renderDeferredInternal(frameState) {}
        getRenderContext(frameState) {
            if (frameState.declutter && !this.deferredContext_) {
                this.deferredContext_ = new ZIndexContext;
            }
            return frameState.declutter ? this.deferredContext_.getContext() : this.context;
        }
        renderDeferred(frameState) {
            if (!frameState.declutter) {
                return;
            }
            this.dispatchRenderEvent_(RenderEventType.PRERENDER, this.context, frameState);
            if (frameState.declutter && this.deferredContext_) {
                this.deferredContext_.draw(this.context);
                this.deferredContext_.clear();
            }
            this.renderDeferredInternal(frameState);
            this.dispatchRenderEvent_(RenderEventType.POSTRENDER, this.context, frameState);
        }
        getRenderTransform(center, resolution, rotation, pixelRatio, width, height, offsetX) {
            const dx1 = width / 2;
            const dy1 = height / 2;
            const sx = pixelRatio / resolution;
            const sy = -sx;
            const dx2 = -center[0] + offsetX;
            const dy2 = -center[1];
            return compose(this.tempTransform, dx1, dy1, sx, sy, -rotation, dx2, dy2);
        }
        disposeInternal() {
            delete this.frameState;
            super.disposeInternal();
        }
    }
    class Tile extends Target {
        constructor(tileCoord, state, options) {
            super();
            options = options ? options : {};
            this.tileCoord = tileCoord;
            this.state = state;
            this.interimTile = null;
            this.key = "";
            this.transition_ = options.transition === undefined ? 250 : options.transition;
            this.transitionStarts_ = {};
            this.interpolate = !!options.interpolate;
        }
        changed() {
            this.dispatchEvent(EventType.CHANGE);
        }
        release() {
            if (this.state === TileState.ERROR) {
                this.setState(TileState.EMPTY);
            }
        }
        getKey() {
            return this.key + "/" + this.tileCoord;
        }
        getInterimTile() {
            let tile = this.interimTile;
            if (!tile) {
                return this;
            }
            do {
                if (tile.getState() == TileState.LOADED) {
                    this.transition_ = 0;
                    return tile;
                }
                tile = tile.interimTile;
            } while (tile);
            return this;
        }
        refreshInterimChain() {
            let tile = this.interimTile;
            if (!tile) {
                return;
            }
            let prev = this;
            do {
                if (tile.getState() == TileState.LOADED) {
                    tile.interimTile = null;
                    break;
                }
                if (tile.getState() == TileState.LOADING) {
                    prev = tile;
                } else if (tile.getState() == TileState.IDLE) {
                    prev.interimTile = tile.interimTile;
                } else {
                    prev = tile;
                }
                tile = prev.interimTile;
            } while (tile);
        }
        getTileCoord() {
            return this.tileCoord;
        }
        getState() {
            return this.state;
        }
        setState(state) {
            if (this.state !== TileState.ERROR && this.state > state) {
                throw new Error("Tile load sequence violation");
            }
            this.state = state;
            this.changed();
        }
        load() {
            abstract();
        }
        getAlpha(id, time) {
            if (!this.transition_) {
                return 1;
            }
            let start = this.transitionStarts_[id];
            if (!start) {
                start = time;
                this.transitionStarts_[id] = start;
            } else if (start === -1) {
                return 1;
            }
            const delta = time - start + 1e3 / 60;
            if (delta >= this.transition_) {
                return 1;
            }
            return easeIn(delta / this.transition_);
        }
        inTransition(id) {
            if (!this.transition_) {
                return false;
            }
            return this.transitionStarts_[id] !== -1;
        }
        endTransition(id) {
            if (this.transition_) {
                this.transitionStarts_[id] = -1;
            }
        }
    }
    class ImageTile extends Tile {
        constructor(tileCoord, state, src, crossOrigin, tileLoadFunction, options) {
            super(tileCoord, state, options);
            this.crossOrigin_ = crossOrigin;
            this.src_ = src;
            this.key = src;
            this.image_ = new Image;
            if (crossOrigin !== null) {
                this.image_.crossOrigin = crossOrigin;
            }
            this.unlisten_ = null;
            this.tileLoadFunction_ = tileLoadFunction;
        }
        getImage() {
            return this.image_;
        }
        setImage(element) {
            this.image_ = element;
            this.state = TileState.LOADED;
            this.unlistenImage_();
            this.changed();
        }
        handleImageError_() {
            this.state = TileState.ERROR;
            this.unlistenImage_();
            this.image_ = getBlankImage();
            this.changed();
        }
        handleImageLoad_() {
            const image = this.image_;
            if (image.naturalWidth && image.naturalHeight) {
                this.state = TileState.LOADED;
            } else {
                this.state = TileState.EMPTY;
            }
            this.unlistenImage_();
            this.changed();
        }
        load() {
            if (this.state == TileState.ERROR) {
                this.state = TileState.IDLE;
                this.image_ = new Image;
                if (this.crossOrigin_ !== null) {
                    this.image_.crossOrigin = this.crossOrigin_;
                }
            }
            if (this.state == TileState.IDLE) {
                this.state = TileState.LOADING;
                this.changed();
                this.tileLoadFunction_(this, this.src_);
                this.unlisten_ = listenImage(this.image_, this.handleImageLoad_.bind(this), this.handleImageError_.bind(this));
            }
        }
        unlistenImage_() {
            if (this.unlisten_) {
                this.unlisten_();
                this.unlisten_ = null;
            }
        }
    }
    function getBlankImage() {
        const ctx = createCanvasContext2D(1, 1);
        ctx.fillStyle = "rgba(0,0,0,0)";
        ctx.fillRect(0, 0, 1, 1);
        return ctx.canvas;
    }
    const ERROR_THRESHOLD = .5;
    const MAX_SUBDIVISION = 10;
    const MAX_TRIANGLE_WIDTH = .25;
    class Triangulation {
        constructor(sourceProj, targetProj, targetExtent, maxSourceExtent, errorThreshold, destinationResolution) {
            this.sourceProj_ = sourceProj;
            this.targetProj_ = targetProj;
            let transformInvCache = {};
            const transformInv = getTransform(this.targetProj_, this.sourceProj_);
            this.transformInv_ = function(c) {
                const key = c[0] + "/" + c[1];
                if (!transformInvCache[key]) {
                    transformInvCache[key] = transformInv(c);
                }
                return transformInvCache[key];
            };
            this.maxSourceExtent_ = maxSourceExtent;
            this.errorThresholdSquared_ = errorThreshold * errorThreshold;
            this.triangles_ = [];
            this.wrapsXInSource_ = false;
            this.canWrapXInSource_ = this.sourceProj_.canWrapX() && !!maxSourceExtent && !!this.sourceProj_.getExtent() && getWidth(maxSourceExtent) >= getWidth(this.sourceProj_.getExtent());
            this.sourceWorldWidth_ = this.sourceProj_.getExtent() ? getWidth(this.sourceProj_.getExtent()) : null;
            this.targetWorldWidth_ = this.targetProj_.getExtent() ? getWidth(this.targetProj_.getExtent()) : null;
            const destinationTopLeft = getTopLeft(targetExtent);
            const destinationTopRight = getTopRight(targetExtent);
            const destinationBottomRight = getBottomRight(targetExtent);
            const destinationBottomLeft = getBottomLeft(targetExtent);
            const sourceTopLeft = this.transformInv_(destinationTopLeft);
            const sourceTopRight = this.transformInv_(destinationTopRight);
            const sourceBottomRight = this.transformInv_(destinationBottomRight);
            const sourceBottomLeft = this.transformInv_(destinationBottomLeft);
            const maxSubdivision = MAX_SUBDIVISION + (destinationResolution ? Math.max(0, Math.ceil(Math.log2(getArea(targetExtent) / (destinationResolution * destinationResolution * 256 * 256)))) : 0);
            this.addQuad_(destinationTopLeft, destinationTopRight, destinationBottomRight, destinationBottomLeft, sourceTopLeft, sourceTopRight, sourceBottomRight, sourceBottomLeft, maxSubdivision);
            if (this.wrapsXInSource_) {
                let leftBound = Infinity;
                this.triangles_.forEach(function(triangle, i, arr) {
                    leftBound = Math.min(leftBound, triangle.source[0][0], triangle.source[1][0], triangle.source[2][0]);
                });
                this.triangles_.forEach(triangle => {
                    if (Math.max(triangle.source[0][0], triangle.source[1][0], triangle.source[2][0]) - leftBound > this.sourceWorldWidth_ / 2) {
                        const newTriangle = [ [ triangle.source[0][0], triangle.source[0][1] ], [ triangle.source[1][0], triangle.source[1][1] ], [ triangle.source[2][0], triangle.source[2][1] ] ];
                        if (newTriangle[0][0] - leftBound > this.sourceWorldWidth_ / 2) {
                            newTriangle[0][0] -= this.sourceWorldWidth_;
                        }
                        if (newTriangle[1][0] - leftBound > this.sourceWorldWidth_ / 2) {
                            newTriangle[1][0] -= this.sourceWorldWidth_;
                        }
                        if (newTriangle[2][0] - leftBound > this.sourceWorldWidth_ / 2) {
                            newTriangle[2][0] -= this.sourceWorldWidth_;
                        }
                        const minX = Math.min(newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]);
                        const maxX = Math.max(newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]);
                        if (maxX - minX < this.sourceWorldWidth_ / 2) {
                            triangle.source = newTriangle;
                        }
                    }
                });
            }
            transformInvCache = {};
        }
        addTriangle_(a, b, c, aSrc, bSrc, cSrc) {
            this.triangles_.push({
                source: [ aSrc, bSrc, cSrc ],
                target: [ a, b, c ]
            });
        }
        addQuad_(a, b, c, d, aSrc, bSrc, cSrc, dSrc, maxSubdivision) {
            const sourceQuadExtent = boundingExtent([ aSrc, bSrc, cSrc, dSrc ]);
            const sourceCoverageX = this.sourceWorldWidth_ ? getWidth(sourceQuadExtent) / this.sourceWorldWidth_ : null;
            const sourceWorldWidth = this.sourceWorldWidth_;
            const wrapsX = this.sourceProj_.canWrapX() && sourceCoverageX > .5 && sourceCoverageX < 1;
            let needsSubdivision = false;
            if (maxSubdivision > 0) {
                if (this.targetProj_.isGlobal() && this.targetWorldWidth_) {
                    const targetQuadExtent = boundingExtent([ a, b, c, d ]);
                    const targetCoverageX = getWidth(targetQuadExtent) / this.targetWorldWidth_;
                    needsSubdivision = targetCoverageX > MAX_TRIANGLE_WIDTH || needsSubdivision;
                }
                if (!wrapsX && this.sourceProj_.isGlobal() && sourceCoverageX) {
                    needsSubdivision = sourceCoverageX > MAX_TRIANGLE_WIDTH || needsSubdivision;
                }
            }
            if (!needsSubdivision && this.maxSourceExtent_) {
                if (isFinite(sourceQuadExtent[0]) && isFinite(sourceQuadExtent[1]) && isFinite(sourceQuadExtent[2]) && isFinite(sourceQuadExtent[3])) {
                    if (!intersects$1(sourceQuadExtent, this.maxSourceExtent_)) {
                        return;
                    }
                }
            }
            let isNotFinite = 0;
            if (!needsSubdivision) {
                if (!isFinite(aSrc[0]) || !isFinite(aSrc[1]) || !isFinite(bSrc[0]) || !isFinite(bSrc[1]) || !isFinite(cSrc[0]) || !isFinite(cSrc[1]) || !isFinite(dSrc[0]) || !isFinite(dSrc[1])) {
                    if (maxSubdivision > 0) {
                        needsSubdivision = true;
                    } else {
                        isNotFinite = (!isFinite(aSrc[0]) || !isFinite(aSrc[1]) ? 8 : 0) + (!isFinite(bSrc[0]) || !isFinite(bSrc[1]) ? 4 : 0) + (!isFinite(cSrc[0]) || !isFinite(cSrc[1]) ? 2 : 0) + (!isFinite(dSrc[0]) || !isFinite(dSrc[1]) ? 1 : 0);
                        if (isNotFinite != 1 && isNotFinite != 2 && isNotFinite != 4 && isNotFinite != 8) {
                            return;
                        }
                    }
                }
            }
            if (maxSubdivision > 0) {
                if (!needsSubdivision) {
                    const center = [ (a[0] + c[0]) / 2, (a[1] + c[1]) / 2 ];
                    const centerSrc = this.transformInv_(center);
                    let dx;
                    if (wrapsX) {
                        const centerSrcEstimX = (modulo(aSrc[0], sourceWorldWidth) + modulo(cSrc[0], sourceWorldWidth)) / 2;
                        dx = centerSrcEstimX - modulo(centerSrc[0], sourceWorldWidth);
                    } else {
                        dx = (aSrc[0] + cSrc[0]) / 2 - centerSrc[0];
                    }
                    const dy = (aSrc[1] + cSrc[1]) / 2 - centerSrc[1];
                    const centerSrcErrorSquared = dx * dx + dy * dy;
                    needsSubdivision = centerSrcErrorSquared > this.errorThresholdSquared_;
                }
                if (needsSubdivision) {
                    if (Math.abs(a[0] - c[0]) <= Math.abs(a[1] - c[1])) {
                        const bc = [ (b[0] + c[0]) / 2, (b[1] + c[1]) / 2 ];
                        const bcSrc = this.transformInv_(bc);
                        const da = [ (d[0] + a[0]) / 2, (d[1] + a[1]) / 2 ];
                        const daSrc = this.transformInv_(da);
                        this.addQuad_(a, b, bc, da, aSrc, bSrc, bcSrc, daSrc, maxSubdivision - 1);
                        this.addQuad_(da, bc, c, d, daSrc, bcSrc, cSrc, dSrc, maxSubdivision - 1);
                    } else {
                        const ab = [ (a[0] + b[0]) / 2, (a[1] + b[1]) / 2 ];
                        const abSrc = this.transformInv_(ab);
                        const cd = [ (c[0] + d[0]) / 2, (c[1] + d[1]) / 2 ];
                        const cdSrc = this.transformInv_(cd);
                        this.addQuad_(a, ab, cd, d, aSrc, abSrc, cdSrc, dSrc, maxSubdivision - 1);
                        this.addQuad_(ab, b, c, cd, abSrc, bSrc, cSrc, cdSrc, maxSubdivision - 1);
                    }
                    return;
                }
            }
            if (wrapsX) {
                if (!this.canWrapXInSource_) {
                    return;
                }
                this.wrapsXInSource_ = true;
            }
            if ((isNotFinite & 11) == 0) {
                this.addTriangle_(a, c, d, aSrc, cSrc, dSrc);
            }
            if ((isNotFinite & 14) == 0) {
                this.addTriangle_(a, c, b, aSrc, cSrc, bSrc);
            }
            if (isNotFinite) {
                if ((isNotFinite & 13) == 0) {
                    this.addTriangle_(b, d, a, bSrc, dSrc, aSrc);
                }
                if ((isNotFinite & 7) == 0) {
                    this.addTriangle_(b, d, c, bSrc, dSrc, cSrc);
                }
            }
        }
        calculateSourceExtent() {
            const extent = createEmpty();
            this.triangles_.forEach(function(triangle, i, arr) {
                const src = triangle.source;
                extendCoordinate(extent, src[0]);
                extendCoordinate(extent, src[1]);
                extendCoordinate(extent, src[2]);
            });
            return extent;
        }
        getTriangles() {
            return this.triangles_;
        }
    }
    let brokenDiagonalRendering_;
    const canvasPool = [];
    function drawTestTriangle(ctx, u1, v1, u2, v2) {
        ctx.beginPath();
        ctx.moveTo(0, 0);
        ctx.lineTo(u1, v1);
        ctx.lineTo(u2, v2);
        ctx.closePath();
        ctx.save();
        ctx.clip();
        ctx.fillRect(0, 0, Math.max(u1, u2) + 1, Math.max(v1, v2));
        ctx.restore();
    }
    function verifyBrokenDiagonalRendering(data, offset) {
        return Math.abs(data[offset * 4] - 210) > 2 || Math.abs(data[offset * 4 + 3] - .75 * 255) > 2;
    }
    function isBrokenDiagonalRendering() {
        if (brokenDiagonalRendering_ === undefined) {
            const ctx = createCanvasContext2D(6, 6, canvasPool);
            ctx.globalCompositeOperation = "lighter";
            ctx.fillStyle = "rgba(210, 0, 0, 0.75)";
            drawTestTriangle(ctx, 4, 5, 4, 0);
            drawTestTriangle(ctx, 4, 5, 0, 5);
            const data = ctx.getImageData(0, 0, 3, 3).data;
            brokenDiagonalRendering_ = verifyBrokenDiagonalRendering(data, 0) || verifyBrokenDiagonalRendering(data, 4) || verifyBrokenDiagonalRendering(data, 8);
            releaseCanvas(ctx);
            canvasPool.push(ctx.canvas);
        }
        return brokenDiagonalRendering_;
    }
    function calculateSourceResolution(sourceProj, targetProj, targetCenter, targetResolution) {
        const sourceCenter = transform(targetCenter, targetProj, sourceProj);
        let sourceResolution = getPointResolution(targetProj, targetResolution, targetCenter);
        const targetMetersPerUnit = targetProj.getMetersPerUnit();
        if (targetMetersPerUnit !== undefined) {
            sourceResolution *= targetMetersPerUnit;
        }
        const sourceMetersPerUnit = sourceProj.getMetersPerUnit();
        if (sourceMetersPerUnit !== undefined) {
            sourceResolution /= sourceMetersPerUnit;
        }
        const sourceExtent = sourceProj.getExtent();
        if (!sourceExtent || containsCoordinate(sourceExtent, sourceCenter)) {
            const compensationFactor = getPointResolution(sourceProj, sourceResolution, sourceCenter) / sourceResolution;
            if (isFinite(compensationFactor) && compensationFactor > 0) {
                sourceResolution /= compensationFactor;
            }
        }
        return sourceResolution;
    }
    function calculateSourceExtentResolution(sourceProj, targetProj, targetExtent, targetResolution) {
        const targetCenter = getCenter(targetExtent);
        let sourceResolution = calculateSourceResolution(sourceProj, targetProj, targetCenter, targetResolution);
        if (!isFinite(sourceResolution) || sourceResolution <= 0) {
            forEachCorner(targetExtent, function(corner) {
                sourceResolution = calculateSourceResolution(sourceProj, targetProj, corner, targetResolution);
                return isFinite(sourceResolution) && sourceResolution > 0;
            });
        }
        return sourceResolution;
    }
    function render(width, height, pixelRatio, sourceResolution, sourceExtent, targetResolution, targetExtent, triangulation, sources, gutter, renderEdges, interpolate, drawSingle, clipExtent) {
        const context = createCanvasContext2D(Math.round(pixelRatio * width), Math.round(pixelRatio * height), canvasPool);
        if (!interpolate) {
            context.imageSmoothingEnabled = false;
        }
        if (sources.length === 0) {
            return context.canvas;
        }
        context.scale(pixelRatio, pixelRatio);
        function pixelRound(value) {
            return Math.round(value * pixelRatio) / pixelRatio;
        }
        context.globalCompositeOperation = "lighter";
        const sourceDataExtent = createEmpty();
        sources.forEach(function(src, i, arr) {
            extend$1(sourceDataExtent, src.extent);
        });
        let stitchContext;
        const stitchScale = pixelRatio / sourceResolution;
        const inverseScale = (interpolate ? 1 : 1 + Math.pow(2, -24)) / stitchScale;
        {
            stitchContext = createCanvasContext2D(Math.round(getWidth(sourceDataExtent) * stitchScale), Math.round(getHeight(sourceDataExtent) * stitchScale), canvasPool);
            if (!interpolate) {
                stitchContext.imageSmoothingEnabled = false;
            }
            sources.forEach(function(src, i, arr) {
                if (src.image.width > 0 && src.image.height > 0) {
                    if (src.clipExtent) {
                        stitchContext.save();
                        const xPos = (src.clipExtent[0] - sourceDataExtent[0]) * stitchScale;
                        const yPos = -(src.clipExtent[3] - sourceDataExtent[3]) * stitchScale;
                        const width = getWidth(src.clipExtent) * stitchScale;
                        const height = getHeight(src.clipExtent) * stitchScale;
                        stitchContext.rect(interpolate ? xPos : Math.round(xPos), interpolate ? yPos : Math.round(yPos), interpolate ? width : Math.round(xPos + width) - Math.round(xPos), interpolate ? height : Math.round(yPos + height) - Math.round(yPos));
                        stitchContext.clip();
                    }
                    const xPos = (src.extent[0] - sourceDataExtent[0]) * stitchScale;
                    const yPos = -(src.extent[3] - sourceDataExtent[3]) * stitchScale;
                    const srcWidth = getWidth(src.extent) * stitchScale;
                    const srcHeight = getHeight(src.extent) * stitchScale;
                    stitchContext.drawImage(src.image, gutter, gutter, src.image.width - 2 * gutter, src.image.height - 2 * gutter, interpolate ? xPos : Math.round(xPos), interpolate ? yPos : Math.round(yPos), interpolate ? srcWidth : Math.round(xPos + srcWidth) - Math.round(xPos), interpolate ? srcHeight : Math.round(yPos + srcHeight) - Math.round(yPos));
                    if (src.clipExtent) {
                        stitchContext.restore();
                    }
                }
            });
        }
        const targetTopLeft = getTopLeft(targetExtent);
        triangulation.getTriangles().forEach(function(triangle, i, arr) {
            const source = triangle.source;
            const target = triangle.target;
            let x0 = source[0][0], y0 = source[0][1];
            let x1 = source[1][0], y1 = source[1][1];
            let x2 = source[2][0], y2 = source[2][1];
            const u0 = pixelRound((target[0][0] - targetTopLeft[0]) / targetResolution);
            const v0 = pixelRound(-(target[0][1] - targetTopLeft[1]) / targetResolution);
            const u1 = pixelRound((target[1][0] - targetTopLeft[0]) / targetResolution);
            const v1 = pixelRound(-(target[1][1] - targetTopLeft[1]) / targetResolution);
            const u2 = pixelRound((target[2][0] - targetTopLeft[0]) / targetResolution);
            const v2 = pixelRound(-(target[2][1] - targetTopLeft[1]) / targetResolution);
            const sourceNumericalShiftX = x0;
            const sourceNumericalShiftY = y0;
            x0 = 0;
            y0 = 0;
            x1 -= sourceNumericalShiftX;
            y1 -= sourceNumericalShiftY;
            x2 -= sourceNumericalShiftX;
            y2 -= sourceNumericalShiftY;
            const augmentedMatrix = [ [ x1, y1, 0, 0, u1 - u0 ], [ x2, y2, 0, 0, u2 - u0 ], [ 0, 0, x1, y1, v1 - v0 ], [ 0, 0, x2, y2, v2 - v0 ] ];
            const affineCoefs = solveLinearSystem(augmentedMatrix);
            if (!affineCoefs) {
                return;
            }
            context.save();
            context.beginPath();
            if (isBrokenDiagonalRendering() || !interpolate) {
                context.moveTo(u1, v1);
                const steps = 4;
                const ud = u0 - u1;
                const vd = v0 - v1;
                for (let step = 0; step < steps; step++) {
                    context.lineTo(u1 + pixelRound((step + 1) * ud / steps), v1 + pixelRound(step * vd / (steps - 1)));
                    if (step != steps - 1) {
                        context.lineTo(u1 + pixelRound((step + 1) * ud / steps), v1 + pixelRound((step + 1) * vd / (steps - 1)));
                    }
                }
                context.lineTo(u2, v2);
            } else {
                context.moveTo(u1, v1);
                context.lineTo(u0, v0);
                context.lineTo(u2, v2);
            }
            context.clip();
            context.transform(affineCoefs[0], affineCoefs[2], affineCoefs[1], affineCoefs[3], u0, v0);
            context.translate(sourceDataExtent[0] - sourceNumericalShiftX, sourceDataExtent[3] - sourceNumericalShiftY);
            let image;
            if (stitchContext) {
                image = stitchContext.canvas;
                context.scale(inverseScale, -inverseScale);
            } else {
                const source = sources[0];
                const extent = source.extent;
                image = source.image;
                context.scale(getWidth(extent) / image.width, -getHeight(extent) / image.height);
            }
            context.drawImage(image, 0, 0);
            context.restore();
        });
        if (stitchContext) {
            releaseCanvas(stitchContext);
            canvasPool.push(stitchContext.canvas);
        }
        if (renderEdges) {
            context.save();
            context.globalCompositeOperation = "source-over";
            context.strokeStyle = "black";
            context.lineWidth = 1;
            triangulation.getTriangles().forEach(function(triangle, i, arr) {
                const target = triangle.target;
                const u0 = (target[0][0] - targetTopLeft[0]) / targetResolution;
                const v0 = -(target[0][1] - targetTopLeft[1]) / targetResolution;
                const u1 = (target[1][0] - targetTopLeft[0]) / targetResolution;
                const v1 = -(target[1][1] - targetTopLeft[1]) / targetResolution;
                const u2 = (target[2][0] - targetTopLeft[0]) / targetResolution;
                const v2 = -(target[2][1] - targetTopLeft[1]) / targetResolution;
                context.beginPath();
                context.moveTo(u1, v1);
                context.lineTo(u0, v0);
                context.lineTo(u2, v2);
                context.closePath();
                context.stroke();
            });
            context.restore();
        }
        return context.canvas;
    }
    class ReprojTile extends Tile {
        constructor(sourceProj, sourceTileGrid, targetProj, targetTileGrid, tileCoord, wrappedTileCoord, pixelRatio, gutter, getTileFunction, errorThreshold, renderEdges, options) {
            super(tileCoord, TileState.IDLE, options);
            this.renderEdges_ = renderEdges !== undefined ? renderEdges : false;
            this.pixelRatio_ = pixelRatio;
            this.gutter_ = gutter;
            this.canvas_ = null;
            this.sourceTileGrid_ = sourceTileGrid;
            this.targetTileGrid_ = targetTileGrid;
            this.wrappedTileCoord_ = wrappedTileCoord ? wrappedTileCoord : tileCoord;
            this.sourceTiles_ = [];
            this.sourcesListenerKeys_ = null;
            this.sourceZ_ = 0;
            this.clipExtent_ = sourceProj.canWrapX() ? sourceProj.getExtent() : undefined;
            const targetExtent = targetTileGrid.getTileCoordExtent(this.wrappedTileCoord_);
            const maxTargetExtent = this.targetTileGrid_.getExtent();
            let maxSourceExtent = this.sourceTileGrid_.getExtent();
            const limitedTargetExtent = maxTargetExtent ? getIntersection(targetExtent, maxTargetExtent) : targetExtent;
            if (getArea(limitedTargetExtent) === 0) {
                this.state = TileState.EMPTY;
                return;
            }
            const sourceProjExtent = sourceProj.getExtent();
            if (sourceProjExtent) {
                if (!maxSourceExtent) {
                    maxSourceExtent = sourceProjExtent;
                } else {
                    maxSourceExtent = getIntersection(maxSourceExtent, sourceProjExtent);
                }
            }
            const targetResolution = targetTileGrid.getResolution(this.wrappedTileCoord_[0]);
            const sourceResolution = calculateSourceExtentResolution(sourceProj, targetProj, limitedTargetExtent, targetResolution);
            if (!isFinite(sourceResolution) || sourceResolution <= 0) {
                this.state = TileState.EMPTY;
                return;
            }
            const errorThresholdInPixels = errorThreshold !== undefined ? errorThreshold : ERROR_THRESHOLD;
            this.triangulation_ = new Triangulation(sourceProj, targetProj, limitedTargetExtent, maxSourceExtent, sourceResolution * errorThresholdInPixels, targetResolution);
            if (this.triangulation_.getTriangles().length === 0) {
                this.state = TileState.EMPTY;
                return;
            }
            this.sourceZ_ = sourceTileGrid.getZForResolution(sourceResolution);
            let sourceExtent = this.triangulation_.calculateSourceExtent();
            if (maxSourceExtent) {
                if (sourceProj.canWrapX()) {
                    sourceExtent[1] = clamp(sourceExtent[1], maxSourceExtent[1], maxSourceExtent[3]);
                    sourceExtent[3] = clamp(sourceExtent[3], maxSourceExtent[1], maxSourceExtent[3]);
                } else {
                    sourceExtent = getIntersection(sourceExtent, maxSourceExtent);
                }
            }
            if (!getArea(sourceExtent)) {
                this.state = TileState.EMPTY;
            } else {
                let worldWidth = 0;
                let worldsAway = 0;
                if (sourceProj.canWrapX()) {
                    worldWidth = getWidth(sourceProjExtent);
                    worldsAway = Math.floor((sourceExtent[0] - sourceProjExtent[0]) / worldWidth);
                }
                const sourceExtents = wrapAndSliceX(sourceExtent.slice(), sourceProj, true);
                sourceExtents.forEach(extent => {
                    const sourceRange = sourceTileGrid.getTileRangeForExtentAndZ(extent, this.sourceZ_);
                    for (let srcX = sourceRange.minX; srcX <= sourceRange.maxX; srcX++) {
                        for (let srcY = sourceRange.minY; srcY <= sourceRange.maxY; srcY++) {
                            const tile = getTileFunction(this.sourceZ_, srcX, srcY, pixelRatio);
                            if (tile) {
                                const offset = worldsAway * worldWidth;
                                this.sourceTiles_.push({
                                    tile: tile,
                                    offset: offset
                                });
                            }
                        }
                    }
                    ++worldsAway;
                });
                if (this.sourceTiles_.length === 0) {
                    this.state = TileState.EMPTY;
                }
            }
        }
        getImage() {
            return this.canvas_;
        }
        reproject_() {
            const sources = [];
            this.sourceTiles_.forEach(source => {
                const tile = source.tile;
                if (tile && tile.getState() == TileState.LOADED) {
                    const extent = this.sourceTileGrid_.getTileCoordExtent(tile.tileCoord);
                    extent[0] += source.offset;
                    extent[2] += source.offset;
                    const clipExtent = this.clipExtent_?.slice();
                    if (clipExtent) {
                        clipExtent[0] += source.offset;
                        clipExtent[2] += source.offset;
                    }
                    sources.push({
                        extent: extent,
                        clipExtent: clipExtent,
                        image: tile.getImage()
                    });
                }
            });
            this.sourceTiles_.length = 0;
            if (sources.length === 0) {
                this.state = TileState.ERROR;
            } else {
                const z = this.wrappedTileCoord_[0];
                const size = this.targetTileGrid_.getTileSize(z);
                const width = typeof size === "number" ? size : size[0];
                const height = typeof size === "number" ? size : size[1];
                const targetResolution = this.targetTileGrid_.getResolution(z);
                const sourceResolution = this.sourceTileGrid_.getResolution(this.sourceZ_);
                const targetExtent = this.targetTileGrid_.getTileCoordExtent(this.wrappedTileCoord_);
                this.canvas_ = render(width, height, this.pixelRatio_, sourceResolution, this.sourceTileGrid_.getExtent(), targetResolution, targetExtent, this.triangulation_, sources, this.gutter_, this.renderEdges_, this.interpolate);
                this.state = TileState.LOADED;
            }
            this.changed();
        }
        load() {
            if (this.state == TileState.IDLE) {
                this.state = TileState.LOADING;
                this.changed();
                let leftToLoad = 0;
                this.sourcesListenerKeys_ = [];
                this.sourceTiles_.forEach(({tile: tile}) => {
                    const state = tile.getState();
                    if (state == TileState.IDLE || state == TileState.LOADING) {
                        leftToLoad++;
                        const sourceListenKey = listen(tile, EventType.CHANGE, function(e) {
                            const state = tile.getState();
                            if (state == TileState.LOADED || state == TileState.ERROR || state == TileState.EMPTY) {
                                unlistenByKey(sourceListenKey);
                                leftToLoad--;
                                if (leftToLoad === 0) {
                                    this.unlistenSources_();
                                    this.reproject_();
                                }
                            }
                        }, this);
                        this.sourcesListenerKeys_.push(sourceListenKey);
                    }
                });
                if (leftToLoad === 0) {
                    setTimeout(this.reproject_.bind(this), 0);
                } else {
                    this.sourceTiles_.forEach(function({tile: tile}, i, arr) {
                        const state = tile.getState();
                        if (state == TileState.IDLE) {
                            tile.load();
                        }
                    });
                }
            }
        }
        unlistenSources_() {
            this.sourcesListenerKeys_.forEach(unlistenByKey);
            this.sourcesListenerKeys_ = null;
        }
        release() {
            if (this.canvas_) {
                releaseCanvas(this.canvas_.getContext("2d"));
                canvasPool.push(this.canvas_);
                this.canvas_ = null;
            }
            super.release();
        }
    }
    class TileRange {
        constructor(minX, maxX, minY, maxY) {
            this.minX = minX;
            this.maxX = maxX;
            this.minY = minY;
            this.maxY = maxY;
        }
        contains(tileCoord) {
            return this.containsXY(tileCoord[1], tileCoord[2]);
        }
        containsTileRange(tileRange) {
            return this.minX <= tileRange.minX && tileRange.maxX <= this.maxX && this.minY <= tileRange.minY && tileRange.maxY <= this.maxY;
        }
        containsXY(x, y) {
            return this.minX <= x && x <= this.maxX && this.minY <= y && y <= this.maxY;
        }
        equals(tileRange) {
            return this.minX == tileRange.minX && this.minY == tileRange.minY && this.maxX == tileRange.maxX && this.maxY == tileRange.maxY;
        }
        extend(tileRange) {
            if (tileRange.minX < this.minX) {
                this.minX = tileRange.minX;
            }
            if (tileRange.maxX > this.maxX) {
                this.maxX = tileRange.maxX;
            }
            if (tileRange.minY < this.minY) {
                this.minY = tileRange.minY;
            }
            if (tileRange.maxY > this.maxY) {
                this.maxY = tileRange.maxY;
            }
        }
        getHeight() {
            return this.maxY - this.minY + 1;
        }
        getSize() {
            return [ this.getWidth(), this.getHeight() ];
        }
        getWidth() {
            return this.maxX - this.minX + 1;
        }
        intersects(tileRange) {
            return this.minX <= tileRange.maxX && this.maxX >= tileRange.minX && this.minY <= tileRange.maxY && this.maxY >= tileRange.minY;
        }
    }
    function createOrUpdate$1(minX, maxX, minY, maxY, tileRange) {
        if (tileRange !== undefined) {
            tileRange.minX = minX;
            tileRange.maxX = maxX;
            tileRange.minY = minY;
            tileRange.maxY = maxY;
            return tileRange;
        }
        return new TileRange(minX, maxX, minY, maxY);
    }
    class CanvasTileLayerRenderer extends CanvasLayerRenderer {
        constructor(tileLayer) {
            super(tileLayer);
            this.extentChanged = true;
            this.renderedExtent_ = null;
            this.renderedPixelRatio;
            this.renderedProjection = null;
            this.renderedRevision;
            this.renderedTiles = [];
            this.newTiles_ = false;
            this.tmpExtent = createEmpty();
            this.tmpTileRange_ = new TileRange(0, 0, 0, 0);
        }
        isDrawableTile(tile) {
            const tileLayer = this.getLayer();
            const tileState = tile.getState();
            const useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
            return tileState == TileState.LOADED || tileState == TileState.EMPTY || tileState == TileState.ERROR && !useInterimTilesOnError;
        }
        getTile(z, x, y, frameState) {
            const pixelRatio = frameState.pixelRatio;
            const projection = frameState.viewState.projection;
            const tileLayer = this.getLayer();
            const tileSource = tileLayer.getSource();
            let tile = tileSource.getTile(z, x, y, pixelRatio, projection);
            if (tile.getState() == TileState.ERROR) {
                if (tileLayer.getUseInterimTilesOnError() && tileLayer.getPreload() > 0) {
                    this.newTiles_ = true;
                }
            }
            if (!this.isDrawableTile(tile)) {
                tile = tile.getInterimTile();
            }
            return tile;
        }
        getData(pixel) {
            const frameState = this.frameState;
            if (!frameState) {
                return null;
            }
            const layer = this.getLayer();
            const coordinate = apply(frameState.pixelToCoordinateTransform, pixel.slice());
            const layerExtent = layer.getExtent();
            if (layerExtent) {
                if (!containsCoordinate(layerExtent, coordinate)) {
                    return null;
                }
            }
            const pixelRatio = frameState.pixelRatio;
            const projection = frameState.viewState.projection;
            const viewState = frameState.viewState;
            const source = layer.getRenderSource();
            const tileGrid = source.getTileGridForProjection(viewState.projection);
            const tilePixelRatio = source.getTilePixelRatio(frameState.pixelRatio);
            for (let z = tileGrid.getZForResolution(viewState.resolution); z >= tileGrid.getMinZoom(); --z) {
                const tileCoord = tileGrid.getTileCoordForCoordAndZ(coordinate, z);
                const tile = source.getTile(z, tileCoord[1], tileCoord[2], pixelRatio, projection);
                if (!(tile instanceof ImageTile || tile instanceof ReprojTile) || tile instanceof ReprojTile && tile.getState() === TileState.EMPTY) {
                    return null;
                }
                if (tile.getState() !== TileState.LOADED) {
                    continue;
                }
                const tileOrigin = tileGrid.getOrigin(z);
                const tileSize = toSize(tileGrid.getTileSize(z));
                const tileResolution = tileGrid.getResolution(z);
                const col = Math.floor(tilePixelRatio * ((coordinate[0] - tileOrigin[0]) / tileResolution - tileCoord[1] * tileSize[0]));
                const row = Math.floor(tilePixelRatio * ((tileOrigin[1] - coordinate[1]) / tileResolution - tileCoord[2] * tileSize[1]));
                const gutter = Math.round(tilePixelRatio * source.getGutterForProjection(viewState.projection));
                return this.getImageData(tile.getImage(), col + gutter, row + gutter);
            }
            return null;
        }
        loadedTileCallback(tiles, zoom, tile) {
            if (this.isDrawableTile(tile)) {
                return super.loadedTileCallback(tiles, zoom, tile);
            }
            return false;
        }
        prepareFrame(frameState) {
            return !!this.getLayer().getSource();
        }
        renderFrame(frameState, target) {
            const layerState = frameState.layerStatesArray[frameState.layerIndex];
            const viewState = frameState.viewState;
            const projection = viewState.projection;
            const viewResolution = viewState.resolution;
            const viewCenter = viewState.center;
            const rotation = viewState.rotation;
            const pixelRatio = frameState.pixelRatio;
            const tileLayer = this.getLayer();
            const tileSource = tileLayer.getSource();
            const sourceRevision = tileSource.getRevision();
            const tileGrid = tileSource.getTileGridForProjection(projection);
            const z = tileGrid.getZForResolution(viewResolution, tileSource.zDirection);
            const tileResolution = tileGrid.getResolution(z);
            let extent = frameState.extent;
            const resolution = frameState.viewState.resolution;
            const tilePixelRatio = tileSource.getTilePixelRatio(pixelRatio);
            this.prepareContainer(frameState, target);
            const width = this.context.canvas.width;
            const height = this.context.canvas.height;
            const layerExtent = layerState.extent && fromUserExtent(layerState.extent);
            if (layerExtent) {
                extent = getIntersection(extent, fromUserExtent(layerState.extent));
            }
            const dx = tileResolution * width / 2 / tilePixelRatio;
            const dy = tileResolution * height / 2 / tilePixelRatio;
            const canvasExtent = [ viewCenter[0] - dx, viewCenter[1] - dy, viewCenter[0] + dx, viewCenter[1] + dy ];
            const tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
            const tilesToDrawByZ = {};
            tilesToDrawByZ[z] = {};
            const findLoadedTiles = this.createLoadedTileFinder(tileSource, projection, tilesToDrawByZ);
            const tmpExtent = this.tmpExtent;
            const tmpTileRange = this.tmpTileRange_;
            this.newTiles_ = false;
            const viewport = rotation ? getRotatedViewport(viewState.center, resolution, rotation, frameState.size) : undefined;
            for (let x = tileRange.minX; x <= tileRange.maxX; ++x) {
                for (let y = tileRange.minY; y <= tileRange.maxY; ++y) {
                    if (rotation && !tileGrid.tileCoordIntersectsViewport([ z, x, y ], viewport)) {
                        continue;
                    }
                    const tile = this.getTile(z, x, y, frameState);
                    if (this.isDrawableTile(tile)) {
                        const uid = getUid(this);
                        if (tile.getState() == TileState.LOADED) {
                            tilesToDrawByZ[z][tile.tileCoord.toString()] = tile;
                            let inTransition = tile.inTransition(uid);
                            if (inTransition && layerState.opacity !== 1) {
                                tile.endTransition(uid);
                                inTransition = false;
                            }
                            if (!this.newTiles_ && (inTransition || !this.renderedTiles.includes(tile))) {
                                this.newTiles_ = true;
                            }
                        }
                        if (tile.getAlpha(uid, frameState.time) === 1) {
                            continue;
                        }
                    }
                    const childTileRange = tileGrid.getTileCoordChildTileRange(tile.tileCoord, tmpTileRange, tmpExtent);
                    let covered = false;
                    if (childTileRange) {
                        covered = findLoadedTiles(z + 1, childTileRange);
                    }
                    if (!covered) {
                        tileGrid.forEachTileCoordParentTileRange(tile.tileCoord, findLoadedTiles, tmpTileRange, tmpExtent);
                    }
                }
            }
            const canvasScale = tileResolution / viewResolution * pixelRatio / tilePixelRatio;
            const context = this.getRenderContext(frameState);
            compose(this.tempTransform, width / 2, height / 2, canvasScale, canvasScale, 0, -width / 2, -height / 2);
            if (layerExtent) {
                this.clipUnrotated(context, frameState, layerExtent);
            }
            if (!tileSource.getInterpolate()) {
                context.imageSmoothingEnabled = false;
            }
            this.preRender(context, frameState);
            this.renderedTiles.length = 0;
            let zs = Object.keys(tilesToDrawByZ).map(Number);
            zs.sort(ascending);
            let clips, clipZs, currentClip;
            if (layerState.opacity === 1 && (!this.containerReused || tileSource.getOpaque(frameState.viewState.projection))) {
                zs = zs.reverse();
            } else {
                clips = [];
                clipZs = [];
            }
            for (let i = zs.length - 1; i >= 0; --i) {
                const currentZ = zs[i];
                const currentTilePixelSize = tileSource.getTilePixelSize(currentZ, pixelRatio, projection);
                const currentResolution = tileGrid.getResolution(currentZ);
                const currentScale = currentResolution / tileResolution;
                const dx = currentTilePixelSize[0] * currentScale * canvasScale;
                const dy = currentTilePixelSize[1] * currentScale * canvasScale;
                const originTileCoord = tileGrid.getTileCoordForCoordAndZ(getTopLeft(canvasExtent), currentZ);
                const originTileExtent = tileGrid.getTileCoordExtent(originTileCoord);
                const origin = apply(this.tempTransform, [ tilePixelRatio * (originTileExtent[0] - canvasExtent[0]) / tileResolution, tilePixelRatio * (canvasExtent[3] - originTileExtent[3]) / tileResolution ]);
                const tileGutter = tilePixelRatio * tileSource.getGutterForProjection(projection);
                const tilesToDraw = tilesToDrawByZ[currentZ];
                for (const tileCoordKey in tilesToDraw) {
                    const tile = tilesToDraw[tileCoordKey];
                    const tileCoord = tile.tileCoord;
                    const xIndex = originTileCoord[1] - tileCoord[1];
                    const nextX = Math.round(origin[0] - (xIndex - 1) * dx);
                    const yIndex = originTileCoord[2] - tileCoord[2];
                    const nextY = Math.round(origin[1] - (yIndex - 1) * dy);
                    const x = Math.round(origin[0] - xIndex * dx);
                    const y = Math.round(origin[1] - yIndex * dy);
                    const w = nextX - x;
                    const h = nextY - y;
                    const transition = z === currentZ;
                    const inTransition = transition && tile.getAlpha(getUid(this), frameState.time) !== 1;
                    let contextSaved = false;
                    if (!inTransition) {
                        if (clips) {
                            currentClip = [ x, y, x + w, y, x + w, y + h, x, y + h ];
                            for (let i = 0, ii = clips.length; i < ii; ++i) {
                                if (z !== currentZ && currentZ < clipZs[i]) {
                                    const clip = clips[i];
                                    if (intersects$1([ x, y, x + w, y + h ], [ clip[0], clip[3], clip[4], clip[7] ])) {
                                        if (!contextSaved) {
                                            context.save();
                                            contextSaved = true;
                                        }
                                        context.beginPath();
                                        context.moveTo(currentClip[0], currentClip[1]);
                                        context.lineTo(currentClip[2], currentClip[3]);
                                        context.lineTo(currentClip[4], currentClip[5]);
                                        context.lineTo(currentClip[6], currentClip[7]);
                                        context.moveTo(clip[6], clip[7]);
                                        context.lineTo(clip[4], clip[5]);
                                        context.lineTo(clip[2], clip[3]);
                                        context.lineTo(clip[0], clip[1]);
                                        context.clip();
                                    }
                                }
                            }
                            clips.push(currentClip);
                            clipZs.push(currentZ);
                        } else {
                            context.clearRect(x, y, w, h);
                        }
                    }
                    this.drawTileImage(tile, frameState, x, y, w, h, tileGutter, transition);
                    if (clips && !inTransition) {
                        if (contextSaved) {
                            context.restore();
                        }
                        this.renderedTiles.unshift(tile);
                    } else {
                        this.renderedTiles.push(tile);
                    }
                    this.updateUsedTiles(frameState.usedTiles, tileSource, tile);
                }
            }
            this.renderedRevision = sourceRevision;
            this.renderedResolution = tileResolution;
            this.extentChanged = !this.renderedExtent_ || !equals$1(this.renderedExtent_, canvasExtent);
            this.renderedExtent_ = canvasExtent;
            this.renderedPixelRatio = pixelRatio;
            this.renderedProjection = projection;
            this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio, projection, extent, z, tileLayer.getPreload());
            this.scheduleExpireCache(frameState, tileSource);
            this.postRender(this.context, frameState);
            if (layerState.extent) {
                context.restore();
            }
            context.imageSmoothingEnabled = true;
            return this.container;
        }
        drawTileImage(tile, frameState, x, y, w, h, gutter, transition) {
            const image = this.getTileImage(tile);
            if (!image) {
                return;
            }
            const context = this.getRenderContext(frameState);
            const uid = getUid(this);
            const layerState = frameState.layerStatesArray[frameState.layerIndex];
            const alpha = layerState.opacity * (transition ? tile.getAlpha(uid, frameState.time) : 1);
            const alphaChanged = alpha !== context.globalAlpha;
            if (alphaChanged) {
                context.save();
                context.globalAlpha = alpha;
            }
            context.drawImage(image, gutter, gutter, image.width - 2 * gutter, image.height - 2 * gutter, x, y, w, h);
            if (alphaChanged) {
                context.restore();
            }
            if (alpha !== layerState.opacity) {
                frameState.animate = true;
            } else if (transition) {
                tile.endTransition(uid);
            }
        }
        getImage() {
            const context = this.context;
            return context ? context.canvas : null;
        }
        getTileImage(tile) {
            return tile.getImage();
        }
        scheduleExpireCache(frameState, tileSource) {
            if (tileSource.canExpireCache()) {
                const postRenderFunction = function(tileSource, map, frameState) {
                    const tileSourceKey = getUid(tileSource);
                    if (tileSourceKey in frameState.usedTiles) {
                        tileSource.expireCache(frameState.viewState.projection, frameState.usedTiles[tileSourceKey]);
                    }
                }.bind(null, tileSource);
                frameState.postRenderFunctions.push(postRenderFunction);
            }
        }
        updateUsedTiles(usedTiles, tileSource, tile) {
            const tileSourceKey = getUid(tileSource);
            if (!(tileSourceKey in usedTiles)) {
                usedTiles[tileSourceKey] = {};
            }
            usedTiles[tileSourceKey][tile.getKey()] = true;
        }
        manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio, projection, extent, currentZ, preload, tileCallback) {
            const tileSourceKey = getUid(tileSource);
            if (!(tileSourceKey in frameState.wantedTiles)) {
                frameState.wantedTiles[tileSourceKey] = {};
            }
            const wantedTiles = frameState.wantedTiles[tileSourceKey];
            const tileQueue = frameState.tileQueue;
            const minZoom = tileGrid.getMinZoom();
            const rotation = frameState.viewState.rotation;
            const viewport = rotation ? getRotatedViewport(frameState.viewState.center, frameState.viewState.resolution, rotation, frameState.size) : undefined;
            let tileCount = 0;
            let tile, tileRange, tileResolution, x, y, z;
            for (z = minZoom; z <= currentZ; ++z) {
                tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z, tileRange);
                tileResolution = tileGrid.getResolution(z);
                for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
                    for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
                        if (rotation && !tileGrid.tileCoordIntersectsViewport([ z, x, y ], viewport)) {
                            continue;
                        }
                        if (currentZ - z <= preload) {
                            ++tileCount;
                            tile = tileSource.getTile(z, x, y, pixelRatio, projection);
                            if (tile.getState() == TileState.IDLE) {
                                wantedTiles[tile.getKey()] = true;
                                if (!tileQueue.isKeyQueued(tile.getKey())) {
                                    tileQueue.enqueue([ tile, tileSourceKey, tileGrid.getTileCoordCenter(tile.tileCoord), tileResolution ]);
                                }
                            }
                            if (tileCallback !== undefined) {
                                tileCallback(tile);
                            }
                        } else {
                            tileSource.useTile(z, x, y, projection);
                        }
                    }
                }
            }
            tileSource.updateCacheSize(tileCount, projection);
        }
    }
    class TileLayer extends BaseTileLayer {
        constructor(options) {
            super(options);
        }
        createRenderer() {
            return new CanvasTileLayerRenderer(this);
        }
    }
    const Instruction = {
        BEGIN_GEOMETRY: 0,
        BEGIN_PATH: 1,
        CIRCLE: 2,
        CLOSE_PATH: 3,
        CUSTOM: 4,
        DRAW_CHARS: 5,
        DRAW_IMAGE: 6,
        END_GEOMETRY: 7,
        FILL: 8,
        MOVE_TO_LINE_TO: 9,
        SET_FILL_STYLE: 10,
        SET_STROKE_STYLE: 11,
        STROKE: 12
    };
    const fillInstruction = [ Instruction.FILL ];
    const strokeInstruction = [ Instruction.STROKE ];
    const beginPathInstruction = [ Instruction.BEGIN_PATH ];
    const closePathInstruction = [ Instruction.CLOSE_PATH ];
    class VectorContext {
        drawCustom(geometry, feature, renderer, hitDetectionRenderer, index) {}
        drawGeometry(geometry) {}
        setStyle(style) {}
        drawCircle(circleGeometry, feature, index) {}
        drawFeature(feature, style, index) {}
        drawGeometryCollection(geometryCollectionGeometry, feature, index) {}
        drawLineString(lineStringGeometry, feature, index) {}
        drawMultiLineString(multiLineStringGeometry, feature, index) {}
        drawMultiPoint(multiPointGeometry, feature, index) {}
        drawMultiPolygon(multiPolygonGeometry, feature, index) {}
        drawPoint(pointGeometry, feature, index) {}
        drawPolygon(polygonGeometry, feature, index) {}
        drawText(geometry, feature, index) {}
        setFillStrokeStyle(fillStyle, strokeStyle) {}
        setImageStyle(imageStyle, declutterImageWithText) {}
        setTextStyle(textStyle, declutterImageWithText) {}
    }
    class CanvasBuilder extends VectorContext {
        constructor(tolerance, maxExtent, resolution, pixelRatio) {
            super();
            this.tolerance = tolerance;
            this.maxExtent = maxExtent;
            this.pixelRatio = pixelRatio;
            this.maxLineWidth = 0;
            this.resolution = resolution;
            this.beginGeometryInstruction1_ = null;
            this.beginGeometryInstruction2_ = null;
            this.bufferedMaxExtent_ = null;
            this.instructions = [];
            this.coordinates = [];
            this.tmpCoordinate_ = [];
            this.hitDetectionInstructions = [];
            this.state = {};
        }
        applyPixelRatio(dashArray) {
            const pixelRatio = this.pixelRatio;
            return pixelRatio == 1 ? dashArray : dashArray.map(function(dash) {
                return dash * pixelRatio;
            });
        }
        appendFlatPointCoordinates(flatCoordinates, stride) {
            const extent = this.getBufferedMaxExtent();
            const tmpCoord = this.tmpCoordinate_;
            const coordinates = this.coordinates;
            let myEnd = coordinates.length;
            for (let i = 0, ii = flatCoordinates.length; i < ii; i += stride) {
                tmpCoord[0] = flatCoordinates[i];
                tmpCoord[1] = flatCoordinates[i + 1];
                if (containsCoordinate(extent, tmpCoord)) {
                    coordinates[myEnd++] = tmpCoord[0];
                    coordinates[myEnd++] = tmpCoord[1];
                }
            }
            return myEnd;
        }
        appendFlatLineCoordinates(flatCoordinates, offset, end, stride, closed, skipFirst) {
            const coordinates = this.coordinates;
            let myEnd = coordinates.length;
            const extent = this.getBufferedMaxExtent();
            if (skipFirst) {
                offset += stride;
            }
            let lastXCoord = flatCoordinates[offset];
            let lastYCoord = flatCoordinates[offset + 1];
            const nextCoord = this.tmpCoordinate_;
            let skipped = true;
            let i, lastRel, nextRel;
            for (i = offset + stride; i < end; i += stride) {
                nextCoord[0] = flatCoordinates[i];
                nextCoord[1] = flatCoordinates[i + 1];
                nextRel = coordinateRelationship(extent, nextCoord);
                if (nextRel !== lastRel) {
                    if (skipped) {
                        coordinates[myEnd++] = lastXCoord;
                        coordinates[myEnd++] = lastYCoord;
                        skipped = false;
                    }
                    coordinates[myEnd++] = nextCoord[0];
                    coordinates[myEnd++] = nextCoord[1];
                } else if (nextRel === Relationship.INTERSECTING) {
                    coordinates[myEnd++] = nextCoord[0];
                    coordinates[myEnd++] = nextCoord[1];
                    skipped = false;
                } else {
                    skipped = true;
                }
                lastXCoord = nextCoord[0];
                lastYCoord = nextCoord[1];
                lastRel = nextRel;
            }
            if (closed && skipped || i === offset + stride) {
                coordinates[myEnd++] = lastXCoord;
                coordinates[myEnd++] = lastYCoord;
            }
            return myEnd;
        }
        drawCustomCoordinates_(flatCoordinates, offset, ends, stride, builderEnds) {
            for (let i = 0, ii = ends.length; i < ii; ++i) {
                const end = ends[i];
                const builderEnd = this.appendFlatLineCoordinates(flatCoordinates, offset, end, stride, false, false);
                builderEnds.push(builderEnd);
                offset = end;
            }
            return offset;
        }
        drawCustom(geometry, feature, renderer, hitDetectionRenderer, index) {
            this.beginGeometry(geometry, feature, index);
            const type = geometry.getType();
            const stride = geometry.getStride();
            const builderBegin = this.coordinates.length;
            let flatCoordinates, builderEnd, builderEnds, builderEndss;
            let offset;
            switch (type) {
              case "MultiPolygon":
                flatCoordinates = geometry.getOrientedFlatCoordinates();
                builderEndss = [];
                const endss = geometry.getEndss();
                offset = 0;
                for (let i = 0, ii = endss.length; i < ii; ++i) {
                    const myEnds = [];
                    offset = this.drawCustomCoordinates_(flatCoordinates, offset, endss[i], stride, myEnds);
                    builderEndss.push(myEnds);
                }
                this.instructions.push([ Instruction.CUSTOM, builderBegin, builderEndss, geometry, renderer, inflateMultiCoordinatesArray, index ]);
                this.hitDetectionInstructions.push([ Instruction.CUSTOM, builderBegin, builderEndss, geometry, hitDetectionRenderer || renderer, inflateMultiCoordinatesArray, index ]);
                break;

              case "Polygon":
              case "MultiLineString":
                builderEnds = [];
                flatCoordinates = type == "Polygon" ? geometry.getOrientedFlatCoordinates() : geometry.getFlatCoordinates();
                offset = this.drawCustomCoordinates_(flatCoordinates, 0, geometry.getEnds(), stride, builderEnds);
                this.instructions.push([ Instruction.CUSTOM, builderBegin, builderEnds, geometry, renderer, inflateCoordinatesArray, index ]);
                this.hitDetectionInstructions.push([ Instruction.CUSTOM, builderBegin, builderEnds, geometry, hitDetectionRenderer || renderer, inflateCoordinatesArray, index ]);
                break;

              case "LineString":
              case "Circle":
                flatCoordinates = geometry.getFlatCoordinates();
                builderEnd = this.appendFlatLineCoordinates(flatCoordinates, 0, flatCoordinates.length, stride, false, false);
                this.instructions.push([ Instruction.CUSTOM, builderBegin, builderEnd, geometry, renderer, inflateCoordinates, index ]);
                this.hitDetectionInstructions.push([ Instruction.CUSTOM, builderBegin, builderEnd, geometry, hitDetectionRenderer || renderer, inflateCoordinates, index ]);
                break;

              case "MultiPoint":
                flatCoordinates = geometry.getFlatCoordinates();
                builderEnd = this.appendFlatPointCoordinates(flatCoordinates, stride);
                if (builderEnd > builderBegin) {
                    this.instructions.push([ Instruction.CUSTOM, builderBegin, builderEnd, geometry, renderer, inflateCoordinates, index ]);
                    this.hitDetectionInstructions.push([ Instruction.CUSTOM, builderBegin, builderEnd, geometry, hitDetectionRenderer || renderer, inflateCoordinates, index ]);
                }
                break;

              case "Point":
                flatCoordinates = geometry.getFlatCoordinates();
                this.coordinates.push(flatCoordinates[0], flatCoordinates[1]);
                builderEnd = this.coordinates.length;
                this.instructions.push([ Instruction.CUSTOM, builderBegin, builderEnd, geometry, renderer, undefined, index ]);
                this.hitDetectionInstructions.push([ Instruction.CUSTOM, builderBegin, builderEnd, geometry, hitDetectionRenderer || renderer, undefined, index ]);
                break;
            }
            this.endGeometry(feature);
        }
        beginGeometry(geometry, feature, index) {
            this.beginGeometryInstruction1_ = [ Instruction.BEGIN_GEOMETRY, feature, 0, geometry, index ];
            this.instructions.push(this.beginGeometryInstruction1_);
            this.beginGeometryInstruction2_ = [ Instruction.BEGIN_GEOMETRY, feature, 0, geometry, index ];
            this.hitDetectionInstructions.push(this.beginGeometryInstruction2_);
        }
        finish() {
            return {
                instructions: this.instructions,
                hitDetectionInstructions: this.hitDetectionInstructions,
                coordinates: this.coordinates
            };
        }
        reverseHitDetectionInstructions() {
            const hitDetectionInstructions = this.hitDetectionInstructions;
            hitDetectionInstructions.reverse();
            let i;
            const n = hitDetectionInstructions.length;
            let instruction;
            let type;
            let begin = -1;
            for (i = 0; i < n; ++i) {
                instruction = hitDetectionInstructions[i];
                type = instruction[0];
                if (type == Instruction.END_GEOMETRY) {
                    begin = i;
                } else if (type == Instruction.BEGIN_GEOMETRY) {
                    instruction[2] = i;
                    reverseSubArray(this.hitDetectionInstructions, begin, i);
                    begin = -1;
                }
            }
        }
        setFillStrokeStyle(fillStyle, strokeStyle) {
            const state = this.state;
            if (fillStyle) {
                const fillStyleColor = fillStyle.getColor();
                state.fillPatternScale = fillStyleColor && typeof fillStyleColor === "object" && "src" in fillStyleColor ? this.pixelRatio : 1;
                state.fillStyle = asColorLike(fillStyleColor ? fillStyleColor : defaultFillStyle);
            } else {
                state.fillStyle = undefined;
            }
            if (strokeStyle) {
                const strokeStyleColor = strokeStyle.getColor();
                state.strokeStyle = asColorLike(strokeStyleColor ? strokeStyleColor : defaultStrokeStyle);
                const strokeStyleLineCap = strokeStyle.getLineCap();
                state.lineCap = strokeStyleLineCap !== undefined ? strokeStyleLineCap : defaultLineCap;
                const strokeStyleLineDash = strokeStyle.getLineDash();
                state.lineDash = strokeStyleLineDash ? strokeStyleLineDash.slice() : defaultLineDash;
                const strokeStyleLineDashOffset = strokeStyle.getLineDashOffset();
                state.lineDashOffset = strokeStyleLineDashOffset ? strokeStyleLineDashOffset : defaultLineDashOffset;
                const strokeStyleLineJoin = strokeStyle.getLineJoin();
                state.lineJoin = strokeStyleLineJoin !== undefined ? strokeStyleLineJoin : defaultLineJoin;
                const strokeStyleWidth = strokeStyle.getWidth();
                state.lineWidth = strokeStyleWidth !== undefined ? strokeStyleWidth : defaultLineWidth;
                const strokeStyleMiterLimit = strokeStyle.getMiterLimit();
                state.miterLimit = strokeStyleMiterLimit !== undefined ? strokeStyleMiterLimit : defaultMiterLimit;
                if (state.lineWidth > this.maxLineWidth) {
                    this.maxLineWidth = state.lineWidth;
                    this.bufferedMaxExtent_ = null;
                }
            } else {
                state.strokeStyle = undefined;
                state.lineCap = undefined;
                state.lineDash = null;
                state.lineDashOffset = undefined;
                state.lineJoin = undefined;
                state.lineWidth = undefined;
                state.miterLimit = undefined;
            }
        }
        createFill(state) {
            const fillStyle = state.fillStyle;
            const fillInstruction = [ Instruction.SET_FILL_STYLE, fillStyle ];
            if (typeof fillStyle !== "string") {
                fillInstruction.push(state.fillPatternScale);
            }
            return fillInstruction;
        }
        applyStroke(state) {
            this.instructions.push(this.createStroke(state));
        }
        createStroke(state) {
            return [ Instruction.SET_STROKE_STYLE, state.strokeStyle, state.lineWidth * this.pixelRatio, state.lineCap, state.lineJoin, state.miterLimit, this.applyPixelRatio(state.lineDash), state.lineDashOffset * this.pixelRatio ];
        }
        updateFillStyle(state, createFill) {
            const fillStyle = state.fillStyle;
            if (typeof fillStyle !== "string" || state.currentFillStyle != fillStyle) {
                if (fillStyle !== undefined) {
                    this.instructions.push(createFill.call(this, state));
                }
                state.currentFillStyle = fillStyle;
            }
        }
        updateStrokeStyle(state, applyStroke) {
            const strokeStyle = state.strokeStyle;
            const lineCap = state.lineCap;
            const lineDash = state.lineDash;
            const lineDashOffset = state.lineDashOffset;
            const lineJoin = state.lineJoin;
            const lineWidth = state.lineWidth;
            const miterLimit = state.miterLimit;
            if (state.currentStrokeStyle != strokeStyle || state.currentLineCap != lineCap || lineDash != state.currentLineDash && !equals$2(state.currentLineDash, lineDash) || state.currentLineDashOffset != lineDashOffset || state.currentLineJoin != lineJoin || state.currentLineWidth != lineWidth || state.currentMiterLimit != miterLimit) {
                if (strokeStyle !== undefined) {
                    applyStroke.call(this, state);
                }
                state.currentStrokeStyle = strokeStyle;
                state.currentLineCap = lineCap;
                state.currentLineDash = lineDash;
                state.currentLineDashOffset = lineDashOffset;
                state.currentLineJoin = lineJoin;
                state.currentLineWidth = lineWidth;
                state.currentMiterLimit = miterLimit;
            }
        }
        endGeometry(feature) {
            this.beginGeometryInstruction1_[2] = this.instructions.length;
            this.beginGeometryInstruction1_ = null;
            this.beginGeometryInstruction2_[2] = this.hitDetectionInstructions.length;
            this.beginGeometryInstruction2_ = null;
            const endGeometryInstruction = [ Instruction.END_GEOMETRY, feature ];
            this.instructions.push(endGeometryInstruction);
            this.hitDetectionInstructions.push(endGeometryInstruction);
        }
        getBufferedMaxExtent() {
            if (!this.bufferedMaxExtent_) {
                this.bufferedMaxExtent_ = clone(this.maxExtent);
                if (this.maxLineWidth > 0) {
                    const width = this.resolution * (this.maxLineWidth + 1) / 2;
                    buffer(this.bufferedMaxExtent_, width, this.bufferedMaxExtent_);
                }
            }
            return this.bufferedMaxExtent_;
        }
    }
    class CanvasImageBuilder extends CanvasBuilder {
        constructor(tolerance, maxExtent, resolution, pixelRatio) {
            super(tolerance, maxExtent, resolution, pixelRatio);
            this.hitDetectionImage_ = null;
            this.image_ = null;
            this.imagePixelRatio_ = undefined;
            this.anchorX_ = undefined;
            this.anchorY_ = undefined;
            this.height_ = undefined;
            this.opacity_ = undefined;
            this.originX_ = undefined;
            this.originY_ = undefined;
            this.rotateWithView_ = undefined;
            this.rotation_ = undefined;
            this.scale_ = undefined;
            this.width_ = undefined;
            this.declutterMode_ = undefined;
            this.declutterImageWithText_ = undefined;
        }
        drawPoint(pointGeometry, feature, index) {
            if (!this.image_ || this.maxExtent && !containsCoordinate(this.maxExtent, pointGeometry.getFlatCoordinates())) {
                return;
            }
            this.beginGeometry(pointGeometry, feature, index);
            const flatCoordinates = pointGeometry.getFlatCoordinates();
            const stride = pointGeometry.getStride();
            const myBegin = this.coordinates.length;
            const myEnd = this.appendFlatPointCoordinates(flatCoordinates, stride);
            this.instructions.push([ Instruction.DRAW_IMAGE, myBegin, myEnd, this.image_, this.anchorX_ * this.imagePixelRatio_, this.anchorY_ * this.imagePixelRatio_, Math.ceil(this.height_ * this.imagePixelRatio_), this.opacity_, this.originX_ * this.imagePixelRatio_, this.originY_ * this.imagePixelRatio_, this.rotateWithView_, this.rotation_, [ this.scale_[0] * this.pixelRatio / this.imagePixelRatio_, this.scale_[1] * this.pixelRatio / this.imagePixelRatio_ ], Math.ceil(this.width_ * this.imagePixelRatio_), this.declutterMode_, this.declutterImageWithText_ ]);
            this.hitDetectionInstructions.push([ Instruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_, this.anchorX_, this.anchorY_, this.height_, 1, this.originX_, this.originY_, this.rotateWithView_, this.rotation_, this.scale_, this.width_, this.declutterMode_, this.declutterImageWithText_ ]);
            this.endGeometry(feature);
        }
        drawMultiPoint(multiPointGeometry, feature, index) {
            if (!this.image_) {
                return;
            }
            this.beginGeometry(multiPointGeometry, feature, index);
            const flatCoordinates = multiPointGeometry.getFlatCoordinates();
            const filteredFlatCoordinates = [];
            for (let i = 0, ii = flatCoordinates.length; i < ii; i += multiPointGeometry.getStride()) {
                if (!this.maxExtent || containsCoordinate(this.maxExtent, flatCoordinates.slice(i, i + 2))) {
                    filteredFlatCoordinates.push(flatCoordinates[i], flatCoordinates[i + 1]);
                }
            }
            const myBegin = this.coordinates.length;
            const myEnd = this.appendFlatPointCoordinates(filteredFlatCoordinates, 2);
            this.instructions.push([ Instruction.DRAW_IMAGE, myBegin, myEnd, this.image_, this.anchorX_ * this.imagePixelRatio_, this.anchorY_ * this.imagePixelRatio_, Math.ceil(this.height_ * this.imagePixelRatio_), this.opacity_, this.originX_ * this.imagePixelRatio_, this.originY_ * this.imagePixelRatio_, this.rotateWithView_, this.rotation_, [ this.scale_[0] * this.pixelRatio / this.imagePixelRatio_, this.scale_[1] * this.pixelRatio / this.imagePixelRatio_ ], Math.ceil(this.width_ * this.imagePixelRatio_), this.declutterMode_, this.declutterImageWithText_ ]);
            this.hitDetectionInstructions.push([ Instruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_, this.anchorX_, this.anchorY_, this.height_, 1, this.originX_, this.originY_, this.rotateWithView_, this.rotation_, this.scale_, this.width_, this.declutterMode_, this.declutterImageWithText_ ]);
            this.endGeometry(feature);
        }
        finish() {
            this.reverseHitDetectionInstructions();
            this.anchorX_ = undefined;
            this.anchorY_ = undefined;
            this.hitDetectionImage_ = null;
            this.image_ = null;
            this.imagePixelRatio_ = undefined;
            this.height_ = undefined;
            this.scale_ = undefined;
            this.opacity_ = undefined;
            this.originX_ = undefined;
            this.originY_ = undefined;
            this.rotateWithView_ = undefined;
            this.rotation_ = undefined;
            this.width_ = undefined;
            return super.finish();
        }
        setImageStyle(imageStyle, sharedData) {
            const anchor = imageStyle.getAnchor();
            const size = imageStyle.getSize();
            const origin = imageStyle.getOrigin();
            this.imagePixelRatio_ = imageStyle.getPixelRatio(this.pixelRatio);
            this.anchorX_ = anchor[0];
            this.anchorY_ = anchor[1];
            this.hitDetectionImage_ = imageStyle.getHitDetectionImage();
            this.image_ = imageStyle.getImage(this.pixelRatio);
            this.height_ = size[1];
            this.opacity_ = imageStyle.getOpacity();
            this.originX_ = origin[0];
            this.originY_ = origin[1];
            this.rotateWithView_ = imageStyle.getRotateWithView();
            this.rotation_ = imageStyle.getRotation();
            this.scale_ = imageStyle.getScaleArray();
            this.width_ = size[0];
            this.declutterMode_ = imageStyle.getDeclutterMode();
            this.declutterImageWithText_ = sharedData;
        }
    }
    class CanvasLineStringBuilder extends CanvasBuilder {
        constructor(tolerance, maxExtent, resolution, pixelRatio) {
            super(tolerance, maxExtent, resolution, pixelRatio);
        }
        drawFlatCoordinates_(flatCoordinates, offset, end, stride) {
            const myBegin = this.coordinates.length;
            const myEnd = this.appendFlatLineCoordinates(flatCoordinates, offset, end, stride, false, false);
            const moveToLineToInstruction = [ Instruction.MOVE_TO_LINE_TO, myBegin, myEnd ];
            this.instructions.push(moveToLineToInstruction);
            this.hitDetectionInstructions.push(moveToLineToInstruction);
            return end;
        }
        drawLineString(lineStringGeometry, feature, index) {
            const state = this.state;
            const strokeStyle = state.strokeStyle;
            const lineWidth = state.lineWidth;
            if (strokeStyle === undefined || lineWidth === undefined) {
                return;
            }
            this.updateStrokeStyle(state, this.applyStroke);
            this.beginGeometry(lineStringGeometry, feature, index);
            this.hitDetectionInstructions.push([ Instruction.SET_STROKE_STYLE, state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, state.miterLimit, defaultLineDash, defaultLineDashOffset ], beginPathInstruction);
            const flatCoordinates = lineStringGeometry.getFlatCoordinates();
            const stride = lineStringGeometry.getStride();
            this.drawFlatCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride);
            this.hitDetectionInstructions.push(strokeInstruction);
            this.endGeometry(feature);
        }
        drawMultiLineString(multiLineStringGeometry, feature, index) {
            const state = this.state;
            const strokeStyle = state.strokeStyle;
            const lineWidth = state.lineWidth;
            if (strokeStyle === undefined || lineWidth === undefined) {
                return;
            }
            this.updateStrokeStyle(state, this.applyStroke);
            this.beginGeometry(multiLineStringGeometry, feature, index);
            this.hitDetectionInstructions.push([ Instruction.SET_STROKE_STYLE, state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, state.miterLimit, defaultLineDash, defaultLineDashOffset ], beginPathInstruction);
            const ends = multiLineStringGeometry.getEnds();
            const flatCoordinates = multiLineStringGeometry.getFlatCoordinates();
            const stride = multiLineStringGeometry.getStride();
            let offset = 0;
            for (let i = 0, ii = ends.length; i < ii; ++i) {
                offset = this.drawFlatCoordinates_(flatCoordinates, offset, ends[i], stride);
            }
            this.hitDetectionInstructions.push(strokeInstruction);
            this.endGeometry(feature);
        }
        finish() {
            const state = this.state;
            if (state.lastStroke != undefined && state.lastStroke != this.coordinates.length) {
                this.instructions.push(strokeInstruction);
            }
            this.reverseHitDetectionInstructions();
            this.state = null;
            return super.finish();
        }
        applyStroke(state) {
            if (state.lastStroke != undefined && state.lastStroke != this.coordinates.length) {
                this.instructions.push(strokeInstruction);
                state.lastStroke = this.coordinates.length;
            }
            state.lastStroke = 0;
            super.applyStroke(state);
            this.instructions.push(beginPathInstruction);
        }
    }
    class CanvasPolygonBuilder extends CanvasBuilder {
        constructor(tolerance, maxExtent, resolution, pixelRatio) {
            super(tolerance, maxExtent, resolution, pixelRatio);
        }
        drawFlatCoordinatess_(flatCoordinates, offset, ends, stride) {
            const state = this.state;
            const fill = state.fillStyle !== undefined;
            const stroke = state.strokeStyle !== undefined;
            const numEnds = ends.length;
            this.instructions.push(beginPathInstruction);
            this.hitDetectionInstructions.push(beginPathInstruction);
            for (let i = 0; i < numEnds; ++i) {
                const end = ends[i];
                const myBegin = this.coordinates.length;
                const myEnd = this.appendFlatLineCoordinates(flatCoordinates, offset, end, stride, true, !stroke);
                const moveToLineToInstruction = [ Instruction.MOVE_TO_LINE_TO, myBegin, myEnd ];
                this.instructions.push(moveToLineToInstruction);
                this.hitDetectionInstructions.push(moveToLineToInstruction);
                if (stroke) {
                    this.instructions.push(closePathInstruction);
                    this.hitDetectionInstructions.push(closePathInstruction);
                }
                offset = end;
            }
            if (fill) {
                this.instructions.push(fillInstruction);
                this.hitDetectionInstructions.push(fillInstruction);
            }
            if (stroke) {
                this.instructions.push(strokeInstruction);
                this.hitDetectionInstructions.push(strokeInstruction);
            }
            return offset;
        }
        drawCircle(circleGeometry, feature, index) {
            const state = this.state;
            const fillStyle = state.fillStyle;
            const strokeStyle = state.strokeStyle;
            if (fillStyle === undefined && strokeStyle === undefined) {
                return;
            }
            this.setFillStrokeStyles_();
            this.beginGeometry(circleGeometry, feature, index);
            if (state.fillStyle !== undefined) {
                this.hitDetectionInstructions.push([ Instruction.SET_FILL_STYLE, defaultFillStyle ]);
            }
            if (state.strokeStyle !== undefined) {
                this.hitDetectionInstructions.push([ Instruction.SET_STROKE_STYLE, state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, state.miterLimit, defaultLineDash, defaultLineDashOffset ]);
            }
            const flatCoordinates = circleGeometry.getFlatCoordinates();
            const stride = circleGeometry.getStride();
            const myBegin = this.coordinates.length;
            this.appendFlatLineCoordinates(flatCoordinates, 0, flatCoordinates.length, stride, false, false);
            const circleInstruction = [ Instruction.CIRCLE, myBegin ];
            this.instructions.push(beginPathInstruction, circleInstruction);
            this.hitDetectionInstructions.push(beginPathInstruction, circleInstruction);
            if (state.fillStyle !== undefined) {
                this.instructions.push(fillInstruction);
                this.hitDetectionInstructions.push(fillInstruction);
            }
            if (state.strokeStyle !== undefined) {
                this.instructions.push(strokeInstruction);
                this.hitDetectionInstructions.push(strokeInstruction);
            }
            this.endGeometry(feature);
        }
        drawPolygon(polygonGeometry, feature, index) {
            const state = this.state;
            const fillStyle = state.fillStyle;
            const strokeStyle = state.strokeStyle;
            if (fillStyle === undefined && strokeStyle === undefined) {
                return;
            }
            this.setFillStrokeStyles_();
            this.beginGeometry(polygonGeometry, feature, index);
            if (state.fillStyle !== undefined) {
                this.hitDetectionInstructions.push([ Instruction.SET_FILL_STYLE, defaultFillStyle ]);
            }
            if (state.strokeStyle !== undefined) {
                this.hitDetectionInstructions.push([ Instruction.SET_STROKE_STYLE, state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, state.miterLimit, defaultLineDash, defaultLineDashOffset ]);
            }
            const ends = polygonGeometry.getEnds();
            const flatCoordinates = polygonGeometry.getOrientedFlatCoordinates();
            const stride = polygonGeometry.getStride();
            this.drawFlatCoordinatess_(flatCoordinates, 0, ends, stride);
            this.endGeometry(feature);
        }
        drawMultiPolygon(multiPolygonGeometry, feature, index) {
            const state = this.state;
            const fillStyle = state.fillStyle;
            const strokeStyle = state.strokeStyle;
            if (fillStyle === undefined && strokeStyle === undefined) {
                return;
            }
            this.setFillStrokeStyles_();
            this.beginGeometry(multiPolygonGeometry, feature, index);
            if (state.fillStyle !== undefined) {
                this.hitDetectionInstructions.push([ Instruction.SET_FILL_STYLE, defaultFillStyle ]);
            }
            if (state.strokeStyle !== undefined) {
                this.hitDetectionInstructions.push([ Instruction.SET_STROKE_STYLE, state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, state.miterLimit, defaultLineDash, defaultLineDashOffset ]);
            }
            const endss = multiPolygonGeometry.getEndss();
            const flatCoordinates = multiPolygonGeometry.getOrientedFlatCoordinates();
            const stride = multiPolygonGeometry.getStride();
            let offset = 0;
            for (let i = 0, ii = endss.length; i < ii; ++i) {
                offset = this.drawFlatCoordinatess_(flatCoordinates, offset, endss[i], stride);
            }
            this.endGeometry(feature);
        }
        finish() {
            this.reverseHitDetectionInstructions();
            this.state = null;
            const tolerance = this.tolerance;
            if (tolerance !== 0) {
                const coordinates = this.coordinates;
                for (let i = 0, ii = coordinates.length; i < ii; ++i) {
                    coordinates[i] = snap(coordinates[i], tolerance);
                }
            }
            return super.finish();
        }
        setFillStrokeStyles_() {
            const state = this.state;
            const fillStyle = state.fillStyle;
            if (fillStyle !== undefined) {
                this.updateFillStyle(state, this.createFill);
            }
            if (state.strokeStyle !== undefined) {
                this.updateStrokeStyle(state, this.applyStroke);
            }
        }
    }
    function lineChunk(chunkLength, flatCoordinates, offset, end, stride) {
        const chunks = [];
        let cursor = offset;
        let chunkM = 0;
        let currentChunk = flatCoordinates.slice(offset, 2);
        while (chunkM < chunkLength && cursor + stride < end) {
            const [x1, y1] = currentChunk.slice(-2);
            const x2 = flatCoordinates[cursor + stride];
            const y2 = flatCoordinates[cursor + stride + 1];
            const segmentLength = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
            chunkM += segmentLength;
            if (chunkM >= chunkLength) {
                const m = (chunkLength - chunkM + segmentLength) / segmentLength;
                const x = lerp(x1, x2, m);
                const y = lerp(y1, y2, m);
                currentChunk.push(x, y);
                chunks.push(currentChunk);
                currentChunk = [ x, y ];
                if (chunkM == chunkLength) {
                    cursor += stride;
                }
                chunkM = 0;
            } else if (chunkM < chunkLength) {
                currentChunk.push(flatCoordinates[cursor + stride], flatCoordinates[cursor + stride + 1]);
                cursor += stride;
            } else {
                const missing = segmentLength - chunkM;
                const x = lerp(x1, x2, missing / segmentLength);
                const y = lerp(y1, y2, missing / segmentLength);
                currentChunk.push(x, y);
                chunks.push(currentChunk);
                currentChunk = [ x, y ];
                chunkM = 0;
                cursor += stride;
            }
        }
        if (chunkM > 0) {
            chunks.push(currentChunk);
        }
        return chunks;
    }
    function matchingChunk(maxAngle, flatCoordinates, offset, end, stride) {
        let chunkStart = offset;
        let chunkEnd = offset;
        let chunkM = 0;
        let m = 0;
        let start = offset;
        let acos, i, m12, m23, x1, y1, x12, y12, x23, y23;
        for (i = offset; i < end; i += stride) {
            const x2 = flatCoordinates[i];
            const y2 = flatCoordinates[i + 1];
            if (x1 !== undefined) {
                x23 = x2 - x1;
                y23 = y2 - y1;
                m23 = Math.sqrt(x23 * x23 + y23 * y23);
                if (x12 !== undefined) {
                    m += m12;
                    acos = Math.acos((x12 * x23 + y12 * y23) / (m12 * m23));
                    if (acos > maxAngle) {
                        if (m > chunkM) {
                            chunkM = m;
                            chunkStart = start;
                            chunkEnd = i;
                        }
                        m = 0;
                        start = i - stride;
                    }
                }
                m12 = m23;
                x12 = x23;
                y12 = y23;
            }
            x1 = x2;
            y1 = y2;
        }
        m += m23;
        return m > chunkM ? [ start, i ] : [ chunkStart, chunkEnd ];
    }
    const TEXT_ALIGN = {
        left: 0,
        center: .5,
        right: 1,
        top: 0,
        middle: .5,
        hanging: .2,
        alphabetic: .8,
        ideographic: .8,
        bottom: 1
    };
    class CanvasTextBuilder extends CanvasBuilder {
        constructor(tolerance, maxExtent, resolution, pixelRatio) {
            super(tolerance, maxExtent, resolution, pixelRatio);
            this.labels_ = null;
            this.text_ = "";
            this.textOffsetX_ = 0;
            this.textOffsetY_ = 0;
            this.textRotateWithView_ = undefined;
            this.textRotation_ = 0;
            this.textFillState_ = null;
            this.fillStates = {};
            this.fillStates[defaultFillStyle] = {
                fillStyle: defaultFillStyle
            };
            this.textStrokeState_ = null;
            this.strokeStates = {};
            this.textState_ = {};
            this.textStates = {};
            this.textKey_ = "";
            this.fillKey_ = "";
            this.strokeKey_ = "";
            this.declutterMode_ = undefined;
            this.declutterImageWithText_ = undefined;
        }
        finish() {
            const instructions = super.finish();
            instructions.textStates = this.textStates;
            instructions.fillStates = this.fillStates;
            instructions.strokeStates = this.strokeStates;
            return instructions;
        }
        drawText(geometry, feature, index) {
            const fillState = this.textFillState_;
            const strokeState = this.textStrokeState_;
            const textState = this.textState_;
            if (this.text_ === "" || !textState || !fillState && !strokeState) {
                return;
            }
            const coordinates = this.coordinates;
            let begin = coordinates.length;
            const geometryType = geometry.getType();
            let flatCoordinates = null;
            let stride = geometry.getStride();
            if (textState.placement === "line" && (geometryType == "LineString" || geometryType == "MultiLineString" || geometryType == "Polygon" || geometryType == "MultiPolygon")) {
                if (!intersects$1(this.maxExtent, geometry.getExtent())) {
                    return;
                }
                let ends;
                flatCoordinates = geometry.getFlatCoordinates();
                if (geometryType == "LineString") {
                    ends = [ flatCoordinates.length ];
                } else if (geometryType == "MultiLineString") {
                    ends = geometry.getEnds();
                } else if (geometryType == "Polygon") {
                    ends = geometry.getEnds().slice(0, 1);
                } else if (geometryType == "MultiPolygon") {
                    const endss = geometry.getEndss();
                    ends = [];
                    for (let i = 0, ii = endss.length; i < ii; ++i) {
                        ends.push(endss[i][0]);
                    }
                }
                this.beginGeometry(geometry, feature, index);
                const repeat = textState.repeat;
                const textAlign = repeat ? undefined : textState.textAlign;
                let flatOffset = 0;
                for (let o = 0, oo = ends.length; o < oo; ++o) {
                    let chunks;
                    if (repeat) {
                        chunks = lineChunk(repeat * this.resolution, flatCoordinates, flatOffset, ends[o], stride);
                    } else {
                        chunks = [ flatCoordinates.slice(flatOffset, ends[o]) ];
                    }
                    for (let c = 0, cc = chunks.length; c < cc; ++c) {
                        const chunk = chunks[c];
                        let chunkBegin = 0;
                        let chunkEnd = chunk.length;
                        if (textAlign == undefined) {
                            const range = matchingChunk(textState.maxAngle, chunk, 0, chunk.length, 2);
                            chunkBegin = range[0];
                            chunkEnd = range[1];
                        }
                        for (let i = chunkBegin; i < chunkEnd; i += stride) {
                            coordinates.push(chunk[i], chunk[i + 1]);
                        }
                        const end = coordinates.length;
                        flatOffset = ends[o];
                        this.drawChars_(begin, end);
                        begin = end;
                    }
                }
                this.endGeometry(feature);
            } else {
                let geometryWidths = textState.overflow ? null : [];
                switch (geometryType) {
                  case "Point":
                  case "MultiPoint":
                    flatCoordinates = geometry.getFlatCoordinates();
                    break;

                  case "LineString":
                    flatCoordinates = geometry.getFlatMidpoint();
                    break;

                  case "Circle":
                    flatCoordinates = geometry.getCenter();
                    break;

                  case "MultiLineString":
                    flatCoordinates = geometry.getFlatMidpoints();
                    stride = 2;
                    break;

                  case "Polygon":
                    flatCoordinates = geometry.getFlatInteriorPoint();
                    if (!textState.overflow) {
                        geometryWidths.push(flatCoordinates[2] / this.resolution);
                    }
                    stride = 3;
                    break;

                  case "MultiPolygon":
                    const interiorPoints = geometry.getFlatInteriorPoints();
                    flatCoordinates = [];
                    for (let i = 0, ii = interiorPoints.length; i < ii; i += 3) {
                        if (!textState.overflow) {
                            geometryWidths.push(interiorPoints[i + 2] / this.resolution);
                        }
                        flatCoordinates.push(interiorPoints[i], interiorPoints[i + 1]);
                    }
                    if (flatCoordinates.length === 0) {
                        return;
                    }
                    stride = 2;
                    break;
                }
                const end = this.appendFlatPointCoordinates(flatCoordinates, stride);
                if (end === begin) {
                    return;
                }
                if (geometryWidths && (end - begin) / 2 !== flatCoordinates.length / stride) {
                    let beg = begin / 2;
                    geometryWidths = geometryWidths.filter((w, i) => {
                        const keep = coordinates[(beg + i) * 2] === flatCoordinates[i * stride] && coordinates[(beg + i) * 2 + 1] === flatCoordinates[i * stride + 1];
                        if (!keep) {
                            --beg;
                        }
                        return keep;
                    });
                }
                this.saveTextStates_();
                if (textState.backgroundFill || textState.backgroundStroke) {
                    this.setFillStrokeStyle(textState.backgroundFill, textState.backgroundStroke);
                    if (textState.backgroundFill) {
                        this.updateFillStyle(this.state, this.createFill);
                    }
                    if (textState.backgroundStroke) {
                        this.updateStrokeStyle(this.state, this.applyStroke);
                        this.hitDetectionInstructions.push(this.createStroke(this.state));
                    }
                }
                this.beginGeometry(geometry, feature, index);
                let padding = textState.padding;
                if (padding != defaultPadding && (textState.scale[0] < 0 || textState.scale[1] < 0)) {
                    let p0 = textState.padding[0];
                    let p1 = textState.padding[1];
                    let p2 = textState.padding[2];
                    let p3 = textState.padding[3];
                    if (textState.scale[0] < 0) {
                        p1 = -p1;
                        p3 = -p3;
                    }
                    if (textState.scale[1] < 0) {
                        p0 = -p0;
                        p2 = -p2;
                    }
                    padding = [ p0, p1, p2, p3 ];
                }
                const pixelRatio = this.pixelRatio;
                this.instructions.push([ Instruction.DRAW_IMAGE, begin, end, null, NaN, NaN, NaN, 1, 0, 0, this.textRotateWithView_, this.textRotation_, [ 1, 1 ], NaN, this.declutterMode_, this.declutterImageWithText_, padding == defaultPadding ? defaultPadding : padding.map(function(p) {
                    return p * pixelRatio;
                }), !!textState.backgroundFill, !!textState.backgroundStroke, this.text_, this.textKey_, this.strokeKey_, this.fillKey_, this.textOffsetX_, this.textOffsetY_, geometryWidths ]);
                const scale = 1 / pixelRatio;
                const currentFillStyle = this.state.fillStyle;
                if (textState.backgroundFill) {
                    this.state.fillStyle = defaultFillStyle;
                    this.hitDetectionInstructions.push(this.createFill(this.state));
                }
                this.hitDetectionInstructions.push([ Instruction.DRAW_IMAGE, begin, end, null, NaN, NaN, NaN, 1, 0, 0, this.textRotateWithView_, this.textRotation_, [ scale, scale ], NaN, this.declutterMode_, this.declutterImageWithText_, padding, !!textState.backgroundFill, !!textState.backgroundStroke, this.text_, this.textKey_, this.strokeKey_, this.fillKey_ ? defaultFillStyle : this.fillKey_, this.textOffsetX_, this.textOffsetY_, geometryWidths ]);
                if (textState.backgroundFill) {
                    this.state.fillStyle = currentFillStyle;
                    this.hitDetectionInstructions.push(this.createFill(this.state));
                }
                this.endGeometry(feature);
            }
        }
        saveTextStates_() {
            const strokeState = this.textStrokeState_;
            const textState = this.textState_;
            const fillState = this.textFillState_;
            const strokeKey = this.strokeKey_;
            if (strokeState) {
                if (!(strokeKey in this.strokeStates)) {
                    this.strokeStates[strokeKey] = {
                        strokeStyle: strokeState.strokeStyle,
                        lineCap: strokeState.lineCap,
                        lineDashOffset: strokeState.lineDashOffset,
                        lineWidth: strokeState.lineWidth,
                        lineJoin: strokeState.lineJoin,
                        miterLimit: strokeState.miterLimit,
                        lineDash: strokeState.lineDash
                    };
                }
            }
            const textKey = this.textKey_;
            if (!(textKey in this.textStates)) {
                this.textStates[textKey] = {
                    font: textState.font,
                    textAlign: textState.textAlign || defaultTextAlign,
                    justify: textState.justify,
                    textBaseline: textState.textBaseline || defaultTextBaseline,
                    scale: textState.scale
                };
            }
            const fillKey = this.fillKey_;
            if (fillState) {
                if (!(fillKey in this.fillStates)) {
                    this.fillStates[fillKey] = {
                        fillStyle: fillState.fillStyle
                    };
                }
            }
        }
        drawChars_(begin, end) {
            const strokeState = this.textStrokeState_;
            const textState = this.textState_;
            const strokeKey = this.strokeKey_;
            const textKey = this.textKey_;
            const fillKey = this.fillKey_;
            this.saveTextStates_();
            const pixelRatio = this.pixelRatio;
            const baseline = TEXT_ALIGN[textState.textBaseline];
            const offsetY = this.textOffsetY_ * pixelRatio;
            const text = this.text_;
            const strokeWidth = strokeState ? strokeState.lineWidth * Math.abs(textState.scale[0]) / 2 : 0;
            this.instructions.push([ Instruction.DRAW_CHARS, begin, end, baseline, textState.overflow, fillKey, textState.maxAngle, pixelRatio, offsetY, strokeKey, strokeWidth * pixelRatio, text, textKey, 1, this.declutterMode_ ]);
            this.hitDetectionInstructions.push([ Instruction.DRAW_CHARS, begin, end, baseline, textState.overflow, fillKey ? defaultFillStyle : fillKey, textState.maxAngle, pixelRatio, offsetY, strokeKey, strokeWidth * pixelRatio, text, textKey, 1 / pixelRatio, this.declutterMode_ ]);
        }
        setTextStyle(textStyle, sharedData) {
            let textState, fillState, strokeState;
            if (!textStyle) {
                this.text_ = "";
            } else {
                const textFillStyle = textStyle.getFill();
                if (!textFillStyle) {
                    fillState = null;
                    this.textFillState_ = fillState;
                } else {
                    fillState = this.textFillState_;
                    if (!fillState) {
                        fillState = {};
                        this.textFillState_ = fillState;
                    }
                    fillState.fillStyle = asColorLike(textFillStyle.getColor() || defaultFillStyle);
                }
                const textStrokeStyle = textStyle.getStroke();
                if (!textStrokeStyle) {
                    strokeState = null;
                    this.textStrokeState_ = strokeState;
                } else {
                    strokeState = this.textStrokeState_;
                    if (!strokeState) {
                        strokeState = {};
                        this.textStrokeState_ = strokeState;
                    }
                    const lineDash = textStrokeStyle.getLineDash();
                    const lineDashOffset = textStrokeStyle.getLineDashOffset();
                    const lineWidth = textStrokeStyle.getWidth();
                    const miterLimit = textStrokeStyle.getMiterLimit();
                    strokeState.lineCap = textStrokeStyle.getLineCap() || defaultLineCap;
                    strokeState.lineDash = lineDash ? lineDash.slice() : defaultLineDash;
                    strokeState.lineDashOffset = lineDashOffset === undefined ? defaultLineDashOffset : lineDashOffset;
                    strokeState.lineJoin = textStrokeStyle.getLineJoin() || defaultLineJoin;
                    strokeState.lineWidth = lineWidth === undefined ? defaultLineWidth : lineWidth;
                    strokeState.miterLimit = miterLimit === undefined ? defaultMiterLimit : miterLimit;
                    strokeState.strokeStyle = asColorLike(textStrokeStyle.getColor() || defaultStrokeStyle);
                }
                textState = this.textState_;
                const font = textStyle.getFont() || defaultFont;
                registerFont(font);
                const textScale = textStyle.getScaleArray();
                textState.overflow = textStyle.getOverflow();
                textState.font = font;
                textState.maxAngle = textStyle.getMaxAngle();
                textState.placement = textStyle.getPlacement();
                textState.textAlign = textStyle.getTextAlign();
                textState.repeat = textStyle.getRepeat();
                textState.justify = textStyle.getJustify();
                textState.textBaseline = textStyle.getTextBaseline() || defaultTextBaseline;
                textState.backgroundFill = textStyle.getBackgroundFill();
                textState.backgroundStroke = textStyle.getBackgroundStroke();
                textState.padding = textStyle.getPadding() || defaultPadding;
                textState.scale = textScale === undefined ? [ 1, 1 ] : textScale;
                const textOffsetX = textStyle.getOffsetX();
                const textOffsetY = textStyle.getOffsetY();
                const textRotateWithView = textStyle.getRotateWithView();
                const textRotation = textStyle.getRotation();
                this.text_ = textStyle.getText() || "";
                this.textOffsetX_ = textOffsetX === undefined ? 0 : textOffsetX;
                this.textOffsetY_ = textOffsetY === undefined ? 0 : textOffsetY;
                this.textRotateWithView_ = textRotateWithView === undefined ? false : textRotateWithView;
                this.textRotation_ = textRotation === undefined ? 0 : textRotation;
                this.strokeKey_ = strokeState ? (typeof strokeState.strokeStyle == "string" ? strokeState.strokeStyle : getUid(strokeState.strokeStyle)) + strokeState.lineCap + strokeState.lineDashOffset + "|" + strokeState.lineWidth + strokeState.lineJoin + strokeState.miterLimit + "[" + strokeState.lineDash.join() + "]" : "";
                this.textKey_ = textState.font + textState.scale + (textState.textAlign || "?") + (textState.repeat || "?") + (textState.justify || "?") + (textState.textBaseline || "?");
                this.fillKey_ = fillState && fillState.fillStyle ? typeof fillState.fillStyle == "string" ? fillState.fillStyle : "|" + getUid(fillState.fillStyle) : "";
            }
            this.declutterMode_ = textStyle.getDeclutterMode();
            this.declutterImageWithText_ = sharedData;
        }
    }
    const BATCH_CONSTRUCTORS = {
        Circle: CanvasPolygonBuilder,
        Default: CanvasBuilder,
        Image: CanvasImageBuilder,
        LineString: CanvasLineStringBuilder,
        Polygon: CanvasPolygonBuilder,
        Text: CanvasTextBuilder
    };
    class BuilderGroup {
        constructor(tolerance, maxExtent, resolution, pixelRatio) {
            this.tolerance_ = tolerance;
            this.maxExtent_ = maxExtent;
            this.pixelRatio_ = pixelRatio;
            this.resolution_ = resolution;
            this.buildersByZIndex_ = {};
        }
        finish() {
            const builderInstructions = {};
            for (const zKey in this.buildersByZIndex_) {
                builderInstructions[zKey] = builderInstructions[zKey] || {};
                const builders = this.buildersByZIndex_[zKey];
                for (const builderKey in builders) {
                    const builderInstruction = builders[builderKey].finish();
                    builderInstructions[zKey][builderKey] = builderInstruction;
                }
            }
            return builderInstructions;
        }
        getBuilder(zIndex, builderType) {
            const zIndexKey = zIndex !== undefined ? zIndex.toString() : "0";
            let replays = this.buildersByZIndex_[zIndexKey];
            if (replays === undefined) {
                replays = {};
                this.buildersByZIndex_[zIndexKey] = replays;
            }
            let replay = replays[builderType];
            if (replay === undefined) {
                const Constructor = BATCH_CONSTRUCTORS[builderType];
                replay = new Constructor(this.tolerance_, this.maxExtent_, this.resolution_, this.pixelRatio_);
                replays[builderType] = replay;
            }
            return replay;
        }
    }
    function drawTextOnPath(flatCoordinates, offset, end, stride, text, startM, maxAngle, scale, measureAndCacheTextWidth, font, cache, rotation) {
        let x2 = flatCoordinates[offset];
        let y2 = flatCoordinates[offset + 1];
        let x1 = 0;
        let y1 = 0;
        let segmentLength = 0;
        let segmentM = 0;
        function advance() {
            x1 = x2;
            y1 = y2;
            offset += stride;
            x2 = flatCoordinates[offset];
            y2 = flatCoordinates[offset + 1];
            segmentM += segmentLength;
            segmentLength = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
        }
        do {
            advance();
        } while (offset < end - stride && segmentM + segmentLength < startM);
        let interpolate = segmentLength === 0 ? 0 : (startM - segmentM) / segmentLength;
        const beginX = lerp(x1, x2, interpolate);
        const beginY = lerp(y1, y2, interpolate);
        const startOffset = offset - stride;
        const startLength = segmentM;
        const endM = startM + scale * measureAndCacheTextWidth(font, text, cache);
        while (offset < end - stride && segmentM + segmentLength < endM) {
            advance();
        }
        interpolate = segmentLength === 0 ? 0 : (endM - segmentM) / segmentLength;
        const endX = lerp(x1, x2, interpolate);
        const endY = lerp(y1, y2, interpolate);
        let reverse;
        if (rotation) {
            const flat = [ beginX, beginY, endX, endY ];
            rotate(flat, 0, 4, 2, rotation, flat, flat);
            reverse = flat[0] > flat[2];
        } else {
            reverse = beginX > endX;
        }
        const PI = Math.PI;
        const result = [];
        const singleSegment = startOffset + stride === offset;
        offset = startOffset;
        segmentLength = 0;
        segmentM = startLength;
        x2 = flatCoordinates[offset];
        y2 = flatCoordinates[offset + 1];
        let previousAngle;
        if (singleSegment) {
            advance();
            previousAngle = Math.atan2(y2 - y1, x2 - x1);
            if (reverse) {
                previousAngle += previousAngle > 0 ? -PI : PI;
            }
            const x = (endX + beginX) / 2;
            const y = (endY + beginY) / 2;
            result[0] = [ x, y, (endM - startM) / 2, previousAngle, text ];
            return result;
        }
        text = text.replace(/\n/g, " ");
        for (let i = 0, ii = text.length; i < ii; ) {
            advance();
            let angle = Math.atan2(y2 - y1, x2 - x1);
            if (reverse) {
                angle += angle > 0 ? -PI : PI;
            }
            if (previousAngle !== undefined) {
                let delta = angle - previousAngle;
                delta += delta > PI ? -2 * PI : delta < -PI ? 2 * PI : 0;
                if (Math.abs(delta) > maxAngle) {
                    return null;
                }
            }
            previousAngle = angle;
            const iStart = i;
            let charLength = 0;
            for (;i < ii; ++i) {
                const index = reverse ? ii - i - 1 : i;
                const len = scale * measureAndCacheTextWidth(font, text[index], cache);
                if (offset + stride < end && segmentM + segmentLength < startM + charLength + len / 2) {
                    break;
                }
                charLength += len;
            }
            if (i === iStart) {
                continue;
            }
            const chars = reverse ? text.substring(ii - iStart, ii - i) : text.substring(iStart, i);
            interpolate = segmentLength === 0 ? 0 : (startM + charLength / 2 - segmentM) / segmentLength;
            const x = lerp(x1, x2, interpolate);
            const y = lerp(y1, y2, interpolate);
            result.push([ x, y, charLength / 2, angle, chars ]);
            startM += charLength;
        }
        return result;
    }
    function lineStringLength(flatCoordinates, offset, end, stride) {
        let x1 = flatCoordinates[offset];
        let y1 = flatCoordinates[offset + 1];
        let length = 0;
        for (let i = offset + stride; i < end; i += stride) {
            const x2 = flatCoordinates[i];
            const y2 = flatCoordinates[i + 1];
            length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
            x1 = x2;
            y1 = y2;
        }
        return length;
    }
    const tmpExtent = createEmpty();
    const p1 = [];
    const p2 = [];
    const p3 = [];
    const p4 = [];
    function getDeclutterBox(replayImageOrLabelArgs) {
        return replayImageOrLabelArgs[3].declutterBox;
    }
    const rtlRegEx = new RegExp("[" + String.fromCharCode(1425) + "-" + String.fromCharCode(2303) + String.fromCharCode(64285) + "-" + String.fromCharCode(65023) + String.fromCharCode(65136) + "-" + String.fromCharCode(65276) + String.fromCharCode(67584) + "-" + String.fromCharCode(69631) + String.fromCharCode(124928) + "-" + String.fromCharCode(126975) + "]");
    function horizontalTextAlign(text, align) {
        if (align === "start") {
            align = rtlRegEx.test(text) ? "right" : "left";
        } else if (align === "end") {
            align = rtlRegEx.test(text) ? "left" : "right";
        }
        return TEXT_ALIGN[align];
    }
    function createTextChunks(acc, line, i) {
        if (i > 0) {
            acc.push("\n", "");
        }
        acc.push(line, "");
        return acc;
    }
    class Executor {
        constructor(resolution, pixelRatio, overlaps, instructions, deferredRendering) {
            this.overlaps = overlaps;
            this.pixelRatio = pixelRatio;
            this.resolution = resolution;
            this.alignAndScaleFill_;
            this.instructions = instructions.instructions;
            this.coordinates = instructions.coordinates;
            this.coordinateCache_ = {};
            this.renderedTransform_ = create();
            this.hitDetectionInstructions = instructions.hitDetectionInstructions;
            this.pixelCoordinates_ = null;
            this.viewRotation_ = 0;
            this.fillStates = instructions.fillStates || {};
            this.strokeStates = instructions.strokeStates || {};
            this.textStates = instructions.textStates || {};
            this.widths_ = {};
            this.labels_ = {};
            this.zIndexContext_ = deferredRendering ? new ZIndexContext : null;
        }
        getZIndexContext() {
            return this.zIndexContext_;
        }
        createLabel(text, textKey, fillKey, strokeKey) {
            const key = text + textKey + fillKey + strokeKey;
            if (this.labels_[key]) {
                return this.labels_[key];
            }
            const strokeState = strokeKey ? this.strokeStates[strokeKey] : null;
            const fillState = fillKey ? this.fillStates[fillKey] : null;
            const textState = this.textStates[textKey];
            const pixelRatio = this.pixelRatio;
            const scale = [ textState.scale[0] * pixelRatio, textState.scale[1] * pixelRatio ];
            const textIsArray = Array.isArray(text);
            const align = textState.justify ? TEXT_ALIGN[textState.justify] : horizontalTextAlign(Array.isArray(text) ? text[0] : text, textState.textAlign || defaultTextAlign);
            const strokeWidth = strokeKey && strokeState.lineWidth ? strokeState.lineWidth : 0;
            const chunks = textIsArray ? text : text.split("\n").reduce(createTextChunks, []);
            const {width: width, height: height, widths: widths, heights: heights, lineWidths: lineWidths} = getTextDimensions(textState, chunks);
            const renderWidth = width + strokeWidth;
            const contextInstructions = [];
            const w = (renderWidth + 2) * scale[0];
            const h = (height + strokeWidth) * scale[1];
            const label = {
                width: w < 0 ? Math.floor(w) : Math.ceil(w),
                height: h < 0 ? Math.floor(h) : Math.ceil(h),
                contextInstructions: contextInstructions
            };
            if (scale[0] != 1 || scale[1] != 1) {
                contextInstructions.push("scale", scale);
            }
            if (strokeKey) {
                contextInstructions.push("strokeStyle", strokeState.strokeStyle);
                contextInstructions.push("lineWidth", strokeWidth);
                contextInstructions.push("lineCap", strokeState.lineCap);
                contextInstructions.push("lineJoin", strokeState.lineJoin);
                contextInstructions.push("miterLimit", strokeState.miterLimit);
                contextInstructions.push("setLineDash", [ strokeState.lineDash ]);
                contextInstructions.push("lineDashOffset", strokeState.lineDashOffset);
            }
            if (fillKey) {
                contextInstructions.push("fillStyle", fillState.fillStyle);
            }
            contextInstructions.push("textBaseline", "middle");
            contextInstructions.push("textAlign", "center");
            const leftRight = .5 - align;
            let x = align * renderWidth + leftRight * strokeWidth;
            const strokeInstructions = [];
            const fillInstructions = [];
            let lineHeight = 0;
            let lineOffset = 0;
            let widthHeightIndex = 0;
            let lineWidthIndex = 0;
            let previousFont;
            for (let i = 0, ii = chunks.length; i < ii; i += 2) {
                const text = chunks[i];
                if (text === "\n") {
                    lineOffset += lineHeight;
                    lineHeight = 0;
                    x = align * renderWidth + leftRight * strokeWidth;
                    ++lineWidthIndex;
                    continue;
                }
                const font = chunks[i + 1] || textState.font;
                if (font !== previousFont) {
                    if (strokeKey) {
                        strokeInstructions.push("font", font);
                    }
                    if (fillKey) {
                        fillInstructions.push("font", font);
                    }
                    previousFont = font;
                }
                lineHeight = Math.max(lineHeight, heights[widthHeightIndex]);
                const fillStrokeArgs = [ text, x + leftRight * widths[widthHeightIndex] + align * (widths[widthHeightIndex] - lineWidths[lineWidthIndex]), .5 * (strokeWidth + lineHeight) + lineOffset ];
                x += widths[widthHeightIndex];
                if (strokeKey) {
                    strokeInstructions.push("strokeText", fillStrokeArgs);
                }
                if (fillKey) {
                    fillInstructions.push("fillText", fillStrokeArgs);
                }
                ++widthHeightIndex;
            }
            Array.prototype.push.apply(contextInstructions, strokeInstructions);
            Array.prototype.push.apply(contextInstructions, fillInstructions);
            this.labels_[key] = label;
            return label;
        }
        replayTextBackground_(context, p1, p2, p3, p4, fillInstruction, strokeInstruction) {
            context.beginPath();
            context.moveTo.apply(context, p1);
            context.lineTo.apply(context, p2);
            context.lineTo.apply(context, p3);
            context.lineTo.apply(context, p4);
            context.lineTo.apply(context, p1);
            if (fillInstruction) {
                this.alignAndScaleFill_ = fillInstruction[2];
                this.fill_(context);
            }
            if (strokeInstruction) {
                this.setStrokeStyle_(context, strokeInstruction);
                context.stroke();
            }
        }
        calculateImageOrLabelDimensions_(sheetWidth, sheetHeight, centerX, centerY, width, height, anchorX, anchorY, originX, originY, rotation, scale, snapToPixel, padding, fillStroke, feature) {
            anchorX *= scale[0];
            anchorY *= scale[1];
            let x = centerX - anchorX;
            let y = centerY - anchorY;
            const w = width + originX > sheetWidth ? sheetWidth - originX : width;
            const h = height + originY > sheetHeight ? sheetHeight - originY : height;
            const boxW = padding[3] + w * scale[0] + padding[1];
            const boxH = padding[0] + h * scale[1] + padding[2];
            const boxX = x - padding[3];
            const boxY = y - padding[0];
            if (fillStroke || rotation !== 0) {
                p1[0] = boxX;
                p4[0] = boxX;
                p1[1] = boxY;
                p2[1] = boxY;
                p2[0] = boxX + boxW;
                p3[0] = p2[0];
                p3[1] = boxY + boxH;
                p4[1] = p3[1];
            }
            let transform;
            if (rotation !== 0) {
                transform = compose(create(), centerX, centerY, 1, 1, rotation, -centerX, -centerY);
                apply(transform, p1);
                apply(transform, p2);
                apply(transform, p3);
                apply(transform, p4);
                createOrUpdate$2(Math.min(p1[0], p2[0], p3[0], p4[0]), Math.min(p1[1], p2[1], p3[1], p4[1]), Math.max(p1[0], p2[0], p3[0], p4[0]), Math.max(p1[1], p2[1], p3[1], p4[1]), tmpExtent);
            } else {
                createOrUpdate$2(Math.min(boxX, boxX + boxW), Math.min(boxY, boxY + boxH), Math.max(boxX, boxX + boxW), Math.max(boxY, boxY + boxH), tmpExtent);
            }
            if (snapToPixel) {
                x = Math.round(x);
                y = Math.round(y);
            }
            return {
                drawImageX: x,
                drawImageY: y,
                drawImageW: w,
                drawImageH: h,
                originX: originX,
                originY: originY,
                declutterBox: {
                    minX: tmpExtent[0],
                    minY: tmpExtent[1],
                    maxX: tmpExtent[2],
                    maxY: tmpExtent[3],
                    value: feature
                },
                canvasTransform: transform,
                scale: scale
            };
        }
        replayImageOrLabel_(context, scaledCanvasSize, imageOrLabel, dimensions, opacity, fillInstruction, strokeInstruction) {
            const fillStroke = !!(fillInstruction || strokeInstruction);
            const box = dimensions.declutterBox;
            const strokePadding = strokeInstruction ? strokeInstruction[2] * dimensions.scale[0] / 2 : 0;
            const intersects = box.minX - strokePadding <= scaledCanvasSize[0] && box.maxX + strokePadding >= 0 && box.minY - strokePadding <= scaledCanvasSize[1] && box.maxY + strokePadding >= 0;
            if (intersects) {
                if (fillStroke) {
                    this.replayTextBackground_(context, p1, p2, p3, p4, fillInstruction, strokeInstruction);
                }
                drawImageOrLabel(context, dimensions.canvasTransform, opacity, imageOrLabel, dimensions.originX, dimensions.originY, dimensions.drawImageW, dimensions.drawImageH, dimensions.drawImageX, dimensions.drawImageY, dimensions.scale);
            }
            return true;
        }
        fill_(context) {
            const alignAndScale = this.alignAndScaleFill_;
            if (alignAndScale) {
                const origin = apply(this.renderedTransform_, [ 0, 0 ]);
                const repeatSize = 512 * this.pixelRatio;
                context.save();
                context.translate(origin[0] % repeatSize, origin[1] % repeatSize);
                if (alignAndScale !== 1) {
                    context.scale(alignAndScale, alignAndScale);
                }
                context.rotate(this.viewRotation_);
            }
            context.fill();
            if (alignAndScale) {
                context.restore();
            }
        }
        setStrokeStyle_(context, instruction) {
            context.strokeStyle = instruction[1];
            context.lineWidth = instruction[2];
            context.lineCap = instruction[3];
            context.lineJoin = instruction[4];
            context.miterLimit = instruction[5];
            context.lineDashOffset = instruction[7];
            context.setLineDash(instruction[6]);
        }
        drawLabelWithPointPlacement_(text, textKey, strokeKey, fillKey) {
            const textState = this.textStates[textKey];
            const label = this.createLabel(text, textKey, fillKey, strokeKey);
            const strokeState = this.strokeStates[strokeKey];
            const pixelRatio = this.pixelRatio;
            const align = horizontalTextAlign(Array.isArray(text) ? text[0] : text, textState.textAlign || defaultTextAlign);
            const baseline = TEXT_ALIGN[textState.textBaseline || defaultTextBaseline];
            const strokeWidth = strokeState && strokeState.lineWidth ? strokeState.lineWidth : 0;
            const width = label.width / pixelRatio - 2 * textState.scale[0];
            const anchorX = align * width + 2 * (.5 - align) * strokeWidth;
            const anchorY = baseline * label.height / pixelRatio + 2 * (.5 - baseline) * strokeWidth;
            return {
                label: label,
                anchorX: anchorX,
                anchorY: anchorY
            };
        }
        execute_(context, scaledCanvasSize, transform, instructions, snapToPixel, featureCallback, hitExtent, declutterTree) {
            const zIndexContext = this.zIndexContext_;
            let pixelCoordinates;
            if (this.pixelCoordinates_ && equals$2(transform, this.renderedTransform_)) {
                pixelCoordinates = this.pixelCoordinates_;
            } else {
                if (!this.pixelCoordinates_) {
                    this.pixelCoordinates_ = [];
                }
                pixelCoordinates = transform2D(this.coordinates, 0, this.coordinates.length, 2, transform, this.pixelCoordinates_);
                setFromArray(this.renderedTransform_, transform);
            }
            let i = 0;
            const ii = instructions.length;
            let d = 0;
            let dd;
            let anchorX, anchorY, declutterMode, prevX, prevY, roundX, roundY, image, text, textKey, strokeKey, fillKey;
            let pendingFill = 0;
            let pendingStroke = 0;
            let lastFillInstruction = null;
            let lastStrokeInstruction = null;
            const coordinateCache = this.coordinateCache_;
            const viewRotation = this.viewRotation_;
            const viewRotationFromTransform = Math.round(Math.atan2(-transform[1], transform[0]) * 1e12) / 1e12;
            const state = {
                context: context,
                pixelRatio: this.pixelRatio,
                resolution: this.resolution,
                rotation: viewRotation
            };
            const batchSize = this.instructions != instructions || this.overlaps ? 0 : 200;
            let feature;
            let x, y, currentGeometry;
            while (i < ii) {
                const instruction = instructions[i];
                const type = instruction[0];
                switch (type) {
                  case Instruction.BEGIN_GEOMETRY:
                    feature = instruction[1];
                    currentGeometry = instruction[3];
                    if (!feature.getGeometry()) {
                        i = instruction[2];
                    } else if (hitExtent !== undefined && !intersects$1(hitExtent, currentGeometry.getExtent())) {
                        i = instruction[2] + 1;
                    } else {
                        ++i;
                    }
                    if (zIndexContext) {
                        zIndexContext.zIndex = instruction[4];
                    }
                    break;

                  case Instruction.BEGIN_PATH:
                    if (pendingFill > batchSize) {
                        this.fill_(context);
                        pendingFill = 0;
                    }
                    if (pendingStroke > batchSize) {
                        context.stroke();
                        pendingStroke = 0;
                    }
                    if (!pendingFill && !pendingStroke) {
                        context.beginPath();
                        prevX = NaN;
                        prevY = NaN;
                    }
                    ++i;
                    break;

                  case Instruction.CIRCLE:
                    d = instruction[1];
                    const x1 = pixelCoordinates[d];
                    const y1 = pixelCoordinates[d + 1];
                    const x2 = pixelCoordinates[d + 2];
                    const y2 = pixelCoordinates[d + 3];
                    const dx = x2 - x1;
                    const dy = y2 - y1;
                    const r = Math.sqrt(dx * dx + dy * dy);
                    context.moveTo(x1 + r, y1);
                    context.arc(x1, y1, r, 0, 2 * Math.PI, true);
                    ++i;
                    break;

                  case Instruction.CLOSE_PATH:
                    context.closePath();
                    ++i;
                    break;

                  case Instruction.CUSTOM:
                    d = instruction[1];
                    dd = instruction[2];
                    const geometry = instruction[3];
                    const renderer = instruction[4];
                    const fn = instruction[5];
                    state.geometry = geometry;
                    state.feature = feature;
                    if (!(i in coordinateCache)) {
                        coordinateCache[i] = [];
                    }
                    const coords = coordinateCache[i];
                    if (fn) {
                        fn(pixelCoordinates, d, dd, 2, coords);
                    } else {
                        coords[0] = pixelCoordinates[d];
                        coords[1] = pixelCoordinates[d + 1];
                        coords.length = 2;
                    }
                    if (zIndexContext) {
                        zIndexContext.zIndex = instruction[6];
                    }
                    renderer(coords, state);
                    ++i;
                    break;

                  case Instruction.DRAW_IMAGE:
                    d = instruction[1];
                    dd = instruction[2];
                    image = instruction[3];
                    anchorX = instruction[4];
                    anchorY = instruction[5];
                    let height = instruction[6];
                    const opacity = instruction[7];
                    const originX = instruction[8];
                    const originY = instruction[9];
                    const rotateWithView = instruction[10];
                    let rotation = instruction[11];
                    const scale = instruction[12];
                    let width = instruction[13];
                    declutterMode = instruction[14] || "declutter";
                    const declutterImageWithText = instruction[15];
                    if (!image && instruction.length >= 20) {
                        text = instruction[19];
                        textKey = instruction[20];
                        strokeKey = instruction[21];
                        fillKey = instruction[22];
                        const labelWithAnchor = this.drawLabelWithPointPlacement_(text, textKey, strokeKey, fillKey);
                        image = labelWithAnchor.label;
                        instruction[3] = image;
                        const textOffsetX = instruction[23];
                        anchorX = (labelWithAnchor.anchorX - textOffsetX) * this.pixelRatio;
                        instruction[4] = anchorX;
                        const textOffsetY = instruction[24];
                        anchorY = (labelWithAnchor.anchorY - textOffsetY) * this.pixelRatio;
                        instruction[5] = anchorY;
                        height = image.height;
                        instruction[6] = height;
                        width = image.width;
                        instruction[13] = width;
                    }
                    let geometryWidths;
                    if (instruction.length > 25) {
                        geometryWidths = instruction[25];
                    }
                    let padding, backgroundFill, backgroundStroke;
                    if (instruction.length > 17) {
                        padding = instruction[16];
                        backgroundFill = instruction[17];
                        backgroundStroke = instruction[18];
                    } else {
                        padding = defaultPadding;
                        backgroundFill = false;
                        backgroundStroke = false;
                    }
                    if (rotateWithView && viewRotationFromTransform) {
                        rotation += viewRotation;
                    } else if (!rotateWithView && !viewRotationFromTransform) {
                        rotation -= viewRotation;
                    }
                    let widthIndex = 0;
                    for (;d < dd; d += 2) {
                        if (geometryWidths && geometryWidths[widthIndex++] < width / this.pixelRatio) {
                            continue;
                        }
                        const dimensions = this.calculateImageOrLabelDimensions_(image.width, image.height, pixelCoordinates[d], pixelCoordinates[d + 1], width, height, anchorX, anchorY, originX, originY, rotation, scale, snapToPixel, padding, backgroundFill || backgroundStroke, feature);
                        const args = [ context, scaledCanvasSize, image, dimensions, opacity, backgroundFill ? lastFillInstruction : null, backgroundStroke ? lastStrokeInstruction : null ];
                        if (declutterTree) {
                            let imageArgs, imageDeclutterMode, imageDeclutterBox;
                            if (declutterImageWithText) {
                                const index = dd - d;
                                if (!declutterImageWithText[index]) {
                                    declutterImageWithText[index] = {
                                        args: args,
                                        declutterMode: declutterMode
                                    };
                                    continue;
                                }
                                const imageDeclutter = declutterImageWithText[index];
                                imageArgs = imageDeclutter.args;
                                imageDeclutterMode = imageDeclutter.declutterMode;
                                delete declutterImageWithText[index];
                                imageDeclutterBox = getDeclutterBox(imageArgs);
                            }
                            let renderImage, renderText;
                            if (imageArgs && (imageDeclutterMode !== "declutter" || !declutterTree.collides(imageDeclutterBox))) {
                                renderImage = true;
                            }
                            if (declutterMode !== "declutter" || !declutterTree.collides(dimensions.declutterBox)) {
                                renderText = true;
                            }
                            if (imageDeclutterMode === "declutter" && declutterMode === "declutter") {
                                const render = renderImage && renderText;
                                renderImage = render;
                                renderText = render;
                            }
                            if (renderImage) {
                                if (imageDeclutterMode !== "none") {
                                    declutterTree.insert(imageDeclutterBox);
                                }
                                this.replayImageOrLabel_.apply(this, imageArgs);
                            }
                            if (renderText) {
                                if (declutterMode !== "none") {
                                    declutterTree.insert(dimensions.declutterBox);
                                }
                                this.replayImageOrLabel_.apply(this, args);
                            }
                        } else {
                            this.replayImageOrLabel_.apply(this, args);
                        }
                    }
                    ++i;
                    break;

                  case Instruction.DRAW_CHARS:
                    const begin = instruction[1];
                    const end = instruction[2];
                    const baseline = instruction[3];
                    const overflow = instruction[4];
                    fillKey = instruction[5];
                    const maxAngle = instruction[6];
                    const measurePixelRatio = instruction[7];
                    const offsetY = instruction[8];
                    strokeKey = instruction[9];
                    const strokeWidth = instruction[10];
                    text = instruction[11];
                    textKey = instruction[12];
                    const pixelRatioScale = [ instruction[13], instruction[13] ];
                    declutterMode = instruction[14] || "declutter";
                    const textState = this.textStates[textKey];
                    const font = textState.font;
                    const textScale = [ textState.scale[0] * measurePixelRatio, textState.scale[1] * measurePixelRatio ];
                    let cachedWidths;
                    if (font in this.widths_) {
                        cachedWidths = this.widths_[font];
                    } else {
                        cachedWidths = {};
                        this.widths_[font] = cachedWidths;
                    }
                    const pathLength = lineStringLength(pixelCoordinates, begin, end, 2);
                    const textLength = Math.abs(textScale[0]) * measureAndCacheTextWidth(font, text, cachedWidths);
                    if (overflow || textLength <= pathLength) {
                        const textAlign = this.textStates[textKey].textAlign;
                        const startM = (pathLength - textLength) * horizontalTextAlign(text, textAlign);
                        const parts = drawTextOnPath(pixelCoordinates, begin, end, 2, text, startM, maxAngle, Math.abs(textScale[0]), measureAndCacheTextWidth, font, cachedWidths, viewRotationFromTransform ? 0 : this.viewRotation_);
                        drawChars: if (parts) {
                            const replayImageOrLabelArgs = [];
                            let c, cc, chars, label, part;
                            if (strokeKey) {
                                for (c = 0, cc = parts.length; c < cc; ++c) {
                                    part = parts[c];
                                    chars = part[4];
                                    label = this.createLabel(chars, textKey, "", strokeKey);
                                    anchorX = part[2] + (textScale[0] < 0 ? -strokeWidth : strokeWidth);
                                    anchorY = baseline * label.height + (.5 - baseline) * 2 * strokeWidth * textScale[1] / textScale[0] - offsetY;
                                    const dimensions = this.calculateImageOrLabelDimensions_(label.width, label.height, part[0], part[1], label.width, label.height, anchorX, anchorY, 0, 0, part[3], pixelRatioScale, false, defaultPadding, false, feature);
                                    if (declutterTree && declutterMode === "declutter" && declutterTree.collides(dimensions.declutterBox)) {
                                        break drawChars;
                                    }
                                    replayImageOrLabelArgs.push([ context, scaledCanvasSize, label, dimensions, 1, null, null ]);
                                }
                            }
                            if (fillKey) {
                                for (c = 0, cc = parts.length; c < cc; ++c) {
                                    part = parts[c];
                                    chars = part[4];
                                    label = this.createLabel(chars, textKey, fillKey, "");
                                    anchorX = part[2];
                                    anchorY = baseline * label.height - offsetY;
                                    const dimensions = this.calculateImageOrLabelDimensions_(label.width, label.height, part[0], part[1], label.width, label.height, anchorX, anchorY, 0, 0, part[3], pixelRatioScale, false, defaultPadding, false, feature);
                                    if (declutterTree && declutterMode === "declutter" && declutterTree.collides(dimensions.declutterBox)) {
                                        break drawChars;
                                    }
                                    replayImageOrLabelArgs.push([ context, scaledCanvasSize, label, dimensions, 1, null, null ]);
                                }
                            }
                            if (declutterTree && declutterMode !== "none") {
                                declutterTree.load(replayImageOrLabelArgs.map(getDeclutterBox));
                            }
                            for (let i = 0, ii = replayImageOrLabelArgs.length; i < ii; ++i) {
                                this.replayImageOrLabel_.apply(this, replayImageOrLabelArgs[i]);
                            }
                        }
                    }
                    ++i;
                    break;

                  case Instruction.END_GEOMETRY:
                    if (featureCallback !== undefined) {
                        feature = instruction[1];
                        const result = featureCallback(feature, currentGeometry, declutterMode);
                        if (result) {
                            return result;
                        }
                    }
                    ++i;
                    break;

                  case Instruction.FILL:
                    if (batchSize) {
                        pendingFill++;
                    } else {
                        this.fill_(context);
                    }
                    ++i;
                    break;

                  case Instruction.MOVE_TO_LINE_TO:
                    d = instruction[1];
                    dd = instruction[2];
                    x = pixelCoordinates[d];
                    y = pixelCoordinates[d + 1];
                    context.moveTo(x, y);
                    prevX = x + .5 | 0;
                    prevY = y + .5 | 0;
                    for (d += 2; d < dd; d += 2) {
                        x = pixelCoordinates[d];
                        y = pixelCoordinates[d + 1];
                        roundX = x + .5 | 0;
                        roundY = y + .5 | 0;
                        if (d == dd - 2 || roundX !== prevX || roundY !== prevY) {
                            context.lineTo(x, y);
                            prevX = roundX;
                            prevY = roundY;
                        }
                    }
                    ++i;
                    break;

                  case Instruction.SET_FILL_STYLE:
                    lastFillInstruction = instruction;
                    this.alignAndScaleFill_ = instruction[2];
                    if (pendingFill) {
                        this.fill_(context);
                        pendingFill = 0;
                        if (pendingStroke) {
                            context.stroke();
                            pendingStroke = 0;
                        }
                    }
                    context.fillStyle = instruction[1];
                    ++i;
                    break;

                  case Instruction.SET_STROKE_STYLE:
                    lastStrokeInstruction = instruction;
                    if (pendingStroke) {
                        context.stroke();
                        pendingStroke = 0;
                    }
                    this.setStrokeStyle_(context, instruction);
                    ++i;
                    break;

                  case Instruction.STROKE:
                    if (batchSize) {
                        pendingStroke++;
                    } else {
                        context.stroke();
                    }
                    ++i;
                    break;

                  default:
                    ++i;
                    break;
                }
            }
            if (pendingFill) {
                this.fill_(context);
            }
            if (pendingStroke) {
                context.stroke();
            }
            return undefined;
        }
        execute(context, scaledCanvasSize, transform, viewRotation, snapToPixel, declutterTree) {
            this.viewRotation_ = viewRotation;
            this.execute_(context, scaledCanvasSize, transform, this.instructions, snapToPixel, undefined, undefined, declutterTree);
        }
        executeHitDetection(context, transform, viewRotation, featureCallback, hitExtent) {
            this.viewRotation_ = viewRotation;
            return this.execute_(context, [ context.canvas.width, context.canvas.height ], transform, this.hitDetectionInstructions, true, featureCallback, hitExtent);
        }
    }
    const ALL = [ "Polygon", "Circle", "LineString", "Image", "Text", "Default" ];
    const DECLUTTER = [ "Image", "Text" ];
    const NON_DECLUTTER = ALL.filter(builderType => !DECLUTTER.includes(builderType));
    class ExecutorGroup {
        constructor(maxExtent, resolution, pixelRatio, overlaps, allInstructions, renderBuffer, deferredRendering) {
            this.maxExtent_ = maxExtent;
            this.overlaps_ = overlaps;
            this.pixelRatio_ = pixelRatio;
            this.resolution_ = resolution;
            this.renderBuffer_ = renderBuffer;
            this.executorsByZIndex_ = {};
            this.hitDetectionContext_ = null;
            this.hitDetectionTransform_ = create();
            this.renderedContext_ = null;
            this.deferredZIndexContexts_ = {};
            this.createExecutors_(allInstructions, deferredRendering);
        }
        clip(context, transform) {
            const flatClipCoords = this.getClipCoords(transform);
            context.beginPath();
            context.moveTo(flatClipCoords[0], flatClipCoords[1]);
            context.lineTo(flatClipCoords[2], flatClipCoords[3]);
            context.lineTo(flatClipCoords[4], flatClipCoords[5]);
            context.lineTo(flatClipCoords[6], flatClipCoords[7]);
            context.clip();
        }
        createExecutors_(allInstructions, deferredRendering) {
            for (const zIndex in allInstructions) {
                let executors = this.executorsByZIndex_[zIndex];
                if (executors === undefined) {
                    executors = {};
                    this.executorsByZIndex_[zIndex] = executors;
                }
                const instructionByZindex = allInstructions[zIndex];
                for (const builderType in instructionByZindex) {
                    const instructions = instructionByZindex[builderType];
                    executors[builderType] = new Executor(this.resolution_, this.pixelRatio_, this.overlaps_, instructions, deferredRendering);
                }
            }
        }
        hasExecutors(executors) {
            for (const zIndex in this.executorsByZIndex_) {
                const candidates = this.executorsByZIndex_[zIndex];
                for (let i = 0, ii = executors.length; i < ii; ++i) {
                    if (executors[i] in candidates) {
                        return true;
                    }
                }
            }
            return false;
        }
        forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance, callback, declutteredFeatures) {
            hitTolerance = Math.round(hitTolerance);
            const contextSize = hitTolerance * 2 + 1;
            const transform = compose(this.hitDetectionTransform_, hitTolerance + .5, hitTolerance + .5, 1 / resolution, -1 / resolution, -rotation, -coordinate[0], -coordinate[1]);
            const newContext = !this.hitDetectionContext_;
            if (newContext) {
                this.hitDetectionContext_ = createCanvasContext2D(contextSize, contextSize, undefined, {
                    willReadFrequently: true
                });
            }
            const context = this.hitDetectionContext_;
            if (context.canvas.width !== contextSize || context.canvas.height !== contextSize) {
                context.canvas.width = contextSize;
                context.canvas.height = contextSize;
            } else if (!newContext) {
                context.clearRect(0, 0, contextSize, contextSize);
            }
            let hitExtent;
            if (this.renderBuffer_ !== undefined) {
                hitExtent = createEmpty();
                extendCoordinate(hitExtent, coordinate);
                buffer(hitExtent, resolution * (this.renderBuffer_ + hitTolerance), hitExtent);
            }
            const indexes = getPixelIndexArray(hitTolerance);
            let builderType;
            function featureCallback(feature, geometry, declutterMode) {
                const imageData = context.getImageData(0, 0, contextSize, contextSize).data;
                for (let i = 0, ii = indexes.length; i < ii; i++) {
                    if (imageData[indexes[i]] > 0) {
                        if (!declutteredFeatures || declutterMode === "none" || builderType !== "Image" && builderType !== "Text" || declutteredFeatures.includes(feature)) {
                            const idx = (indexes[i] - 3) / 4;
                            const x = hitTolerance - idx % contextSize;
                            const y = hitTolerance - (idx / contextSize | 0);
                            const result = callback(feature, geometry, x * x + y * y);
                            if (result) {
                                return result;
                            }
                        }
                        context.clearRect(0, 0, contextSize, contextSize);
                        break;
                    }
                }
                return undefined;
            }
            const zs = Object.keys(this.executorsByZIndex_).map(Number);
            zs.sort(ascending);
            let i, j, executors, executor, result;
            for (i = zs.length - 1; i >= 0; --i) {
                const zIndexKey = zs[i].toString();
                executors = this.executorsByZIndex_[zIndexKey];
                for (j = ALL.length - 1; j >= 0; --j) {
                    builderType = ALL[j];
                    executor = executors[builderType];
                    if (executor !== undefined) {
                        result = executor.executeHitDetection(context, transform, rotation, featureCallback, hitExtent);
                        if (result) {
                            return result;
                        }
                    }
                }
            }
            return undefined;
        }
        getClipCoords(transform) {
            const maxExtent = this.maxExtent_;
            if (!maxExtent) {
                return null;
            }
            const minX = maxExtent[0];
            const minY = maxExtent[1];
            const maxX = maxExtent[2];
            const maxY = maxExtent[3];
            const flatClipCoords = [ minX, minY, minX, maxY, maxX, maxY, maxX, minY ];
            transform2D(flatClipCoords, 0, 8, 2, transform, flatClipCoords);
            return flatClipCoords;
        }
        isEmpty() {
            return isEmpty$1(this.executorsByZIndex_);
        }
        execute(targetContext, scaledCanvasSize, transform, viewRotation, snapToPixel, builderTypes, declutterTree) {
            const zs = Object.keys(this.executorsByZIndex_).map(Number);
            zs.sort(ascending);
            builderTypes = builderTypes ? builderTypes : ALL;
            const maxBuilderTypes = ALL.length;
            let i, ii, j, jj, replays;
            if (declutterTree) {
                zs.reverse();
            }
            for (i = 0, ii = zs.length; i < ii; ++i) {
                const zIndexKey = zs[i].toString();
                replays = this.executorsByZIndex_[zIndexKey];
                for (j = 0, jj = builderTypes.length; j < jj; ++j) {
                    const builderType = builderTypes[j];
                    const replay = replays[builderType];
                    if (replay !== undefined) {
                        const zIndexContext = declutterTree === null ? undefined : replay.getZIndexContext();
                        const context = zIndexContext ? zIndexContext.getContext() : targetContext;
                        const requireClip = this.maxExtent_ && builderType !== "Image" && builderType !== "Text";
                        if (requireClip) {
                            context.save();
                            this.clip(context, transform);
                        }
                        if (!zIndexContext || builderType === "Text" || builderType === "Image") {
                            replay.execute(context, scaledCanvasSize, transform, viewRotation, snapToPixel, declutterTree);
                        } else {
                            zIndexContext.pushFunction(context => replay.execute(context, scaledCanvasSize, transform, viewRotation, snapToPixel, declutterTree));
                        }
                        if (requireClip) {
                            context.restore();
                        }
                        if (zIndexContext) {
                            zIndexContext.offset();
                            const index = zs[i] * maxBuilderTypes + j;
                            if (!this.deferredZIndexContexts_[index]) {
                                this.deferredZIndexContexts_[index] = [];
                            }
                            this.deferredZIndexContexts_[index].push(zIndexContext);
                        }
                    }
                }
            }
            this.renderedContext_ = targetContext;
        }
        getDeferredZIndexContexts() {
            return this.deferredZIndexContexts_;
        }
        getRenderedContext() {
            return this.renderedContext_;
        }
        renderDeferred() {
            const deferredZIndexContexts = this.deferredZIndexContexts_;
            const zs = Object.keys(deferredZIndexContexts).map(Number).sort(ascending);
            for (let i = 0, ii = zs.length; i < ii; ++i) {
                deferredZIndexContexts[zs[i]].forEach(zIndexContext => {
                    zIndexContext.draw(this.renderedContext_);
                    zIndexContext.clear();
                });
                deferredZIndexContexts[zs[i]].length = 0;
            }
        }
    }
    const circlePixelIndexArrayCache = {};
    function getPixelIndexArray(radius) {
        if (circlePixelIndexArrayCache[radius] !== undefined) {
            return circlePixelIndexArrayCache[radius];
        }
        const size = radius * 2 + 1;
        const maxDistanceSq = radius * radius;
        const distances = new Array(maxDistanceSq + 1);
        for (let i = 0; i <= radius; ++i) {
            for (let j = 0; j <= radius; ++j) {
                const distanceSq = i * i + j * j;
                if (distanceSq > maxDistanceSq) {
                    break;
                }
                let distance = distances[distanceSq];
                if (!distance) {
                    distance = [];
                    distances[distanceSq] = distance;
                }
                distance.push(((radius + i) * size + (radius + j)) * 4 + 3);
                if (i > 0) {
                    distance.push(((radius - i) * size + (radius + j)) * 4 + 3);
                }
                if (j > 0) {
                    distance.push(((radius + i) * size + (radius - j)) * 4 + 3);
                    if (i > 0) {
                        distance.push(((radius - i) * size + (radius - j)) * 4 + 3);
                    }
                }
            }
        }
        const pixelIndex = [];
        for (let i = 0, ii = distances.length; i < ii; ++i) {
            if (distances[i]) {
                pixelIndex.push(...distances[i]);
            }
        }
        circlePixelIndexArrayCache[radius] = pixelIndex;
        return pixelIndex;
    }
    class CanvasImmediateRenderer extends VectorContext {
        constructor(context, pixelRatio, extent, transform, viewRotation, squaredTolerance, userTransform) {
            super();
            this.context_ = context;
            this.pixelRatio_ = pixelRatio;
            this.extent_ = extent;
            this.transform_ = transform;
            this.transformRotation_ = transform ? toFixed(Math.atan2(transform[1], transform[0]), 10) : 0;
            this.viewRotation_ = viewRotation;
            this.squaredTolerance_ = squaredTolerance;
            this.userTransform_ = userTransform;
            this.contextFillState_ = null;
            this.contextStrokeState_ = null;
            this.contextTextState_ = null;
            this.fillState_ = null;
            this.strokeState_ = null;
            this.image_ = null;
            this.imageAnchorX_ = 0;
            this.imageAnchorY_ = 0;
            this.imageHeight_ = 0;
            this.imageOpacity_ = 0;
            this.imageOriginX_ = 0;
            this.imageOriginY_ = 0;
            this.imageRotateWithView_ = false;
            this.imageRotation_ = 0;
            this.imageScale_ = [ 0, 0 ];
            this.imageWidth_ = 0;
            this.text_ = "";
            this.textOffsetX_ = 0;
            this.textOffsetY_ = 0;
            this.textRotateWithView_ = false;
            this.textRotation_ = 0;
            this.textScale_ = [ 0, 0 ];
            this.textFillState_ = null;
            this.textStrokeState_ = null;
            this.textState_ = null;
            this.pixelCoordinates_ = [];
            this.tmpLocalTransform_ = create();
        }
        drawImages_(flatCoordinates, offset, end, stride) {
            if (!this.image_) {
                return;
            }
            const pixelCoordinates = transform2D(flatCoordinates, offset, end, stride, this.transform_, this.pixelCoordinates_);
            const context = this.context_;
            const localTransform = this.tmpLocalTransform_;
            const alpha = context.globalAlpha;
            if (this.imageOpacity_ != 1) {
                context.globalAlpha = alpha * this.imageOpacity_;
            }
            let rotation = this.imageRotation_;
            if (this.transformRotation_ === 0) {
                rotation -= this.viewRotation_;
            }
            if (this.imageRotateWithView_) {
                rotation += this.viewRotation_;
            }
            for (let i = 0, ii = pixelCoordinates.length; i < ii; i += 2) {
                const x = pixelCoordinates[i] - this.imageAnchorX_;
                const y = pixelCoordinates[i + 1] - this.imageAnchorY_;
                if (rotation !== 0 || this.imageScale_[0] != 1 || this.imageScale_[1] != 1) {
                    const centerX = x + this.imageAnchorX_;
                    const centerY = y + this.imageAnchorY_;
                    compose(localTransform, centerX, centerY, 1, 1, rotation, -centerX, -centerY);
                    context.save();
                    context.transform.apply(context, localTransform);
                    context.translate(centerX, centerY);
                    context.scale(this.imageScale_[0], this.imageScale_[1]);
                    context.drawImage(this.image_, this.imageOriginX_, this.imageOriginY_, this.imageWidth_, this.imageHeight_, -this.imageAnchorX_, -this.imageAnchorY_, this.imageWidth_, this.imageHeight_);
                    context.restore();
                } else {
                    context.drawImage(this.image_, this.imageOriginX_, this.imageOriginY_, this.imageWidth_, this.imageHeight_, x, y, this.imageWidth_, this.imageHeight_);
                }
            }
            if (this.imageOpacity_ != 1) {
                context.globalAlpha = alpha;
            }
        }
        drawText_(flatCoordinates, offset, end, stride) {
            if (!this.textState_ || this.text_ === "") {
                return;
            }
            if (this.textFillState_) {
                this.setContextFillState_(this.textFillState_);
            }
            if (this.textStrokeState_) {
                this.setContextStrokeState_(this.textStrokeState_);
            }
            this.setContextTextState_(this.textState_);
            const pixelCoordinates = transform2D(flatCoordinates, offset, end, stride, this.transform_, this.pixelCoordinates_);
            const context = this.context_;
            let rotation = this.textRotation_;
            if (this.transformRotation_ === 0) {
                rotation -= this.viewRotation_;
            }
            if (this.textRotateWithView_) {
                rotation += this.viewRotation_;
            }
            for (;offset < end; offset += stride) {
                const x = pixelCoordinates[offset] + this.textOffsetX_;
                const y = pixelCoordinates[offset + 1] + this.textOffsetY_;
                if (rotation !== 0 || this.textScale_[0] != 1 || this.textScale_[1] != 1) {
                    context.save();
                    context.translate(x - this.textOffsetX_, y - this.textOffsetY_);
                    context.rotate(rotation);
                    context.translate(this.textOffsetX_, this.textOffsetY_);
                    context.scale(this.textScale_[0], this.textScale_[1]);
                    if (this.textStrokeState_) {
                        context.strokeText(this.text_, 0, 0);
                    }
                    if (this.textFillState_) {
                        context.fillText(this.text_, 0, 0);
                    }
                    context.restore();
                } else {
                    if (this.textStrokeState_) {
                        context.strokeText(this.text_, x, y);
                    }
                    if (this.textFillState_) {
                        context.fillText(this.text_, x, y);
                    }
                }
            }
        }
        moveToLineTo_(flatCoordinates, offset, end, stride, close) {
            const context = this.context_;
            const pixelCoordinates = transform2D(flatCoordinates, offset, end, stride, this.transform_, this.pixelCoordinates_);
            context.moveTo(pixelCoordinates[0], pixelCoordinates[1]);
            let length = pixelCoordinates.length;
            if (close) {
                length -= 2;
            }
            for (let i = 2; i < length; i += 2) {
                context.lineTo(pixelCoordinates[i], pixelCoordinates[i + 1]);
            }
            if (close) {
                context.closePath();
            }
            return end;
        }
        drawRings_(flatCoordinates, offset, ends, stride) {
            for (let i = 0, ii = ends.length; i < ii; ++i) {
                offset = this.moveToLineTo_(flatCoordinates, offset, ends[i], stride, true);
            }
            return offset;
        }
        drawCircle(geometry) {
            if (this.squaredTolerance_) {
                geometry = geometry.simplifyTransformed(this.squaredTolerance_, this.userTransform_);
            }
            if (!intersects$1(this.extent_, geometry.getExtent())) {
                return;
            }
            if (this.fillState_ || this.strokeState_) {
                if (this.fillState_) {
                    this.setContextFillState_(this.fillState_);
                }
                if (this.strokeState_) {
                    this.setContextStrokeState_(this.strokeState_);
                }
                const pixelCoordinates = transformGeom2D(geometry, this.transform_, this.pixelCoordinates_);
                const dx = pixelCoordinates[2] - pixelCoordinates[0];
                const dy = pixelCoordinates[3] - pixelCoordinates[1];
                const radius = Math.sqrt(dx * dx + dy * dy);
                const context = this.context_;
                context.beginPath();
                context.arc(pixelCoordinates[0], pixelCoordinates[1], radius, 0, 2 * Math.PI);
                if (this.fillState_) {
                    context.fill();
                }
                if (this.strokeState_) {
                    context.stroke();
                }
            }
            if (this.text_ !== "") {
                this.drawText_(geometry.getCenter(), 0, 2, 2);
            }
        }
        setStyle(style) {
            this.setFillStrokeStyle(style.getFill(), style.getStroke());
            this.setImageStyle(style.getImage());
            this.setTextStyle(style.getText());
        }
        setTransform(transform) {
            this.transform_ = transform;
        }
        drawGeometry(geometry) {
            const type = geometry.getType();
            switch (type) {
              case "Point":
                this.drawPoint(geometry);
                break;

              case "LineString":
                this.drawLineString(geometry);
                break;

              case "Polygon":
                this.drawPolygon(geometry);
                break;

              case "MultiPoint":
                this.drawMultiPoint(geometry);
                break;

              case "MultiLineString":
                this.drawMultiLineString(geometry);
                break;

              case "MultiPolygon":
                this.drawMultiPolygon(geometry);
                break;

              case "GeometryCollection":
                this.drawGeometryCollection(geometry);
                break;

              case "Circle":
                this.drawCircle(geometry);
                break;
            }
        }
        drawFeature(feature, style) {
            const geometry = style.getGeometryFunction()(feature);
            if (!geometry) {
                return;
            }
            this.setStyle(style);
            this.drawGeometry(geometry);
        }
        drawGeometryCollection(geometry) {
            const geometries = geometry.getGeometriesArray();
            for (let i = 0, ii = geometries.length; i < ii; ++i) {
                this.drawGeometry(geometries[i]);
            }
        }
        drawPoint(geometry) {
            if (this.squaredTolerance_) {
                geometry = geometry.simplifyTransformed(this.squaredTolerance_, this.userTransform_);
            }
            const flatCoordinates = geometry.getFlatCoordinates();
            const stride = geometry.getStride();
            if (this.image_) {
                this.drawImages_(flatCoordinates, 0, flatCoordinates.length, stride);
            }
            if (this.text_ !== "") {
                this.drawText_(flatCoordinates, 0, flatCoordinates.length, stride);
            }
        }
        drawMultiPoint(geometry) {
            if (this.squaredTolerance_) {
                geometry = geometry.simplifyTransformed(this.squaredTolerance_, this.userTransform_);
            }
            const flatCoordinates = geometry.getFlatCoordinates();
            const stride = geometry.getStride();
            if (this.image_) {
                this.drawImages_(flatCoordinates, 0, flatCoordinates.length, stride);
            }
            if (this.text_ !== "") {
                this.drawText_(flatCoordinates, 0, flatCoordinates.length, stride);
            }
        }
        drawLineString(geometry) {
            if (this.squaredTolerance_) {
                geometry = geometry.simplifyTransformed(this.squaredTolerance_, this.userTransform_);
            }
            if (!intersects$1(this.extent_, geometry.getExtent())) {
                return;
            }
            if (this.strokeState_) {
                this.setContextStrokeState_(this.strokeState_);
                const context = this.context_;
                const flatCoordinates = geometry.getFlatCoordinates();
                context.beginPath();
                this.moveToLineTo_(flatCoordinates, 0, flatCoordinates.length, geometry.getStride(), false);
                context.stroke();
            }
            if (this.text_ !== "") {
                const flatMidpoint = geometry.getFlatMidpoint();
                this.drawText_(flatMidpoint, 0, 2, 2);
            }
        }
        drawMultiLineString(geometry) {
            if (this.squaredTolerance_) {
                geometry = geometry.simplifyTransformed(this.squaredTolerance_, this.userTransform_);
            }
            const geometryExtent = geometry.getExtent();
            if (!intersects$1(this.extent_, geometryExtent)) {
                return;
            }
            if (this.strokeState_) {
                this.setContextStrokeState_(this.strokeState_);
                const context = this.context_;
                const flatCoordinates = geometry.getFlatCoordinates();
                let offset = 0;
                const ends = geometry.getEnds();
                const stride = geometry.getStride();
                context.beginPath();
                for (let i = 0, ii = ends.length; i < ii; ++i) {
                    offset = this.moveToLineTo_(flatCoordinates, offset, ends[i], stride, false);
                }
                context.stroke();
            }
            if (this.text_ !== "") {
                const flatMidpoints = geometry.getFlatMidpoints();
                this.drawText_(flatMidpoints, 0, flatMidpoints.length, 2);
            }
        }
        drawPolygon(geometry) {
            if (this.squaredTolerance_) {
                geometry = geometry.simplifyTransformed(this.squaredTolerance_, this.userTransform_);
            }
            if (!intersects$1(this.extent_, geometry.getExtent())) {
                return;
            }
            if (this.strokeState_ || this.fillState_) {
                if (this.fillState_) {
                    this.setContextFillState_(this.fillState_);
                }
                if (this.strokeState_) {
                    this.setContextStrokeState_(this.strokeState_);
                }
                const context = this.context_;
                context.beginPath();
                this.drawRings_(geometry.getOrientedFlatCoordinates(), 0, geometry.getEnds(), geometry.getStride());
                if (this.fillState_) {
                    context.fill();
                }
                if (this.strokeState_) {
                    context.stroke();
                }
            }
            if (this.text_ !== "") {
                const flatInteriorPoint = geometry.getFlatInteriorPoint();
                this.drawText_(flatInteriorPoint, 0, 2, 2);
            }
        }
        drawMultiPolygon(geometry) {
            if (this.squaredTolerance_) {
                geometry = geometry.simplifyTransformed(this.squaredTolerance_, this.userTransform_);
            }
            if (!intersects$1(this.extent_, geometry.getExtent())) {
                return;
            }
            if (this.strokeState_ || this.fillState_) {
                if (this.fillState_) {
                    this.setContextFillState_(this.fillState_);
                }
                if (this.strokeState_) {
                    this.setContextStrokeState_(this.strokeState_);
                }
                const context = this.context_;
                const flatCoordinates = geometry.getOrientedFlatCoordinates();
                let offset = 0;
                const endss = geometry.getEndss();
                const stride = geometry.getStride();
                context.beginPath();
                for (let i = 0, ii = endss.length; i < ii; ++i) {
                    const ends = endss[i];
                    offset = this.drawRings_(flatCoordinates, offset, ends, stride);
                }
                if (this.fillState_) {
                    context.fill();
                }
                if (this.strokeState_) {
                    context.stroke();
                }
            }
            if (this.text_ !== "") {
                const flatInteriorPoints = geometry.getFlatInteriorPoints();
                this.drawText_(flatInteriorPoints, 0, flatInteriorPoints.length, 2);
            }
        }
        setContextFillState_(fillState) {
            const context = this.context_;
            const contextFillState = this.contextFillState_;
            if (!contextFillState) {
                context.fillStyle = fillState.fillStyle;
                this.contextFillState_ = {
                    fillStyle: fillState.fillStyle
                };
            } else {
                if (contextFillState.fillStyle != fillState.fillStyle) {
                    contextFillState.fillStyle = fillState.fillStyle;
                    context.fillStyle = fillState.fillStyle;
                }
            }
        }
        setContextStrokeState_(strokeState) {
            const context = this.context_;
            const contextStrokeState = this.contextStrokeState_;
            if (!contextStrokeState) {
                context.lineCap = strokeState.lineCap;
                context.setLineDash(strokeState.lineDash);
                context.lineDashOffset = strokeState.lineDashOffset;
                context.lineJoin = strokeState.lineJoin;
                context.lineWidth = strokeState.lineWidth;
                context.miterLimit = strokeState.miterLimit;
                context.strokeStyle = strokeState.strokeStyle;
                this.contextStrokeState_ = {
                    lineCap: strokeState.lineCap,
                    lineDash: strokeState.lineDash,
                    lineDashOffset: strokeState.lineDashOffset,
                    lineJoin: strokeState.lineJoin,
                    lineWidth: strokeState.lineWidth,
                    miterLimit: strokeState.miterLimit,
                    strokeStyle: strokeState.strokeStyle
                };
            } else {
                if (contextStrokeState.lineCap != strokeState.lineCap) {
                    contextStrokeState.lineCap = strokeState.lineCap;
                    context.lineCap = strokeState.lineCap;
                }
                if (!equals$2(contextStrokeState.lineDash, strokeState.lineDash)) {
                    context.setLineDash(contextStrokeState.lineDash = strokeState.lineDash);
                }
                if (contextStrokeState.lineDashOffset != strokeState.lineDashOffset) {
                    contextStrokeState.lineDashOffset = strokeState.lineDashOffset;
                    context.lineDashOffset = strokeState.lineDashOffset;
                }
                if (contextStrokeState.lineJoin != strokeState.lineJoin) {
                    contextStrokeState.lineJoin = strokeState.lineJoin;
                    context.lineJoin = strokeState.lineJoin;
                }
                if (contextStrokeState.lineWidth != strokeState.lineWidth) {
                    contextStrokeState.lineWidth = strokeState.lineWidth;
                    context.lineWidth = strokeState.lineWidth;
                }
                if (contextStrokeState.miterLimit != strokeState.miterLimit) {
                    contextStrokeState.miterLimit = strokeState.miterLimit;
                    context.miterLimit = strokeState.miterLimit;
                }
                if (contextStrokeState.strokeStyle != strokeState.strokeStyle) {
                    contextStrokeState.strokeStyle = strokeState.strokeStyle;
                    context.strokeStyle = strokeState.strokeStyle;
                }
            }
        }
        setContextTextState_(textState) {
            const context = this.context_;
            const contextTextState = this.contextTextState_;
            const textAlign = textState.textAlign ? textState.textAlign : defaultTextAlign;
            if (!contextTextState) {
                context.font = textState.font;
                context.textAlign = textAlign;
                context.textBaseline = textState.textBaseline;
                this.contextTextState_ = {
                    font: textState.font,
                    textAlign: textAlign,
                    textBaseline: textState.textBaseline
                };
            } else {
                if (contextTextState.font != textState.font) {
                    contextTextState.font = textState.font;
                    context.font = textState.font;
                }
                if (contextTextState.textAlign != textAlign) {
                    contextTextState.textAlign = textAlign;
                    context.textAlign = textAlign;
                }
                if (contextTextState.textBaseline != textState.textBaseline) {
                    contextTextState.textBaseline = textState.textBaseline;
                    context.textBaseline = textState.textBaseline;
                }
            }
        }
        setFillStrokeStyle(fillStyle, strokeStyle) {
            if (!fillStyle) {
                this.fillState_ = null;
            } else {
                const fillStyleColor = fillStyle.getColor();
                this.fillState_ = {
                    fillStyle: asColorLike(fillStyleColor ? fillStyleColor : defaultFillStyle)
                };
            }
            if (!strokeStyle) {
                this.strokeState_ = null;
            } else {
                const strokeStyleColor = strokeStyle.getColor();
                const strokeStyleLineCap = strokeStyle.getLineCap();
                const strokeStyleLineDash = strokeStyle.getLineDash();
                const strokeStyleLineDashOffset = strokeStyle.getLineDashOffset();
                const strokeStyleLineJoin = strokeStyle.getLineJoin();
                const strokeStyleWidth = strokeStyle.getWidth();
                const strokeStyleMiterLimit = strokeStyle.getMiterLimit();
                const lineDash = strokeStyleLineDash ? strokeStyleLineDash : defaultLineDash;
                this.strokeState_ = {
                    lineCap: strokeStyleLineCap !== undefined ? strokeStyleLineCap : defaultLineCap,
                    lineDash: this.pixelRatio_ === 1 ? lineDash : lineDash.map(n => n * this.pixelRatio_),
                    lineDashOffset: (strokeStyleLineDashOffset ? strokeStyleLineDashOffset : defaultLineDashOffset) * this.pixelRatio_,
                    lineJoin: strokeStyleLineJoin !== undefined ? strokeStyleLineJoin : defaultLineJoin,
                    lineWidth: (strokeStyleWidth !== undefined ? strokeStyleWidth : defaultLineWidth) * this.pixelRatio_,
                    miterLimit: strokeStyleMiterLimit !== undefined ? strokeStyleMiterLimit : defaultMiterLimit,
                    strokeStyle: asColorLike(strokeStyleColor ? strokeStyleColor : defaultStrokeStyle)
                };
            }
        }
        setImageStyle(imageStyle) {
            let imageSize;
            if (!imageStyle || !(imageSize = imageStyle.getSize())) {
                this.image_ = null;
                return;
            }
            const imagePixelRatio = imageStyle.getPixelRatio(this.pixelRatio_);
            const imageAnchor = imageStyle.getAnchor();
            const imageOrigin = imageStyle.getOrigin();
            this.image_ = imageStyle.getImage(this.pixelRatio_);
            this.imageAnchorX_ = imageAnchor[0] * imagePixelRatio;
            this.imageAnchorY_ = imageAnchor[1] * imagePixelRatio;
            this.imageHeight_ = imageSize[1] * imagePixelRatio;
            this.imageOpacity_ = imageStyle.getOpacity();
            this.imageOriginX_ = imageOrigin[0];
            this.imageOriginY_ = imageOrigin[1];
            this.imageRotateWithView_ = imageStyle.getRotateWithView();
            this.imageRotation_ = imageStyle.getRotation();
            const imageScale = imageStyle.getScaleArray();
            this.imageScale_ = [ imageScale[0] * this.pixelRatio_ / imagePixelRatio, imageScale[1] * this.pixelRatio_ / imagePixelRatio ];
            this.imageWidth_ = imageSize[0] * imagePixelRatio;
        }
        setTextStyle(textStyle) {
            if (!textStyle) {
                this.text_ = "";
            } else {
                const textFillStyle = textStyle.getFill();
                if (!textFillStyle) {
                    this.textFillState_ = null;
                } else {
                    const textFillStyleColor = textFillStyle.getColor();
                    this.textFillState_ = {
                        fillStyle: asColorLike(textFillStyleColor ? textFillStyleColor : defaultFillStyle)
                    };
                }
                const textStrokeStyle = textStyle.getStroke();
                if (!textStrokeStyle) {
                    this.textStrokeState_ = null;
                } else {
                    const textStrokeStyleColor = textStrokeStyle.getColor();
                    const textStrokeStyleLineCap = textStrokeStyle.getLineCap();
                    const textStrokeStyleLineDash = textStrokeStyle.getLineDash();
                    const textStrokeStyleLineDashOffset = textStrokeStyle.getLineDashOffset();
                    const textStrokeStyleLineJoin = textStrokeStyle.getLineJoin();
                    const textStrokeStyleWidth = textStrokeStyle.getWidth();
                    const textStrokeStyleMiterLimit = textStrokeStyle.getMiterLimit();
                    this.textStrokeState_ = {
                        lineCap: textStrokeStyleLineCap !== undefined ? textStrokeStyleLineCap : defaultLineCap,
                        lineDash: textStrokeStyleLineDash ? textStrokeStyleLineDash : defaultLineDash,
                        lineDashOffset: textStrokeStyleLineDashOffset ? textStrokeStyleLineDashOffset : defaultLineDashOffset,
                        lineJoin: textStrokeStyleLineJoin !== undefined ? textStrokeStyleLineJoin : defaultLineJoin,
                        lineWidth: textStrokeStyleWidth !== undefined ? textStrokeStyleWidth : defaultLineWidth,
                        miterLimit: textStrokeStyleMiterLimit !== undefined ? textStrokeStyleMiterLimit : defaultMiterLimit,
                        strokeStyle: asColorLike(textStrokeStyleColor ? textStrokeStyleColor : defaultStrokeStyle)
                    };
                }
                const textFont = textStyle.getFont();
                const textOffsetX = textStyle.getOffsetX();
                const textOffsetY = textStyle.getOffsetY();
                const textRotateWithView = textStyle.getRotateWithView();
                const textRotation = textStyle.getRotation();
                const textScale = textStyle.getScaleArray();
                const textText = textStyle.getText();
                const textTextAlign = textStyle.getTextAlign();
                const textTextBaseline = textStyle.getTextBaseline();
                this.textState_ = {
                    font: textFont !== undefined ? textFont : defaultFont,
                    textAlign: textTextAlign !== undefined ? textTextAlign : defaultTextAlign,
                    textBaseline: textTextBaseline !== undefined ? textTextBaseline : defaultTextBaseline
                };
                this.text_ = textText !== undefined ? Array.isArray(textText) ? textText.reduce((acc, t, i) => acc += i % 2 ? " " : t, "") : textText : "";
                this.textOffsetX_ = textOffsetX !== undefined ? this.pixelRatio_ * textOffsetX : 0;
                this.textOffsetY_ = textOffsetY !== undefined ? this.pixelRatio_ * textOffsetY : 0;
                this.textRotateWithView_ = textRotateWithView !== undefined ? textRotateWithView : false;
                this.textRotation_ = textRotation !== undefined ? textRotation : 0;
                this.textScale_ = [ this.pixelRatio_ * textScale[0], this.pixelRatio_ * textScale[1] ];
            }
        }
    }
    const HIT_DETECT_RESOLUTION = .5;
    function createHitDetectionImageData(size, transforms, features, styleFunction, extent, resolution, rotation, squaredTolerance, projection) {
        const userExtent = extent;
        const width = size[0] * HIT_DETECT_RESOLUTION;
        const height = size[1] * HIT_DETECT_RESOLUTION;
        const context = createCanvasContext2D(width, height);
        context.imageSmoothingEnabled = false;
        const canvas = context.canvas;
        const renderer = new CanvasImmediateRenderer(context, HIT_DETECT_RESOLUTION, extent, null, rotation, squaredTolerance, null);
        const featureCount = features.length;
        const indexFactor = Math.floor((256 * 256 * 256 - 1) / featureCount);
        const featuresByZIndex = {};
        for (let i = 1; i <= featureCount; ++i) {
            const feature = features[i - 1];
            const featureStyleFunction = feature.getStyleFunction() || styleFunction;
            if (!featureStyleFunction) {
                continue;
            }
            let styles = featureStyleFunction(feature, resolution);
            if (!styles) {
                continue;
            }
            if (!Array.isArray(styles)) {
                styles = [ styles ];
            }
            const index = i * indexFactor;
            const color = index.toString(16).padStart(7, "#00000");
            for (let j = 0, jj = styles.length; j < jj; ++j) {
                const originalStyle = styles[j];
                const geometry = originalStyle.getGeometryFunction()(feature);
                if (!geometry || !intersects$1(userExtent, geometry.getExtent())) {
                    continue;
                }
                const style = originalStyle.clone();
                const fill = style.getFill();
                if (fill) {
                    fill.setColor(color);
                }
                const stroke = style.getStroke();
                if (stroke) {
                    stroke.setColor(color);
                    stroke.setLineDash(null);
                }
                style.setText(undefined);
                const image = originalStyle.getImage();
                if (image) {
                    const imgSize = image.getImageSize();
                    if (!imgSize) {
                        continue;
                    }
                    const imgContext = createCanvasContext2D(imgSize[0], imgSize[1], undefined, {
                        alpha: false
                    });
                    const img = imgContext.canvas;
                    imgContext.fillStyle = color;
                    imgContext.fillRect(0, 0, img.width, img.height);
                    style.setImage(new Icon({
                        img: img,
                        anchor: image.getAnchor(),
                        anchorXUnits: "pixels",
                        anchorYUnits: "pixels",
                        offset: image.getOrigin(),
                        opacity: 1,
                        size: image.getSize(),
                        scale: image.getScale(),
                        rotation: image.getRotation(),
                        rotateWithView: image.getRotateWithView()
                    }));
                }
                const zIndex = style.getZIndex() || 0;
                let byGeometryType = featuresByZIndex[zIndex];
                if (!byGeometryType) {
                    byGeometryType = {};
                    featuresByZIndex[zIndex] = byGeometryType;
                    byGeometryType["Polygon"] = [];
                    byGeometryType["Circle"] = [];
                    byGeometryType["LineString"] = [];
                    byGeometryType["Point"] = [];
                }
                const type = geometry.getType();
                if (type === "GeometryCollection") {
                    const geometries = geometry.getGeometriesArrayRecursive();
                    for (let i = 0, ii = geometries.length; i < ii; ++i) {
                        const geometry = geometries[i];
                        byGeometryType[geometry.getType().replace("Multi", "")].push(geometry, style);
                    }
                } else {
                    byGeometryType[type.replace("Multi", "")].push(geometry, style);
                }
            }
        }
        const zIndexKeys = Object.keys(featuresByZIndex).map(Number).sort(ascending);
        for (let i = 0, ii = zIndexKeys.length; i < ii; ++i) {
            const byGeometryType = featuresByZIndex[zIndexKeys[i]];
            for (const type in byGeometryType) {
                const geomAndStyle = byGeometryType[type];
                for (let j = 0, jj = geomAndStyle.length; j < jj; j += 2) {
                    renderer.setStyle(geomAndStyle[j + 1]);
                    for (let k = 0, kk = transforms.length; k < kk; ++k) {
                        renderer.setTransform(transforms[k]);
                        renderer.drawGeometry(geomAndStyle[j]);
                    }
                }
            }
        }
        return context.getImageData(0, 0, canvas.width, canvas.height);
    }
    function hitDetect(pixel, features, imageData) {
        const resultFeatures = [];
        if (imageData) {
            const x = Math.floor(Math.round(pixel[0]) * HIT_DETECT_RESOLUTION);
            const y = Math.floor(Math.round(pixel[1]) * HIT_DETECT_RESOLUTION);
            const index = (clamp(x, 0, imageData.width - 1) + clamp(y, 0, imageData.height - 1) * imageData.width) * 4;
            const r = imageData.data[index];
            const g = imageData.data[index + 1];
            const b = imageData.data[index + 2];
            const i = b + 256 * (g + 256 * r);
            const indexFactor = Math.floor((256 * 256 * 256 - 1) / features.length);
            if (i && i % indexFactor === 0) {
                resultFeatures.push(features[i / indexFactor - 1]);
            }
        }
        return resultFeatures;
    }
    const SIMPLIFY_TOLERANCE = .5;
    const GEOMETRY_RENDERERS = {
        Point: renderPointGeometry,
        LineString: renderLineStringGeometry,
        Polygon: renderPolygonGeometry,
        MultiPoint: renderMultiPointGeometry,
        MultiLineString: renderMultiLineStringGeometry,
        MultiPolygon: renderMultiPolygonGeometry,
        GeometryCollection: renderGeometryCollectionGeometry,
        Circle: renderCircleGeometry
    };
    function defaultOrder(feature1, feature2) {
        return parseInt(getUid(feature1), 10) - parseInt(getUid(feature2), 10);
    }
    function getSquaredTolerance(resolution, pixelRatio) {
        const tolerance = getTolerance(resolution, pixelRatio);
        return tolerance * tolerance;
    }
    function getTolerance(resolution, pixelRatio) {
        return SIMPLIFY_TOLERANCE * resolution / pixelRatio;
    }
    function renderCircleGeometry(builderGroup, geometry, style, feature, index) {
        const fillStyle = style.getFill();
        const strokeStyle = style.getStroke();
        if (fillStyle || strokeStyle) {
            const circleReplay = builderGroup.getBuilder(style.getZIndex(), "Circle");
            circleReplay.setFillStrokeStyle(fillStyle, strokeStyle);
            circleReplay.drawCircle(geometry, feature, index);
        }
        const textStyle = style.getText();
        if (textStyle && textStyle.getText()) {
            const textReplay = builderGroup.getBuilder(style.getZIndex(), "Text");
            textReplay.setTextStyle(textStyle);
            textReplay.drawText(geometry, feature);
        }
    }
    function renderFeature(replayGroup, feature, style, squaredTolerance, listener, transform, declutter, index) {
        const loadingPromises = [];
        const imageStyle = style.getImage();
        if (imageStyle) {
            let loading = true;
            const imageState = imageStyle.getImageState();
            if (imageState == ImageState.LOADED || imageState == ImageState.ERROR) {
                loading = false;
            } else {
                if (imageState == ImageState.IDLE) {
                    imageStyle.load();
                }
            }
            if (loading) {
                loadingPromises.push(imageStyle.ready());
            }
        }
        const fillStyle = style.getFill();
        if (fillStyle && fillStyle.loading()) {
            loadingPromises.push(fillStyle.ready());
        }
        const loading = loadingPromises.length > 0;
        if (loading) {
            Promise.all(loadingPromises).then(() => listener(null));
        }
        renderFeatureInternal(replayGroup, feature, style, squaredTolerance, transform, declutter, index);
        return loading;
    }
    function renderFeatureInternal(replayGroup, feature, style, squaredTolerance, transform, declutter, index) {
        const geometry = style.getGeometryFunction()(feature);
        if (!geometry) {
            return;
        }
        const simplifiedGeometry = geometry.simplifyTransformed(squaredTolerance, transform);
        const renderer = style.getRenderer();
        if (renderer) {
            renderGeometry(replayGroup, simplifiedGeometry, style, feature, index);
        } else {
            const geometryRenderer = GEOMETRY_RENDERERS[simplifiedGeometry.getType()];
            geometryRenderer(replayGroup, simplifiedGeometry, style, feature, index, declutter);
        }
    }
    function renderGeometry(replayGroup, geometry, style, feature, index) {
        if (geometry.getType() == "GeometryCollection") {
            const geometries = geometry.getGeometries();
            for (let i = 0, ii = geometries.length; i < ii; ++i) {
                renderGeometry(replayGroup, geometries[i], style, feature, index);
            }
            return;
        }
        const replay = replayGroup.getBuilder(style.getZIndex(), "Default");
        replay.drawCustom(geometry, feature, style.getRenderer(), style.getHitDetectionRenderer(), index);
    }
    function renderGeometryCollectionGeometry(replayGroup, geometry, style, feature, declutterBuilderGroup, index) {
        const geometries = geometry.getGeometriesArray();
        let i, ii;
        for (i = 0, ii = geometries.length; i < ii; ++i) {
            const geometryRenderer = GEOMETRY_RENDERERS[geometries[i].getType()];
            geometryRenderer(replayGroup, geometries[i], style, feature, declutterBuilderGroup, index);
        }
    }
    function renderLineStringGeometry(builderGroup, geometry, style, feature, index) {
        const strokeStyle = style.getStroke();
        if (strokeStyle) {
            const lineStringReplay = builderGroup.getBuilder(style.getZIndex(), "LineString");
            lineStringReplay.setFillStrokeStyle(null, strokeStyle);
            lineStringReplay.drawLineString(geometry, feature, index);
        }
        const textStyle = style.getText();
        if (textStyle && textStyle.getText()) {
            const textReplay = builderGroup.getBuilder(style.getZIndex(), "Text");
            textReplay.setTextStyle(textStyle);
            textReplay.drawText(geometry, feature, index);
        }
    }
    function renderMultiLineStringGeometry(builderGroup, geometry, style, feature, index) {
        const strokeStyle = style.getStroke();
        if (strokeStyle) {
            const lineStringReplay = builderGroup.getBuilder(style.getZIndex(), "LineString");
            lineStringReplay.setFillStrokeStyle(null, strokeStyle);
            lineStringReplay.drawMultiLineString(geometry, feature, index);
        }
        const textStyle = style.getText();
        if (textStyle && textStyle.getText()) {
            const textReplay = builderGroup.getBuilder(style.getZIndex(), "Text");
            textReplay.setTextStyle(textStyle);
            textReplay.drawText(geometry, feature, index);
        }
    }
    function renderMultiPolygonGeometry(builderGroup, geometry, style, feature, index) {
        const fillStyle = style.getFill();
        const strokeStyle = style.getStroke();
        if (strokeStyle || fillStyle) {
            const polygonReplay = builderGroup.getBuilder(style.getZIndex(), "Polygon");
            polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle);
            polygonReplay.drawMultiPolygon(geometry, feature, index);
        }
        const textStyle = style.getText();
        if (textStyle && textStyle.getText()) {
            const textReplay = builderGroup.getBuilder(style.getZIndex(), "Text");
            textReplay.setTextStyle(textStyle);
            textReplay.drawText(geometry, feature, index);
        }
    }
    function renderPointGeometry(builderGroup, geometry, style, feature, index, declutter) {
        const imageStyle = style.getImage();
        const textStyle = style.getText();
        const hasText = textStyle && textStyle.getText();
        const declutterImageWithText = declutter && imageStyle && hasText ? {} : undefined;
        if (imageStyle) {
            if (imageStyle.getImageState() != ImageState.LOADED) {
                return;
            }
            const imageReplay = builderGroup.getBuilder(style.getZIndex(), "Image");
            imageReplay.setImageStyle(imageStyle, declutterImageWithText);
            imageReplay.drawPoint(geometry, feature, index);
        }
        if (hasText) {
            const textReplay = builderGroup.getBuilder(style.getZIndex(), "Text");
            textReplay.setTextStyle(textStyle, declutterImageWithText);
            textReplay.drawText(geometry, feature, index);
        }
    }
    function renderMultiPointGeometry(builderGroup, geometry, style, feature, index, declutter) {
        const imageStyle = style.getImage();
        const hasImage = imageStyle && imageStyle.getOpacity() !== 0;
        const textStyle = style.getText();
        const hasText = textStyle && textStyle.getText();
        const declutterImageWithText = declutter && hasImage && hasText ? {} : undefined;
        if (hasImage) {
            if (imageStyle.getImageState() != ImageState.LOADED) {
                return;
            }
            const imageReplay = builderGroup.getBuilder(style.getZIndex(), "Image");
            imageReplay.setImageStyle(imageStyle, declutterImageWithText);
            imageReplay.drawMultiPoint(geometry, feature, index);
        }
        if (hasText) {
            const textReplay = builderGroup.getBuilder(style.getZIndex(), "Text");
            textReplay.setTextStyle(textStyle, declutterImageWithText);
            textReplay.drawText(geometry, feature, index);
        }
    }
    function renderPolygonGeometry(builderGroup, geometry, style, feature, index) {
        const fillStyle = style.getFill();
        const strokeStyle = style.getStroke();
        if (fillStyle || strokeStyle) {
            const polygonReplay = builderGroup.getBuilder(style.getZIndex(), "Polygon");
            polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle);
            polygonReplay.drawPolygon(geometry, feature, index);
        }
        const textStyle = style.getText();
        if (textStyle && textStyle.getText()) {
            const textReplay = builderGroup.getBuilder(style.getZIndex(), "Text");
            textReplay.setTextStyle(textStyle);
            textReplay.drawText(geometry, feature, index);
        }
    }
    class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
        constructor(vectorLayer) {
            super(vectorLayer);
            this.boundHandleStyleImageChange_ = this.handleStyleImageChange_.bind(this);
            this.animatingOrInteracting_;
            this.hitDetectionImageData_ = null;
            this.clipped_ = false;
            this.renderedFeatures_ = null;
            this.renderedRevision_ = -1;
            this.renderedResolution_ = NaN;
            this.renderedExtent_ = createEmpty();
            this.wrappedRenderedExtent_ = createEmpty();
            this.renderedRotation_;
            this.renderedCenter_ = null;
            this.renderedProjection_ = null;
            this.renderedPixelRatio_ = 1;
            this.renderedRenderOrder_ = null;
            this.renderedFrameDeclutter_;
            this.replayGroup_ = null;
            this.replayGroupChanged = true;
            this.clipping = true;
            this.targetContext_ = null;
            this.opacity_ = 1;
        }
        renderWorlds(executorGroup, frameState, declutterable) {
            const extent = frameState.extent;
            const viewState = frameState.viewState;
            const center = viewState.center;
            const resolution = viewState.resolution;
            const projection = viewState.projection;
            const rotation = viewState.rotation;
            const projectionExtent = projection.getExtent();
            const vectorSource = this.getLayer().getSource();
            const declutter = this.getLayer().getDeclutter();
            const pixelRatio = frameState.pixelRatio;
            const viewHints = frameState.viewHints;
            const snapToPixel = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]);
            const context = this.context;
            const width = Math.round(getWidth(extent) / resolution * pixelRatio);
            const height = Math.round(getHeight(extent) / resolution * pixelRatio);
            const multiWorld = vectorSource.getWrapX() && projection.canWrapX();
            const worldWidth = multiWorld ? getWidth(projectionExtent) : null;
            const endWorld = multiWorld ? Math.ceil((extent[2] - projectionExtent[2]) / worldWidth) + 1 : 1;
            let world = multiWorld ? Math.floor((extent[0] - projectionExtent[0]) / worldWidth) : 0;
            do {
                let transform = this.getRenderTransform(center, resolution, 0, pixelRatio, width, height, world * worldWidth);
                if (frameState.declutter) {
                    transform = transform.slice(0);
                }
                executorGroup.execute(context, [ context.canvas.width, context.canvas.height ], transform, rotation, snapToPixel, declutterable === undefined ? ALL : declutterable ? DECLUTTER : NON_DECLUTTER, declutterable ? declutter && frameState.declutter[declutter] : undefined);
            } while (++world < endWorld);
        }
        setDrawContext_() {
            if (this.opacity_ !== 1) {
                this.targetContext_ = this.context;
                this.context = createCanvasContext2D(this.context.canvas.width, this.context.canvas.height, canvasPool$1);
            }
        }
        resetDrawContext_() {
            if (this.opacity_ !== 1) {
                const alpha = this.targetContext_.globalAlpha;
                this.targetContext_.globalAlpha = this.opacity_;
                this.targetContext_.drawImage(this.context.canvas, 0, 0);
                this.targetContext_.globalAlpha = alpha;
                releaseCanvas(this.context);
                canvasPool$1.push(this.context.canvas);
                this.context = this.targetContext_;
                this.targetContext_ = null;
            }
        }
        renderDeclutter(frameState) {
            if (!this.replayGroup_ || !this.getLayer().getDeclutter()) {
                return;
            }
            this.renderWorlds(this.replayGroup_, frameState, true);
        }
        renderDeferredInternal(frameState) {
            if (!this.replayGroup_) {
                return;
            }
            this.replayGroup_.renderDeferred();
            if (this.clipped_) {
                this.context.restore();
            }
            this.resetDrawContext_();
        }
        renderFrame(frameState, target) {
            const layerState = frameState.layerStatesArray[frameState.layerIndex];
            this.opacity_ = layerState.opacity;
            const viewState = frameState.viewState;
            this.prepareContainer(frameState, target);
            const context = this.context;
            const replayGroup = this.replayGroup_;
            let render = replayGroup && !replayGroup.isEmpty();
            if (!render) {
                const hasRenderListeners = this.getLayer().hasListener(RenderEventType.PRERENDER) || this.getLayer().hasListener(RenderEventType.POSTRENDER);
                if (!hasRenderListeners) {
                    return null;
                }
            }
            this.setDrawContext_();
            this.preRender(context, frameState);
            viewState.projection;
            this.clipped_ = false;
            if (render && layerState.extent && this.clipping) {
                const layerExtent = fromUserExtent(layerState.extent);
                render = intersects$1(layerExtent, frameState.extent);
                this.clipped_ = render && !containsExtent(layerExtent, frameState.extent);
                if (this.clipped_) {
                    this.clipUnrotated(context, frameState, layerExtent);
                }
            }
            if (render) {
                this.renderWorlds(replayGroup, frameState, this.getLayer().getDeclutter() ? false : undefined);
            }
            if (!frameState.declutter && this.clipped_) {
                context.restore();
            }
            this.postRender(context, frameState);
            if (this.renderedRotation_ !== viewState.rotation) {
                this.renderedRotation_ = viewState.rotation;
                this.hitDetectionImageData_ = null;
            }
            if (!frameState.declutter) {
                this.resetDrawContext_();
            }
            return this.container;
        }
        getFeatures(pixel) {
            return new Promise(resolve => {
                if (this.frameState && !this.hitDetectionImageData_ && !this.animatingOrInteracting_) {
                    const size = this.frameState.size.slice();
                    const center = this.renderedCenter_;
                    const resolution = this.renderedResolution_;
                    const rotation = this.renderedRotation_;
                    const projection = this.renderedProjection_;
                    const extent = this.wrappedRenderedExtent_;
                    const layer = this.getLayer();
                    const transforms = [];
                    const width = size[0] * HIT_DETECT_RESOLUTION;
                    const height = size[1] * HIT_DETECT_RESOLUTION;
                    transforms.push(this.getRenderTransform(center, resolution, rotation, HIT_DETECT_RESOLUTION, width, height, 0).slice());
                    const source = layer.getSource();
                    const projectionExtent = projection.getExtent();
                    if (source.getWrapX() && projection.canWrapX() && !containsExtent(projectionExtent, extent)) {
                        let startX = extent[0];
                        const worldWidth = getWidth(projectionExtent);
                        let world = 0;
                        let offsetX;
                        while (startX < projectionExtent[0]) {
                            --world;
                            offsetX = worldWidth * world;
                            transforms.push(this.getRenderTransform(center, resolution, rotation, HIT_DETECT_RESOLUTION, width, height, offsetX).slice());
                            startX += worldWidth;
                        }
                        world = 0;
                        startX = extent[2];
                        while (startX > projectionExtent[2]) {
                            ++world;
                            offsetX = worldWidth * world;
                            transforms.push(this.getRenderTransform(center, resolution, rotation, HIT_DETECT_RESOLUTION, width, height, offsetX).slice());
                            startX -= worldWidth;
                        }
                    }
                    this.hitDetectionImageData_ = createHitDetectionImageData(size, transforms, this.renderedFeatures_, layer.getStyleFunction(), extent, resolution, rotation, getSquaredTolerance(resolution, this.renderedPixelRatio_));
                }
                resolve(hitDetect(pixel, this.renderedFeatures_, this.hitDetectionImageData_));
            });
        }
        forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, matches) {
            if (!this.replayGroup_) {
                return undefined;
            }
            const resolution = frameState.viewState.resolution;
            const rotation = frameState.viewState.rotation;
            const layer = this.getLayer();
            const features = {};
            const featureCallback = function(feature, geometry, distanceSq) {
                const key = getUid(feature);
                const match = features[key];
                if (!match) {
                    if (distanceSq === 0) {
                        features[key] = true;
                        return callback(feature, layer, geometry);
                    }
                    matches.push(features[key] = {
                        feature: feature,
                        layer: layer,
                        geometry: geometry,
                        distanceSq: distanceSq,
                        callback: callback
                    });
                } else if (match !== true && distanceSq < match.distanceSq) {
                    if (distanceSq === 0) {
                        features[key] = true;
                        matches.splice(matches.lastIndexOf(match), 1);
                        return callback(feature, layer, geometry);
                    }
                    match.geometry = geometry;
                    match.distanceSq = distanceSq;
                }
                return undefined;
            };
            let result;
            const executorGroups = [ this.replayGroup_ ];
            const declutter = this.getLayer().getDeclutter();
            executorGroups.some(executorGroup => result = executorGroup.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance, featureCallback, declutter && frameState.declutter[declutter] ? frameState.declutter[declutter].all().map(item => item.value) : null));
            return result;
        }
        handleFontsChanged() {
            const layer = this.getLayer();
            if (layer.getVisible() && this.replayGroup_) {
                layer.changed();
            }
        }
        handleStyleImageChange_(event) {
            this.renderIfReadyAndVisible();
        }
        prepareFrame(frameState) {
            const vectorLayer = this.getLayer();
            const vectorSource = vectorLayer.getSource();
            if (!vectorSource) {
                return false;
            }
            const animating = frameState.viewHints[ViewHint.ANIMATING];
            const interacting = frameState.viewHints[ViewHint.INTERACTING];
            const updateWhileAnimating = vectorLayer.getUpdateWhileAnimating();
            const updateWhileInteracting = vectorLayer.getUpdateWhileInteracting();
            if (this.ready && !updateWhileAnimating && animating || !updateWhileInteracting && interacting) {
                this.animatingOrInteracting_ = true;
                return true;
            }
            this.animatingOrInteracting_ = false;
            const frameStateExtent = frameState.extent;
            const viewState = frameState.viewState;
            const projection = viewState.projection;
            const resolution = viewState.resolution;
            const pixelRatio = frameState.pixelRatio;
            const vectorLayerRevision = vectorLayer.getRevision();
            const vectorLayerRenderBuffer = vectorLayer.getRenderBuffer();
            let vectorLayerRenderOrder = vectorLayer.getRenderOrder();
            if (vectorLayerRenderOrder === undefined) {
                vectorLayerRenderOrder = defaultOrder;
            }
            const center = viewState.center.slice();
            const extent = buffer(frameStateExtent, vectorLayerRenderBuffer * resolution);
            const renderedExtent = extent.slice();
            const loadExtents = [ extent.slice() ];
            const projectionExtent = projection.getExtent();
            if (vectorSource.getWrapX() && projection.canWrapX() && !containsExtent(projectionExtent, frameState.extent)) {
                const worldWidth = getWidth(projectionExtent);
                const gutter = Math.max(getWidth(extent) / 2, worldWidth);
                extent[0] = projectionExtent[0] - gutter;
                extent[2] = projectionExtent[2] + gutter;
                wrapX$1(center, projection);
                const loadExtent = wrapX$2(loadExtents[0], projection);
                if (loadExtent[0] < projectionExtent[0] && loadExtent[2] < projectionExtent[2]) {
                    loadExtents.push([ loadExtent[0] + worldWidth, loadExtent[1], loadExtent[2] + worldWidth, loadExtent[3] ]);
                } else if (loadExtent[0] > projectionExtent[0] && loadExtent[2] > projectionExtent[2]) {
                    loadExtents.push([ loadExtent[0] - worldWidth, loadExtent[1], loadExtent[2] - worldWidth, loadExtent[3] ]);
                }
            }
            if (this.ready && this.renderedResolution_ == resolution && this.renderedRevision_ == vectorLayerRevision && this.renderedRenderOrder_ == vectorLayerRenderOrder && this.renderedFrameDeclutter_ === !!frameState.declutter && containsExtent(this.wrappedRenderedExtent_, extent)) {
                if (!equals$2(this.renderedExtent_, renderedExtent)) {
                    this.hitDetectionImageData_ = null;
                    this.renderedExtent_ = renderedExtent;
                }
                this.renderedCenter_ = center;
                this.replayGroupChanged = false;
                return true;
            }
            this.replayGroup_ = null;
            const replayGroup = new BuilderGroup(getTolerance(resolution, pixelRatio), extent, resolution, pixelRatio);
            let userTransform;
            {
                for (let i = 0, ii = loadExtents.length; i < ii; ++i) {
                    vectorSource.loadFeatures(loadExtents[i], resolution, projection);
                }
            }
            const squaredTolerance = getSquaredTolerance(resolution, pixelRatio);
            let ready = true;
            const render = (feature, index) => {
                let styles;
                const styleFunction = feature.getStyleFunction() || vectorLayer.getStyleFunction();
                if (styleFunction) {
                    styles = styleFunction(feature, resolution);
                }
                if (styles) {
                    const dirty = this.renderFeature(feature, squaredTolerance, styles, replayGroup, userTransform, this.getLayer().getDeclutter(), index);
                    ready = ready && !dirty;
                }
            };
            const userExtent = toUserExtent(extent);
            const features = vectorSource.getFeaturesInExtent(userExtent);
            if (vectorLayerRenderOrder) {
                features.sort(vectorLayerRenderOrder);
            }
            for (let i = 0, ii = features.length; i < ii; ++i) {
                render(features[i], i);
            }
            this.renderedFeatures_ = features;
            this.ready = ready;
            const replayGroupInstructions = replayGroup.finish();
            const executorGroup = new ExecutorGroup(extent, resolution, pixelRatio, vectorSource.getOverlaps(), replayGroupInstructions, vectorLayer.getRenderBuffer(), !!frameState.declutter);
            this.renderedResolution_ = resolution;
            this.renderedRevision_ = vectorLayerRevision;
            this.renderedRenderOrder_ = vectorLayerRenderOrder;
            this.renderedFrameDeclutter_ = !!frameState.declutter;
            this.renderedExtent_ = renderedExtent;
            this.wrappedRenderedExtent_ = extent;
            this.renderedCenter_ = center;
            this.renderedProjection_ = projection;
            this.renderedPixelRatio_ = pixelRatio;
            this.replayGroup_ = executorGroup;
            this.hitDetectionImageData_ = null;
            this.replayGroupChanged = true;
            return true;
        }
        renderFeature(feature, squaredTolerance, styles, builderGroup, transform, declutter, index) {
            if (!styles) {
                return false;
            }
            let loading = false;
            if (Array.isArray(styles)) {
                for (let i = 0, ii = styles.length; i < ii; ++i) {
                    loading = renderFeature(builderGroup, feature, styles[i], squaredTolerance, this.boundHandleStyleImageChange_, transform, declutter, index) || loading;
                }
            } else {
                loading = renderFeature(builderGroup, feature, styles, squaredTolerance, this.boundHandleStyleImageChange_, transform, declutter, index);
            }
            return loading;
        }
    }
    class VectorLayer extends BaseVectorLayer {
        constructor(options) {
            super(options);
        }
        createRenderer() {
            return new CanvasVectorLayerRenderer(this);
        }
    }
    class LRUCache {
        constructor(highWaterMark) {
            this.highWaterMark = highWaterMark !== undefined ? highWaterMark : 2048;
            this.count_ = 0;
            this.entries_ = {};
            this.oldest_ = null;
            this.newest_ = null;
        }
        canExpireCache() {
            return this.highWaterMark > 0 && this.getCount() > this.highWaterMark;
        }
        expireCache(keep) {
            while (this.canExpireCache()) {
                this.pop();
            }
        }
        clear() {
            this.count_ = 0;
            this.entries_ = {};
            this.oldest_ = null;
            this.newest_ = null;
        }
        containsKey(key) {
            return this.entries_.hasOwnProperty(key);
        }
        forEach(f) {
            let entry = this.oldest_;
            while (entry) {
                f(entry.value_, entry.key_, this);
                entry = entry.newer;
            }
        }
        get(key, options) {
            const entry = this.entries_[key];
            assert(entry !== undefined, "Tried to get a value for a key that does not exist in the cache");
            if (entry === this.newest_) {
                return entry.value_;
            }
            if (entry === this.oldest_) {
                this.oldest_ = this.oldest_.newer;
                this.oldest_.older = null;
            } else {
                entry.newer.older = entry.older;
                entry.older.newer = entry.newer;
            }
            entry.newer = null;
            entry.older = this.newest_;
            this.newest_.newer = entry;
            this.newest_ = entry;
            return entry.value_;
        }
        remove(key) {
            const entry = this.entries_[key];
            assert(entry !== undefined, "Tried to get a value for a key that does not exist in the cache");
            if (entry === this.newest_) {
                this.newest_ = entry.older;
                if (this.newest_) {
                    this.newest_.newer = null;
                }
            } else if (entry === this.oldest_) {
                this.oldest_ = entry.newer;
                if (this.oldest_) {
                    this.oldest_.older = null;
                }
            } else {
                entry.newer.older = entry.older;
                entry.older.newer = entry.newer;
            }
            delete this.entries_[key];
            --this.count_;
            return entry.value_;
        }
        getCount() {
            return this.count_;
        }
        getKeys() {
            const keys = new Array(this.count_);
            let i = 0;
            let entry;
            for (entry = this.newest_; entry; entry = entry.older) {
                keys[i++] = entry.key_;
            }
            return keys;
        }
        getValues() {
            const values = new Array(this.count_);
            let i = 0;
            let entry;
            for (entry = this.newest_; entry; entry = entry.older) {
                values[i++] = entry.value_;
            }
            return values;
        }
        peekLast() {
            return this.oldest_.value_;
        }
        peekLastKey() {
            return this.oldest_.key_;
        }
        peekFirstKey() {
            return this.newest_.key_;
        }
        peek(key) {
            return this.entries_[key]?.value_;
        }
        pop() {
            const entry = this.oldest_;
            delete this.entries_[entry.key_];
            if (entry.newer) {
                entry.newer.older = null;
            }
            this.oldest_ = entry.newer;
            if (!this.oldest_) {
                this.newest_ = null;
            }
            --this.count_;
            return entry.value_;
        }
        replace(key, value) {
            this.get(key);
            this.entries_[key].value_ = value;
        }
        set(key, value) {
            assert(!(key in this.entries_), "Tried to set a value for a key that is used already");
            const entry = {
                key_: key,
                newer: null,
                older: this.newest_,
                value_: value
            };
            if (!this.newest_) {
                this.oldest_ = entry;
            } else {
                this.newest_.newer = entry;
            }
            this.newest_ = entry;
            this.entries_[key] = entry;
            ++this.count_;
        }
        setSize(size) {
            this.highWaterMark = size;
        }
    }
    function createOrUpdate(z, x, y, tileCoord) {
        if (tileCoord !== undefined) {
            tileCoord[0] = z;
            tileCoord[1] = x;
            tileCoord[2] = y;
            return tileCoord;
        }
        return [ z, x, y ];
    }
    function getKeyZXY(z, x, y) {
        return z + "/" + x + "/" + y;
    }
    function getKey(tileCoord) {
        return getKeyZXY(tileCoord[0], tileCoord[1], tileCoord[2]);
    }
    function fromKey(key) {
        return key.split("/").map(Number);
    }
    function hash(tileCoord) {
        return (tileCoord[1] << tileCoord[0]) + tileCoord[2];
    }
    function withinExtentAndZ(tileCoord, tileGrid) {
        const z = tileCoord[0];
        const x = tileCoord[1];
        const y = tileCoord[2];
        if (tileGrid.getMinZoom() > z || z > tileGrid.getMaxZoom()) {
            return false;
        }
        const tileRange = tileGrid.getFullTileRange(z);
        if (!tileRange) {
            return true;
        }
        return tileRange.containsXY(x, y);
    }
    class TileCache extends LRUCache {
        clear() {
            while (this.getCount() > 0) {
                this.pop().release();
            }
            super.clear();
        }
        expireCache(usedTiles) {
            while (this.canExpireCache()) {
                const tile = this.peekLast();
                if (tile.getKey() in usedTiles) {
                    break;
                } else {
                    this.pop().release();
                }
            }
        }
        pruneExceptNewestZ() {
            if (this.getCount() === 0) {
                return;
            }
            const key = this.peekFirstKey();
            const tileCoord = fromKey(key);
            const z = tileCoord[0];
            this.forEach(tile => {
                if (tile.tileCoord[0] !== z) {
                    this.remove(getKey(tile.tileCoord));
                    tile.release();
                }
            });
        }
    }
    var TileEventType = {
        TILELOADSTART: "tileloadstart",
        TILELOADEND: "tileloadend",
        TILELOADERROR: "tileloaderror"
    };
    class Source extends BaseObject {
        constructor(options) {
            super();
            this.projection = get$1(options.projection);
            this.attributions_ = adaptAttributions(options.attributions);
            this.attributionsCollapsible_ = options.attributionsCollapsible !== undefined ? options.attributionsCollapsible : true;
            this.loading = false;
            this.state_ = options.state !== undefined ? options.state : "ready";
            this.wrapX_ = options.wrapX !== undefined ? options.wrapX : false;
            this.interpolate_ = !!options.interpolate;
            this.viewResolver = null;
            this.viewRejector = null;
            const self = this;
            this.viewPromise_ = new Promise(function(resolve, reject) {
                self.viewResolver = resolve;
                self.viewRejector = reject;
            });
        }
        getAttributions() {
            return this.attributions_;
        }
        getAttributionsCollapsible() {
            return this.attributionsCollapsible_;
        }
        getProjection() {
            return this.projection;
        }
        getResolutions(projection) {
            return null;
        }
        getView() {
            return this.viewPromise_;
        }
        getState() {
            return this.state_;
        }
        getWrapX() {
            return this.wrapX_;
        }
        getInterpolate() {
            return this.interpolate_;
        }
        refresh() {
            this.changed();
        }
        setAttributions(attributions) {
            this.attributions_ = adaptAttributions(attributions);
            this.changed();
        }
        setState(state) {
            this.state_ = state;
            this.changed();
        }
    }
    function adaptAttributions(attributionLike) {
        if (!attributionLike) {
            return null;
        }
        if (Array.isArray(attributionLike)) {
            return function(frameState) {
                return attributionLike;
            };
        }
        if (typeof attributionLike === "function") {
            return attributionLike;
        }
        return function(frameState) {
            return [ attributionLike ];
        };
    }
    const tmpTileCoord = [ 0, 0, 0 ];
    const DECIMALS = 5;
    class TileGrid {
        constructor(options) {
            this.minZoom = options.minZoom !== undefined ? options.minZoom : 0;
            this.resolutions_ = options.resolutions;
            assert(isSorted(this.resolutions_, (a, b) => b - a), "`resolutions` must be sorted in descending order");
            let zoomFactor;
            if (!options.origins) {
                for (let i = 0, ii = this.resolutions_.length - 1; i < ii; ++i) {
                    if (!zoomFactor) {
                        zoomFactor = this.resolutions_[i] / this.resolutions_[i + 1];
                    } else {
                        if (this.resolutions_[i] / this.resolutions_[i + 1] !== zoomFactor) {
                            zoomFactor = undefined;
                            break;
                        }
                    }
                }
            }
            this.zoomFactor_ = zoomFactor;
            this.maxZoom = this.resolutions_.length - 1;
            this.origin_ = options.origin !== undefined ? options.origin : null;
            this.origins_ = null;
            if (options.origins !== undefined) {
                this.origins_ = options.origins;
                assert(this.origins_.length == this.resolutions_.length, "Number of `origins` and `resolutions` must be equal");
            }
            const extent = options.extent;
            if (extent !== undefined && !this.origin_ && !this.origins_) {
                this.origin_ = getTopLeft(extent);
            }
            assert(!this.origin_ && this.origins_ || this.origin_ && !this.origins_, "Either `origin` or `origins` must be configured, never both");
            this.tileSizes_ = null;
            if (options.tileSizes !== undefined) {
                this.tileSizes_ = options.tileSizes;
                assert(this.tileSizes_.length == this.resolutions_.length, "Number of `tileSizes` and `resolutions` must be equal");
            }
            this.tileSize_ = options.tileSize !== undefined ? options.tileSize : !this.tileSizes_ ? DEFAULT_TILE_SIZE : null;
            assert(!this.tileSize_ && this.tileSizes_ || this.tileSize_ && !this.tileSizes_, "Either `tileSize` or `tileSizes` must be configured, never both");
            this.extent_ = extent !== undefined ? extent : null;
            this.fullTileRanges_ = null;
            this.tmpSize_ = [ 0, 0 ];
            this.tmpExtent_ = [ 0, 0, 0, 0 ];
            if (options.sizes !== undefined) {
                this.fullTileRanges_ = options.sizes.map((size, z) => {
                    const tileRange = new TileRange(Math.min(0, size[0]), Math.max(size[0] - 1, -1), Math.min(0, size[1]), Math.max(size[1] - 1, -1));
                    if (extent) {
                        const restrictedTileRange = this.getTileRangeForExtentAndZ(extent, z);
                        tileRange.minX = Math.max(restrictedTileRange.minX, tileRange.minX);
                        tileRange.maxX = Math.min(restrictedTileRange.maxX, tileRange.maxX);
                        tileRange.minY = Math.max(restrictedTileRange.minY, tileRange.minY);
                        tileRange.maxY = Math.min(restrictedTileRange.maxY, tileRange.maxY);
                    }
                    return tileRange;
                });
            } else if (extent) {
                this.calculateTileRanges_(extent);
            }
        }
        forEachTileCoord(extent, zoom, callback) {
            const tileRange = this.getTileRangeForExtentAndZ(extent, zoom);
            for (let i = tileRange.minX, ii = tileRange.maxX; i <= ii; ++i) {
                for (let j = tileRange.minY, jj = tileRange.maxY; j <= jj; ++j) {
                    callback([ zoom, i, j ]);
                }
            }
        }
        forEachTileCoordParentTileRange(tileCoord, callback, tempTileRange, tempExtent) {
            let tileRange, x, y;
            let tileCoordExtent = null;
            let z = tileCoord[0] - 1;
            if (this.zoomFactor_ === 2) {
                x = tileCoord[1];
                y = tileCoord[2];
            } else {
                tileCoordExtent = this.getTileCoordExtent(tileCoord, tempExtent);
            }
            while (z >= this.minZoom) {
                if (x !== undefined && y !== undefined) {
                    x = Math.floor(x / 2);
                    y = Math.floor(y / 2);
                    tileRange = createOrUpdate$1(x, x, y, y, tempTileRange);
                } else {
                    tileRange = this.getTileRangeForExtentAndZ(tileCoordExtent, z, tempTileRange);
                }
                if (callback(z, tileRange)) {
                    return true;
                }
                --z;
            }
            return false;
        }
        getExtent() {
            return this.extent_;
        }
        getMaxZoom() {
            return this.maxZoom;
        }
        getMinZoom() {
            return this.minZoom;
        }
        getOrigin(z) {
            if (this.origin_) {
                return this.origin_;
            }
            return this.origins_[z];
        }
        getResolution(z) {
            return this.resolutions_[z];
        }
        getResolutions() {
            return this.resolutions_;
        }
        getTileCoordChildTileRange(tileCoord, tempTileRange, tempExtent) {
            if (tileCoord[0] < this.maxZoom) {
                if (this.zoomFactor_ === 2) {
                    const minX = tileCoord[1] * 2;
                    const minY = tileCoord[2] * 2;
                    return createOrUpdate$1(minX, minX + 1, minY, minY + 1, tempTileRange);
                }
                const tileCoordExtent = this.getTileCoordExtent(tileCoord, tempExtent || this.tmpExtent_);
                return this.getTileRangeForExtentAndZ(tileCoordExtent, tileCoord[0] + 1, tempTileRange);
            }
            return null;
        }
        getTileRangeForTileCoordAndZ(tileCoord, z, tempTileRange) {
            if (z > this.maxZoom || z < this.minZoom) {
                return null;
            }
            const tileCoordZ = tileCoord[0];
            const tileCoordX = tileCoord[1];
            const tileCoordY = tileCoord[2];
            if (z === tileCoordZ) {
                return createOrUpdate$1(tileCoordX, tileCoordY, tileCoordX, tileCoordY, tempTileRange);
            }
            if (this.zoomFactor_) {
                const factor = Math.pow(this.zoomFactor_, z - tileCoordZ);
                const minX = Math.floor(tileCoordX * factor);
                const minY = Math.floor(tileCoordY * factor);
                if (z < tileCoordZ) {
                    return createOrUpdate$1(minX, minX, minY, minY, tempTileRange);
                }
                const maxX = Math.floor(factor * (tileCoordX + 1)) - 1;
                const maxY = Math.floor(factor * (tileCoordY + 1)) - 1;
                return createOrUpdate$1(minX, maxX, minY, maxY, tempTileRange);
            }
            const tileCoordExtent = this.getTileCoordExtent(tileCoord, this.tmpExtent_);
            return this.getTileRangeForExtentAndZ(tileCoordExtent, z, tempTileRange);
        }
        getTileRangeForExtentAndZ(extent, z, tempTileRange) {
            this.getTileCoordForXYAndZ_(extent[0], extent[3], z, false, tmpTileCoord);
            const minX = tmpTileCoord[1];
            const minY = tmpTileCoord[2];
            this.getTileCoordForXYAndZ_(extent[2], extent[1], z, true, tmpTileCoord);
            const maxX = tmpTileCoord[1];
            const maxY = tmpTileCoord[2];
            return createOrUpdate$1(minX, maxX, minY, maxY, tempTileRange);
        }
        getTileCoordCenter(tileCoord) {
            const origin = this.getOrigin(tileCoord[0]);
            const resolution = this.getResolution(tileCoord[0]);
            const tileSize = toSize(this.getTileSize(tileCoord[0]), this.tmpSize_);
            return [ origin[0] + (tileCoord[1] + .5) * tileSize[0] * resolution, origin[1] - (tileCoord[2] + .5) * tileSize[1] * resolution ];
        }
        getTileCoordExtent(tileCoord, tempExtent) {
            const origin = this.getOrigin(tileCoord[0]);
            const resolution = this.getResolution(tileCoord[0]);
            const tileSize = toSize(this.getTileSize(tileCoord[0]), this.tmpSize_);
            const minX = origin[0] + tileCoord[1] * tileSize[0] * resolution;
            const minY = origin[1] - (tileCoord[2] + 1) * tileSize[1] * resolution;
            const maxX = minX + tileSize[0] * resolution;
            const maxY = minY + tileSize[1] * resolution;
            return createOrUpdate$2(minX, minY, maxX, maxY, tempExtent);
        }
        getTileCoordForCoordAndResolution(coordinate, resolution, opt_tileCoord) {
            return this.getTileCoordForXYAndResolution_(coordinate[0], coordinate[1], resolution, false, opt_tileCoord);
        }
        getTileCoordForXYAndResolution_(x, y, resolution, reverseIntersectionPolicy, opt_tileCoord) {
            const z = this.getZForResolution(resolution);
            const scale = resolution / this.getResolution(z);
            const origin = this.getOrigin(z);
            const tileSize = toSize(this.getTileSize(z), this.tmpSize_);
            let tileCoordX = scale * (x - origin[0]) / resolution / tileSize[0];
            let tileCoordY = scale * (origin[1] - y) / resolution / tileSize[1];
            if (reverseIntersectionPolicy) {
                tileCoordX = ceil(tileCoordX, DECIMALS) - 1;
                tileCoordY = ceil(tileCoordY, DECIMALS) - 1;
            } else {
                tileCoordX = floor(tileCoordX, DECIMALS);
                tileCoordY = floor(tileCoordY, DECIMALS);
            }
            return createOrUpdate(z, tileCoordX, tileCoordY, opt_tileCoord);
        }
        getTileCoordForXYAndZ_(x, y, z, reverseIntersectionPolicy, opt_tileCoord) {
            const origin = this.getOrigin(z);
            const resolution = this.getResolution(z);
            const tileSize = toSize(this.getTileSize(z), this.tmpSize_);
            let tileCoordX = (x - origin[0]) / resolution / tileSize[0];
            let tileCoordY = (origin[1] - y) / resolution / tileSize[1];
            if (reverseIntersectionPolicy) {
                tileCoordX = ceil(tileCoordX, DECIMALS) - 1;
                tileCoordY = ceil(tileCoordY, DECIMALS) - 1;
            } else {
                tileCoordX = floor(tileCoordX, DECIMALS);
                tileCoordY = floor(tileCoordY, DECIMALS);
            }
            return createOrUpdate(z, tileCoordX, tileCoordY, opt_tileCoord);
        }
        getTileCoordForCoordAndZ(coordinate, z, opt_tileCoord) {
            return this.getTileCoordForXYAndZ_(coordinate[0], coordinate[1], z, false, opt_tileCoord);
        }
        getTileCoordResolution(tileCoord) {
            return this.resolutions_[tileCoord[0]];
        }
        getTileSize(z) {
            if (this.tileSize_) {
                return this.tileSize_;
            }
            return this.tileSizes_[z];
        }
        getFullTileRange(z) {
            if (!this.fullTileRanges_) {
                return this.extent_ ? this.getTileRangeForExtentAndZ(this.extent_, z) : null;
            }
            return this.fullTileRanges_[z];
        }
        getZForResolution(resolution, opt_direction) {
            const z = linearFindNearest(this.resolutions_, resolution, opt_direction || 0);
            return clamp(z, this.minZoom, this.maxZoom);
        }
        tileCoordIntersectsViewport(tileCoord, viewport) {
            return intersectsLinearRing(viewport, 0, viewport.length, 2, this.getTileCoordExtent(tileCoord));
        }
        calculateTileRanges_(extent) {
            const length = this.resolutions_.length;
            const fullTileRanges = new Array(length);
            for (let z = this.minZoom; z < length; ++z) {
                fullTileRanges[z] = this.getTileRangeForExtentAndZ(extent, z);
            }
            this.fullTileRanges_ = fullTileRanges;
        }
    }
    function getForProjection(projection) {
        let tileGrid = projection.getDefaultTileGrid();
        if (!tileGrid) {
            tileGrid = createForProjection(projection);
            projection.setDefaultTileGrid(tileGrid);
        }
        return tileGrid;
    }
    function wrapX(tileGrid, tileCoord, projection) {
        const z = tileCoord[0];
        const center = tileGrid.getTileCoordCenter(tileCoord);
        const projectionExtent = extentFromProjection(projection);
        if (!containsCoordinate(projectionExtent, center)) {
            const worldWidth = getWidth(projectionExtent);
            const worldsAway = Math.ceil((projectionExtent[0] - center[0]) / worldWidth);
            center[0] += worldWidth * worldsAway;
            return tileGrid.getTileCoordForCoordAndZ(center, z);
        }
        return tileCoord;
    }
    function createForExtent(extent, maxZoom, tileSize, corner) {
        corner = corner !== undefined ? corner : "top-left";
        const resolutions = resolutionsFromExtent(extent, maxZoom, tileSize);
        return new TileGrid({
            extent: extent,
            origin: getCorner(extent, corner),
            resolutions: resolutions,
            tileSize: tileSize
        });
    }
    function createXYZ(options) {
        const xyzOptions = options || {};
        const extent = xyzOptions.extent || get$1("EPSG:3857").getExtent();
        const gridOptions = {
            extent: extent,
            minZoom: xyzOptions.minZoom,
            tileSize: xyzOptions.tileSize,
            resolutions: resolutionsFromExtent(extent, xyzOptions.maxZoom, xyzOptions.tileSize, xyzOptions.maxResolution)
        };
        return new TileGrid(gridOptions);
    }
    function resolutionsFromExtent(extent, maxZoom, tileSize, maxResolution) {
        maxZoom = maxZoom !== undefined ? maxZoom : DEFAULT_MAX_ZOOM;
        tileSize = toSize(tileSize !== undefined ? tileSize : DEFAULT_TILE_SIZE);
        const height = getHeight(extent);
        const width = getWidth(extent);
        maxResolution = maxResolution > 0 ? maxResolution : Math.max(width / tileSize[0], height / tileSize[1]);
        const length = maxZoom + 1;
        const resolutions = new Array(length);
        for (let z = 0; z < length; ++z) {
            resolutions[z] = maxResolution / Math.pow(2, z);
        }
        return resolutions;
    }
    function createForProjection(projection, maxZoom, tileSize, corner) {
        const extent = extentFromProjection(projection);
        return createForExtent(extent, maxZoom, tileSize, corner);
    }
    function extentFromProjection(projection) {
        projection = get$1(projection);
        let extent = projection.getExtent();
        if (!extent) {
            const half = 180 * METERS_PER_UNIT$1.degrees / projection.getMetersPerUnit();
            extent = createOrUpdate$2(-half, -half, half, half);
        }
        return extent;
    }
    class TileSource extends Source {
        constructor(options) {
            super({
                attributions: options.attributions,
                attributionsCollapsible: options.attributionsCollapsible,
                projection: options.projection,
                state: options.state,
                wrapX: options.wrapX,
                interpolate: options.interpolate
            });
            this.on;
            this.once;
            this.un;
            this.opaque_ = options.opaque !== undefined ? options.opaque : false;
            this.tilePixelRatio_ = options.tilePixelRatio !== undefined ? options.tilePixelRatio : 1;
            this.tileGrid = options.tileGrid !== undefined ? options.tileGrid : null;
            const tileSize = [ 256, 256 ];
            if (this.tileGrid) {
                toSize(this.tileGrid.getTileSize(this.tileGrid.getMinZoom()), tileSize);
            }
            this.tileCache = new TileCache(options.cacheSize || 0);
            this.tmpSize = [ 0, 0 ];
            this.key_ = options.key || "";
            this.tileOptions = {
                transition: options.transition,
                interpolate: options.interpolate
            };
            this.zDirection = options.zDirection ? options.zDirection : 0;
        }
        canExpireCache() {
            return this.tileCache.canExpireCache();
        }
        expireCache(projection, usedTiles) {
            const tileCache = this.getTileCacheForProjection(projection);
            if (tileCache) {
                tileCache.expireCache(usedTiles);
            }
        }
        forEachLoadedTile(projection, z, tileRange, callback) {
            const tileCache = this.getTileCacheForProjection(projection);
            if (!tileCache) {
                return false;
            }
            let covered = true;
            let tile, tileCoordKey, loaded;
            for (let x = tileRange.minX; x <= tileRange.maxX; ++x) {
                for (let y = tileRange.minY; y <= tileRange.maxY; ++y) {
                    tileCoordKey = getKeyZXY(z, x, y);
                    loaded = false;
                    if (tileCache.containsKey(tileCoordKey)) {
                        tile = tileCache.get(tileCoordKey);
                        loaded = tile.getState() === TileState.LOADED;
                        if (loaded) {
                            loaded = callback(tile) !== false;
                        }
                    }
                    if (!loaded) {
                        covered = false;
                    }
                }
            }
            return covered;
        }
        getGutterForProjection(projection) {
            return 0;
        }
        getKey() {
            return this.key_;
        }
        setKey(key) {
            if (this.key_ !== key) {
                this.key_ = key;
                this.changed();
            }
        }
        getOpaque(projection) {
            return this.opaque_;
        }
        getResolutions(projection) {
            const tileGrid = projection ? this.getTileGridForProjection(projection) : this.tileGrid;
            if (!tileGrid) {
                return null;
            }
            return tileGrid.getResolutions();
        }
        getTile(z, x, y, pixelRatio, projection) {
            return abstract();
        }
        getTileGrid() {
            return this.tileGrid;
        }
        getTileGridForProjection(projection) {
            if (!this.tileGrid) {
                return getForProjection(projection);
            }
            return this.tileGrid;
        }
        getTileCacheForProjection(projection) {
            const sourceProjection = this.getProjection();
            assert(sourceProjection === null || equivalent(sourceProjection, projection), "A VectorTile source can only be rendered if it has a projection compatible with the view projection.");
            return this.tileCache;
        }
        getTilePixelRatio(pixelRatio) {
            return this.tilePixelRatio_;
        }
        getTilePixelSize(z, pixelRatio, projection) {
            const tileGrid = this.getTileGridForProjection(projection);
            const tilePixelRatio = this.getTilePixelRatio(pixelRatio);
            const tileSize = toSize(tileGrid.getTileSize(z), this.tmpSize);
            if (tilePixelRatio == 1) {
                return tileSize;
            }
            return scale(tileSize, tilePixelRatio, this.tmpSize);
        }
        getTileCoordForTileUrlFunction(tileCoord, projection) {
            projection = projection !== undefined ? projection : this.getProjection();
            const tileGrid = this.getTileGridForProjection(projection);
            if (this.getWrapX() && projection.isGlobal()) {
                tileCoord = wrapX(tileGrid, tileCoord, projection);
            }
            return withinExtentAndZ(tileCoord, tileGrid) ? tileCoord : null;
        }
        clear() {
            this.tileCache.clear();
        }
        refresh() {
            this.clear();
            super.refresh();
        }
        updateCacheSize(tileCount, projection) {
            const tileCache = this.getTileCacheForProjection(projection);
            if (tileCount > tileCache.highWaterMark) {
                tileCache.highWaterMark = tileCount;
            }
        }
        useTile(z, x, y, projection) {}
    }
    class TileSourceEvent extends BaseEvent {
        constructor(type, tile) {
            super(type);
            this.tile = tile;
        }
    }
    function createFromTemplate(template, tileGrid) {
        const zRegEx = /\{z\}/g;
        const xRegEx = /\{x\}/g;
        const yRegEx = /\{y\}/g;
        const dashYRegEx = /\{-y\}/g;
        return function(tileCoord, pixelRatio, projection) {
            if (!tileCoord) {
                return undefined;
            }
            return template.replace(zRegEx, tileCoord[0].toString()).replace(xRegEx, tileCoord[1].toString()).replace(yRegEx, tileCoord[2].toString()).replace(dashYRegEx, function() {
                const z = tileCoord[0];
                const range = tileGrid.getFullTileRange(z);
                if (!range) {
                    throw new Error("The {-y} placeholder requires a tile grid with extent");
                }
                const y = range.getHeight() - tileCoord[2] - 1;
                return y.toString();
            });
        };
    }
    function createFromTemplates(templates, tileGrid) {
        const len = templates.length;
        const tileUrlFunctions = new Array(len);
        for (let i = 0; i < len; ++i) {
            tileUrlFunctions[i] = createFromTemplate(templates[i], tileGrid);
        }
        return createFromTileUrlFunctions(tileUrlFunctions);
    }
    function createFromTileUrlFunctions(tileUrlFunctions) {
        if (tileUrlFunctions.length === 1) {
            return tileUrlFunctions[0];
        }
        return function(tileCoord, pixelRatio, projection) {
            if (!tileCoord) {
                return undefined;
            }
            const h = hash(tileCoord);
            const index = modulo(h, tileUrlFunctions.length);
            return tileUrlFunctions[index](tileCoord, pixelRatio, projection);
        };
    }
    function expandUrl(url) {
        const urls = [];
        let match = /\{([a-z])-([a-z])\}/.exec(url);
        if (match) {
            const startCharCode = match[1].charCodeAt(0);
            const stopCharCode = match[2].charCodeAt(0);
            let charCode;
            for (charCode = startCharCode; charCode <= stopCharCode; ++charCode) {
                urls.push(url.replace(match[0], String.fromCharCode(charCode)));
            }
            return urls;
        }
        match = /\{(\d+)-(\d+)\}/.exec(url);
        if (match) {
            const stop = parseInt(match[2], 10);
            for (let i = parseInt(match[1], 10); i <= stop; i++) {
                urls.push(url.replace(match[0], i.toString()));
            }
            return urls;
        }
        urls.push(url);
        return urls;
    }
    class UrlTile extends TileSource {
        constructor(options) {
            super({
                attributions: options.attributions,
                cacheSize: options.cacheSize,
                opaque: options.opaque,
                projection: options.projection,
                state: options.state,
                tileGrid: options.tileGrid,
                tilePixelRatio: options.tilePixelRatio,
                wrapX: options.wrapX,
                transition: options.transition,
                interpolate: options.interpolate,
                key: options.key,
                attributionsCollapsible: options.attributionsCollapsible,
                zDirection: options.zDirection
            });
            this.generateTileUrlFunction_ = this.tileUrlFunction === UrlTile.prototype.tileUrlFunction;
            this.tileLoadFunction = options.tileLoadFunction;
            if (options.tileUrlFunction) {
                this.tileUrlFunction = options.tileUrlFunction;
            }
            this.urls = null;
            if (options.urls) {
                this.setUrls(options.urls);
            } else if (options.url) {
                this.setUrl(options.url);
            }
            this.tileLoadingKeys_ = {};
        }
        getTileLoadFunction() {
            return this.tileLoadFunction;
        }
        getTileUrlFunction() {
            return Object.getPrototypeOf(this).tileUrlFunction === this.tileUrlFunction ? this.tileUrlFunction.bind(this) : this.tileUrlFunction;
        }
        getUrls() {
            return this.urls;
        }
        handleTileChange(event) {
            const tile = event.target;
            const uid = getUid(tile);
            const tileState = tile.getState();
            let type;
            if (tileState == TileState.LOADING) {
                this.tileLoadingKeys_[uid] = true;
                type = TileEventType.TILELOADSTART;
            } else if (uid in this.tileLoadingKeys_) {
                delete this.tileLoadingKeys_[uid];
                type = tileState == TileState.ERROR ? TileEventType.TILELOADERROR : tileState == TileState.LOADED ? TileEventType.TILELOADEND : undefined;
            }
            if (type != undefined) {
                this.dispatchEvent(new TileSourceEvent(type, tile));
            }
        }
        setTileLoadFunction(tileLoadFunction) {
            this.tileCache.clear();
            this.tileLoadFunction = tileLoadFunction;
            this.changed();
        }
        setTileUrlFunction(tileUrlFunction, key) {
            this.tileUrlFunction = tileUrlFunction;
            this.tileCache.pruneExceptNewestZ();
            if (typeof key !== "undefined") {
                this.setKey(key);
            } else {
                this.changed();
            }
        }
        setUrl(url) {
            const urls = expandUrl(url);
            this.urls = urls;
            this.setUrls(urls);
        }
        setUrls(urls) {
            this.urls = urls;
            const key = urls.join("\n");
            if (this.generateTileUrlFunction_) {
                this.setTileUrlFunction(createFromTemplates(urls, this.tileGrid), key);
            } else {
                this.setKey(key);
            }
        }
        tileUrlFunction(tileCoord, pixelRatio, projection) {
            return undefined;
        }
        useTile(z, x, y) {
            const tileCoordKey = getKeyZXY(z, x, y);
            if (this.tileCache.containsKey(tileCoordKey)) {
                this.tileCache.get(tileCoordKey);
            }
        }
    }
    class TileImage extends UrlTile {
        constructor(options) {
            super({
                attributions: options.attributions,
                cacheSize: options.cacheSize,
                opaque: options.opaque,
                projection: options.projection,
                state: options.state,
                tileGrid: options.tileGrid,
                tileLoadFunction: options.tileLoadFunction ? options.tileLoadFunction : defaultTileLoadFunction,
                tilePixelRatio: options.tilePixelRatio,
                tileUrlFunction: options.tileUrlFunction,
                url: options.url,
                urls: options.urls,
                wrapX: options.wrapX,
                transition: options.transition,
                interpolate: options.interpolate !== undefined ? options.interpolate : true,
                key: options.key,
                attributionsCollapsible: options.attributionsCollapsible,
                zDirection: options.zDirection
            });
            this.crossOrigin = options.crossOrigin !== undefined ? options.crossOrigin : null;
            this.tileClass = options.tileClass !== undefined ? options.tileClass : ImageTile;
            this.tileCacheForProjection = {};
            this.tileGridForProjection = {};
            this.reprojectionErrorThreshold_ = options.reprojectionErrorThreshold;
            this.renderReprojectionEdges_ = false;
        }
        canExpireCache() {
            if (this.tileCache.canExpireCache()) {
                return true;
            }
            for (const key in this.tileCacheForProjection) {
                if (this.tileCacheForProjection[key].canExpireCache()) {
                    return true;
                }
            }
            return false;
        }
        expireCache(projection, usedTiles) {
            const usedTileCache = this.getTileCacheForProjection(projection);
            this.tileCache.expireCache(this.tileCache == usedTileCache ? usedTiles : {});
            for (const id in this.tileCacheForProjection) {
                const tileCache = this.tileCacheForProjection[id];
                tileCache.expireCache(tileCache == usedTileCache ? usedTiles : {});
            }
        }
        getGutterForProjection(projection) {
            if (this.getProjection() && projection && !equivalent(this.getProjection(), projection)) {
                return 0;
            }
            return this.getGutter();
        }
        getGutter() {
            return 0;
        }
        getKey() {
            let key = super.getKey();
            if (!this.getInterpolate()) {
                key += ":disable-interpolation";
            }
            return key;
        }
        getOpaque(projection) {
            if (this.getProjection() && projection && !equivalent(this.getProjection(), projection)) {
                return false;
            }
            return super.getOpaque(projection);
        }
        getTileGridForProjection(projection) {
            const thisProj = this.getProjection();
            if (this.tileGrid && (!thisProj || equivalent(thisProj, projection))) {
                return this.tileGrid;
            }
            const projKey = getUid(projection);
            if (!(projKey in this.tileGridForProjection)) {
                this.tileGridForProjection[projKey] = getForProjection(projection);
            }
            return this.tileGridForProjection[projKey];
        }
        getTileCacheForProjection(projection) {
            const thisProj = this.getProjection();
            if (!thisProj || equivalent(thisProj, projection)) {
                return this.tileCache;
            }
            const projKey = getUid(projection);
            if (!(projKey in this.tileCacheForProjection)) {
                this.tileCacheForProjection[projKey] = new TileCache(this.tileCache.highWaterMark);
            }
            return this.tileCacheForProjection[projKey];
        }
        createTile_(z, x, y, pixelRatio, projection, key) {
            const tileCoord = [ z, x, y ];
            const urlTileCoord = this.getTileCoordForTileUrlFunction(tileCoord, projection);
            const tileUrl = urlTileCoord ? this.tileUrlFunction(urlTileCoord, pixelRatio, projection) : undefined;
            const tile = new this.tileClass(tileCoord, tileUrl !== undefined ? TileState.IDLE : TileState.EMPTY, tileUrl !== undefined ? tileUrl : "", this.crossOrigin, this.tileLoadFunction, this.tileOptions);
            tile.key = key;
            tile.addEventListener(EventType.CHANGE, this.handleTileChange.bind(this));
            return tile;
        }
        getTile(z, x, y, pixelRatio, projection) {
            const sourceProjection = this.getProjection();
            if (!sourceProjection || !projection || equivalent(sourceProjection, projection)) {
                return this.getTileInternal(z, x, y, pixelRatio, sourceProjection || projection);
            }
            const cache = this.getTileCacheForProjection(projection);
            const tileCoord = [ z, x, y ];
            let tile;
            const tileCoordKey = getKey(tileCoord);
            if (cache.containsKey(tileCoordKey)) {
                tile = cache.get(tileCoordKey);
            }
            const key = this.getKey();
            if (tile && tile.key == key) {
                return tile;
            }
            const sourceTileGrid = this.getTileGridForProjection(sourceProjection);
            const targetTileGrid = this.getTileGridForProjection(projection);
            const wrappedTileCoord = this.getTileCoordForTileUrlFunction(tileCoord, projection);
            const newTile = new ReprojTile(sourceProjection, sourceTileGrid, projection, targetTileGrid, tileCoord, wrappedTileCoord, this.getTilePixelRatio(pixelRatio), this.getGutter(), (z, x, y, pixelRatio) => this.getTileInternal(z, x, y, pixelRatio, sourceProjection), this.reprojectionErrorThreshold_, this.renderReprojectionEdges_, this.tileOptions);
            newTile.key = key;
            if (tile) {
                newTile.interimTile = tile;
                newTile.refreshInterimChain();
                cache.replace(tileCoordKey, newTile);
            } else {
                cache.set(tileCoordKey, newTile);
            }
            return newTile;
        }
        getTileInternal(z, x, y, pixelRatio, projection) {
            let tile = null;
            const tileCoordKey = getKeyZXY(z, x, y);
            const key = this.getKey();
            if (!this.tileCache.containsKey(tileCoordKey)) {
                tile = this.createTile_(z, x, y, pixelRatio, projection, key);
                this.tileCache.set(tileCoordKey, tile);
            } else {
                tile = this.tileCache.get(tileCoordKey);
                if (tile.key != key) {
                    const interimTile = tile;
                    tile = this.createTile_(z, x, y, pixelRatio, projection, key);
                    if (interimTile.getState() == TileState.IDLE) {
                        tile.interimTile = interimTile.interimTile;
                    } else {
                        tile.interimTile = interimTile;
                    }
                    tile.refreshInterimChain();
                    this.tileCache.replace(tileCoordKey, tile);
                }
            }
            return tile;
        }
        setRenderReprojectionEdges(render) {
            if (this.renderReprojectionEdges_ == render) {
                return;
            }
            this.renderReprojectionEdges_ = render;
            for (const id in this.tileCacheForProjection) {
                this.tileCacheForProjection[id].clear();
            }
            this.changed();
        }
        setTileGridForProjection(projection, tilegrid) {
            const proj = get$1(projection);
            if (proj) {
                const projKey = getUid(proj);
                if (!(projKey in this.tileGridForProjection)) {
                    this.tileGridForProjection[projKey] = tilegrid;
                }
            }
        }
        clear() {
            super.clear();
            for (const id in this.tileCacheForProjection) {
                this.tileCacheForProjection[id].clear();
            }
        }
    }
    function defaultTileLoadFunction(imageTile, src) {
        imageTile.getImage().src = src;
    }
    class XYZ extends TileImage {
        constructor(options) {
            options = options || {};
            const projection = options.projection !== undefined ? options.projection : "EPSG:3857";
            const tileGrid = options.tileGrid !== undefined ? options.tileGrid : createXYZ({
                extent: extentFromProjection(projection),
                maxResolution: options.maxResolution,
                maxZoom: options.maxZoom,
                minZoom: options.minZoom,
                tileSize: options.tileSize
            });
            super({
                attributions: options.attributions,
                cacheSize: options.cacheSize,
                crossOrigin: options.crossOrigin,
                interpolate: options.interpolate,
                opaque: options.opaque,
                projection: projection,
                reprojectionErrorThreshold: options.reprojectionErrorThreshold,
                tileGrid: tileGrid,
                tileLoadFunction: options.tileLoadFunction,
                tilePixelRatio: options.tilePixelRatio,
                tileUrlFunction: options.tileUrlFunction,
                url: options.url,
                urls: options.urls,
                wrapX: options.wrapX !== undefined ? options.wrapX : true,
                transition: options.transition,
                attributionsCollapsible: options.attributionsCollapsible,
                zDirection: options.zDirection
            });
            this.gutter_ = options.gutter !== undefined ? options.gutter : 0;
        }
        getGutter() {
            return this.gutter_;
        }
    }
    const ATTRIBUTION = "&#169; " + '<a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a> ' + "contributors.";
    class OSM extends XYZ {
        constructor(options) {
            options = options || {};
            let attributions;
            if (options.attributions !== undefined) {
                attributions = options.attributions;
            } else {
                attributions = [ ATTRIBUTION ];
            }
            const crossOrigin = options.crossOrigin !== undefined ? options.crossOrigin : "anonymous";
            const url = options.url !== undefined ? options.url : "https://tile.openstreetmap.org/{z}/{x}/{y}.png";
            super({
                attributions: attributions,
                attributionsCollapsible: false,
                cacheSize: options.cacheSize,
                crossOrigin: crossOrigin,
                interpolate: options.interpolate,
                maxZoom: options.maxZoom !== undefined ? options.maxZoom : 19,
                opaque: options.opaque !== undefined ? options.opaque : true,
                reprojectionErrorThreshold: options.reprojectionErrorThreshold,
                tileLoadFunction: options.tileLoadFunction,
                transition: options.transition,
                url: url,
                wrapX: options.wrapX,
                zDirection: options.zDirection
            });
        }
    }
    class RBush {
        constructor(maxEntries) {
            this.rbush_ = new RBush$1(maxEntries);
            this.items_ = {};
        }
        insert(extent, value) {
            const item = {
                minX: extent[0],
                minY: extent[1],
                maxX: extent[2],
                maxY: extent[3],
                value: value
            };
            this.rbush_.insert(item);
            this.items_[getUid(value)] = item;
        }
        load(extents, values) {
            const items = new Array(values.length);
            for (let i = 0, l = values.length; i < l; i++) {
                const extent = extents[i];
                const value = values[i];
                const item = {
                    minX: extent[0],
                    minY: extent[1],
                    maxX: extent[2],
                    maxY: extent[3],
                    value: value
                };
                items[i] = item;
                this.items_[getUid(value)] = item;
            }
            this.rbush_.load(items);
        }
        remove(value) {
            const uid = getUid(value);
            const item = this.items_[uid];
            delete this.items_[uid];
            return this.rbush_.remove(item) !== null;
        }
        update(extent, value) {
            const item = this.items_[getUid(value)];
            const bbox = [ item.minX, item.minY, item.maxX, item.maxY ];
            if (!equals$1(bbox, extent)) {
                this.remove(value);
                this.insert(extent, value);
            }
        }
        getAll() {
            const items = this.rbush_.all();
            return items.map(function(item) {
                return item.value;
            });
        }
        getInExtent(extent) {
            const bbox = {
                minX: extent[0],
                minY: extent[1],
                maxX: extent[2],
                maxY: extent[3]
            };
            const items = this.rbush_.search(bbox);
            return items.map(function(item) {
                return item.value;
            });
        }
        forEach(callback) {
            return this.forEach_(this.getAll(), callback);
        }
        forEachInExtent(extent, callback) {
            return this.forEach_(this.getInExtent(extent), callback);
        }
        forEach_(values, callback) {
            let result;
            for (let i = 0, l = values.length; i < l; i++) {
                result = callback(values[i]);
                if (result) {
                    return result;
                }
            }
            return result;
        }
        isEmpty() {
            return isEmpty$1(this.items_);
        }
        clear() {
            this.rbush_.clear();
            this.items_ = {};
        }
        getExtent(extent) {
            const data = this.rbush_.toJSON();
            return createOrUpdate$2(data.minX, data.minY, data.maxX, data.maxY, extent);
        }
        concat(rbush) {
            this.rbush_.load(rbush.rbush_.all());
            for (const i in rbush.items_) {
                this.items_[i] = rbush.items_[i];
            }
        }
    }
    class Feature extends BaseObject {
        constructor(geometryOrProperties) {
            super();
            this.on;
            this.once;
            this.un;
            this.id_ = undefined;
            this.geometryName_ = "geometry";
            this.style_ = null;
            this.styleFunction_ = undefined;
            this.geometryChangeKey_ = null;
            this.addChangeListener(this.geometryName_, this.handleGeometryChanged_);
            if (geometryOrProperties) {
                if (typeof geometryOrProperties.getSimplifiedGeometry === "function") {
                    const geometry = geometryOrProperties;
                    this.setGeometry(geometry);
                } else {
                    const properties = geometryOrProperties;
                    this.setProperties(properties);
                }
            }
        }
        clone() {
            const clone = new Feature(this.hasProperties() ? this.getProperties() : null);
            clone.setGeometryName(this.getGeometryName());
            const geometry = this.getGeometry();
            if (geometry) {
                clone.setGeometry(geometry.clone());
            }
            const style = this.getStyle();
            if (style) {
                clone.setStyle(style);
            }
            return clone;
        }
        getGeometry() {
            return this.get(this.geometryName_);
        }
        getId() {
            return this.id_;
        }
        getGeometryName() {
            return this.geometryName_;
        }
        getStyle() {
            return this.style_;
        }
        getStyleFunction() {
            return this.styleFunction_;
        }
        handleGeometryChange_() {
            this.changed();
        }
        handleGeometryChanged_() {
            if (this.geometryChangeKey_) {
                unlistenByKey(this.geometryChangeKey_);
                this.geometryChangeKey_ = null;
            }
            const geometry = this.getGeometry();
            if (geometry) {
                this.geometryChangeKey_ = listen(geometry, EventType.CHANGE, this.handleGeometryChange_, this);
            }
            this.changed();
        }
        setGeometry(geometry) {
            this.set(this.geometryName_, geometry);
        }
        setStyle(style) {
            this.style_ = style;
            this.styleFunction_ = !style ? undefined : createStyleFunction(style);
            this.changed();
        }
        setId(id) {
            this.id_ = id;
            this.changed();
        }
        setGeometryName(name) {
            this.removeChangeListener(this.geometryName_, this.handleGeometryChanged_);
            this.geometryName_ = name;
            this.addChangeListener(this.geometryName_, this.handleGeometryChanged_);
            this.handleGeometryChanged_();
        }
    }
    function createStyleFunction(obj) {
        if (typeof obj === "function") {
            return obj;
        }
        let styles;
        if (Array.isArray(obj)) {
            styles = obj;
        } else {
            assert(typeof obj.getZIndex === "function", "Expected an `ol/style/Style` or an array of `ol/style/Style.js`");
            const style = obj;
            styles = [ style ];
        }
        return function() {
            return styles;
        };
    }
    function interpolatePoint(flatCoordinates, offset, end, stride, fraction, dest, dimension) {
        let o, t;
        const n = (end - offset) / stride;
        if (n === 1) {
            o = offset;
        } else if (n === 2) {
            o = offset;
            t = fraction;
        } else if (n !== 0) {
            let x1 = flatCoordinates[offset];
            let y1 = flatCoordinates[offset + 1];
            let length = 0;
            const cumulativeLengths = [ 0 ];
            for (let i = offset + stride; i < end; i += stride) {
                const x2 = flatCoordinates[i];
                const y2 = flatCoordinates[i + 1];
                length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
                cumulativeLengths.push(length);
                x1 = x2;
                y1 = y2;
            }
            const target = fraction * length;
            const index = binarySearch(cumulativeLengths, target);
            if (index < 0) {
                t = (target - cumulativeLengths[-index - 2]) / (cumulativeLengths[-index - 1] - cumulativeLengths[-index - 2]);
                o = offset + (-index - 2) * stride;
            } else {
                o = offset + index * stride;
            }
        }
        dimension = dimension > 1 ? dimension : 2;
        dest = dest ? dest : new Array(dimension);
        for (let i = 0; i < dimension; ++i) {
            dest[i] = o === undefined ? NaN : t === undefined ? flatCoordinates[o + i] : lerp(flatCoordinates[o + i], flatCoordinates[o + stride + i], t);
        }
        return dest;
    }
    function linearRingss(flatCoordinates, offset, endss, stride) {
        const flatCenters = [];
        let extent = createEmpty();
        for (let i = 0, ii = endss.length; i < ii; ++i) {
            const ends = endss[i];
            extent = createOrUpdateFromFlatCoordinates(flatCoordinates, offset, ends[0], stride);
            flatCenters.push((extent[0] + extent[2]) / 2, (extent[1] + extent[3]) / 2);
            offset = ends[ends.length - 1];
        }
        return flatCenters;
    }
    const tmpTransform = create();
    class RenderFeature {
        constructor(type, flatCoordinates, ends, stride, properties, id) {
            this.styleFunction;
            this.extent_;
            this.id_ = id;
            this.type_ = type;
            this.flatCoordinates_ = flatCoordinates;
            this.flatInteriorPoints_ = null;
            this.flatMidpoints_ = null;
            this.ends_ = ends || null;
            this.properties_ = properties;
            this.squaredTolerance_;
            this.stride_ = stride;
            this.simplifiedGeometry_;
        }
        get(key) {
            return this.properties_[key];
        }
        getExtent() {
            if (!this.extent_) {
                this.extent_ = this.type_ === "Point" ? createOrUpdateFromCoordinate(this.flatCoordinates_) : createOrUpdateFromFlatCoordinates(this.flatCoordinates_, 0, this.flatCoordinates_.length, 2);
            }
            return this.extent_;
        }
        getFlatInteriorPoint() {
            if (!this.flatInteriorPoints_) {
                const flatCenter = getCenter(this.getExtent());
                this.flatInteriorPoints_ = getInteriorPointOfArray(this.flatCoordinates_, 0, this.ends_, 2, flatCenter, 0);
            }
            return this.flatInteriorPoints_;
        }
        getFlatInteriorPoints() {
            if (!this.flatInteriorPoints_) {
                const ends = inflateEnds(this.flatCoordinates_, this.ends_);
                const flatCenters = linearRingss(this.flatCoordinates_, 0, ends, 2);
                this.flatInteriorPoints_ = getInteriorPointsOfMultiArray(this.flatCoordinates_, 0, ends, 2, flatCenters);
            }
            return this.flatInteriorPoints_;
        }
        getFlatMidpoint() {
            if (!this.flatMidpoints_) {
                this.flatMidpoints_ = interpolatePoint(this.flatCoordinates_, 0, this.flatCoordinates_.length, 2, .5);
            }
            return this.flatMidpoints_;
        }
        getFlatMidpoints() {
            if (!this.flatMidpoints_) {
                this.flatMidpoints_ = [];
                const flatCoordinates = this.flatCoordinates_;
                let offset = 0;
                const ends = this.ends_;
                for (let i = 0, ii = ends.length; i < ii; ++i) {
                    const end = ends[i];
                    const midpoint = interpolatePoint(flatCoordinates, offset, end, 2, .5);
                    extend$2(this.flatMidpoints_, midpoint);
                    offset = end;
                }
            }
            return this.flatMidpoints_;
        }
        getId() {
            return this.id_;
        }
        getOrientedFlatCoordinates() {
            return this.flatCoordinates_;
        }
        getGeometry() {
            return this;
        }
        getSimplifiedGeometry(squaredTolerance) {
            return this;
        }
        simplifyTransformed(squaredTolerance, transform) {
            return this;
        }
        getProperties() {
            return this.properties_;
        }
        getPropertiesInternal() {
            return this.properties_;
        }
        getStride() {
            return this.stride_;
        }
        getStyleFunction() {
            return this.styleFunction;
        }
        getType() {
            return this.type_;
        }
        transform(projection) {
            projection = get$1(projection);
            const pixelExtent = projection.getExtent();
            const projectedExtent = projection.getWorldExtent();
            if (pixelExtent && projectedExtent) {
                const scale = getHeight(projectedExtent) / getHeight(pixelExtent);
                compose(tmpTransform, projectedExtent[0], projectedExtent[3], scale, -scale, 0, 0, 0);
                transform2D(this.flatCoordinates_, 0, this.flatCoordinates_.length, 2, tmpTransform, this.flatCoordinates_);
            }
        }
        applyTransform(transformFn) {
            transformFn(this.flatCoordinates_, this.flatCoordinates_, this.stride_);
        }
        clone() {
            return new RenderFeature(this.type_, this.flatCoordinates_.slice(), this.ends_?.slice(), this.stride_, Object.assign({}, this.properties_), this.id_);
        }
        getEnds() {
            return this.ends_;
        }
        enableSimplifyTransformed() {
            this.simplifyTransformed = memoizeOne((squaredTolerance, transform) => {
                if (squaredTolerance === this.squaredTolerance_) {
                    return this.simplifiedGeometry_;
                }
                this.simplifiedGeometry_ = this.clone();
                if (transform) {
                    this.simplifiedGeometry_.applyTransform(transform);
                }
                const simplifiedFlatCoordinates = this.simplifiedGeometry_.getFlatCoordinates();
                let simplifiedEnds;
                switch (this.type_) {
                  case "LineString":
                    simplifiedFlatCoordinates.length = douglasPeucker(simplifiedFlatCoordinates, 0, this.simplifiedGeometry_.flatCoordinates_.length, this.simplifiedGeometry_.stride_, squaredTolerance, simplifiedFlatCoordinates, 0);
                    simplifiedEnds = [ simplifiedFlatCoordinates.length ];
                    break;

                  case "MultiLineString":
                    simplifiedEnds = [];
                    simplifiedFlatCoordinates.length = douglasPeuckerArray(simplifiedFlatCoordinates, 0, this.simplifiedGeometry_.ends_, this.simplifiedGeometry_.stride_, squaredTolerance, simplifiedFlatCoordinates, 0, simplifiedEnds);
                    break;

                  case "Polygon":
                    simplifiedEnds = [];
                    simplifiedFlatCoordinates.length = quantizeArray(simplifiedFlatCoordinates, 0, this.simplifiedGeometry_.ends_, this.simplifiedGeometry_.stride_, Math.sqrt(squaredTolerance), simplifiedFlatCoordinates, 0, simplifiedEnds);
                    break;
                }
                if (simplifiedEnds) {
                    this.simplifiedGeometry_ = new RenderFeature(this.type_, simplifiedFlatCoordinates, simplifiedEnds, 2, this.properties_, this.id_);
                }
                this.squaredTolerance_ = squaredTolerance;
                return this.simplifiedGeometry_;
            });
            return this;
        }
    }
    RenderFeature.prototype.getFlatCoordinates = RenderFeature.prototype.getOrientedFlatCoordinates;
    var VectorEventType = {
        ADDFEATURE: "addfeature",
        CHANGEFEATURE: "changefeature",
        CLEAR: "clear",
        REMOVEFEATURE: "removefeature",
        FEATURESLOADSTART: "featuresloadstart",
        FEATURESLOADEND: "featuresloadend",
        FEATURESLOADERROR: "featuresloaderror"
    };
    function all(extent, resolution) {
        return [ [ -Infinity, -Infinity, Infinity, Infinity ] ];
    }
    let withCredentials = false;
    function loadFeaturesXhr(url, format, extent, resolution, projection, success, failure) {
        const xhr = new XMLHttpRequest;
        xhr.open("GET", typeof url === "function" ? url(extent, resolution, projection) : url, true);
        if (format.getType() == "arraybuffer") {
            xhr.responseType = "arraybuffer";
        }
        xhr.withCredentials = withCredentials;
        xhr.onload = function(event) {
            if (!xhr.status || xhr.status >= 200 && xhr.status < 300) {
                const type = format.getType();
                try {
                    let source;
                    if (type == "text" || type == "json") {
                        source = xhr.responseText;
                    } else if (type == "xml") {
                        source = xhr.responseXML || xhr.responseText;
                    } else if (type == "arraybuffer") {
                        source = xhr.response;
                    }
                    if (source) {
                        success(format.readFeatures(source, {
                            extent: extent,
                            featureProjection: projection
                        }), format.readProjection(source));
                    } else {
                        failure();
                    }
                } catch {
                    failure();
                }
            } else {
                failure();
            }
        };
        xhr.onerror = failure;
        xhr.send();
    }
    function xhr(url, format) {
        return function(extent, resolution, projection, success, failure) {
            const source = this;
            loadFeaturesXhr(url, format, extent, resolution, projection, function(features, dataProjection) {
                source.addFeatures(features);
                if (success !== undefined) {
                    success(features);
                }
            }, failure ? failure : VOID);
        };
    }
    class VectorSourceEvent extends BaseEvent {
        constructor(type, feature, features) {
            super(type);
            this.feature = feature;
            this.features = features;
        }
    }
    class VectorSource extends Source {
        constructor(options) {
            options = options || {};
            super({
                attributions: options.attributions,
                interpolate: true,
                projection: undefined,
                state: "ready",
                wrapX: options.wrapX !== undefined ? options.wrapX : true
            });
            this.on;
            this.once;
            this.un;
            this.loader_ = VOID;
            this.format_ = options.format;
            this.overlaps_ = options.overlaps === undefined ? true : options.overlaps;
            this.url_ = options.url;
            if (options.loader !== undefined) {
                this.loader_ = options.loader;
            } else if (this.url_ !== undefined) {
                assert(this.format_, "`format` must be set when `url` is set");
                this.loader_ = xhr(this.url_, this.format_);
            }
            this.strategy_ = options.strategy !== undefined ? options.strategy : all;
            const useSpatialIndex = options.useSpatialIndex !== undefined ? options.useSpatialIndex : true;
            this.featuresRtree_ = useSpatialIndex ? new RBush : null;
            this.loadedExtentsRtree_ = new RBush;
            this.loadingExtentsCount_ = 0;
            this.nullGeometryFeatures_ = {};
            this.idIndex_ = {};
            this.uidIndex_ = {};
            this.featureChangeKeys_ = {};
            this.featuresCollection_ = null;
            let collection;
            let features;
            if (Array.isArray(options.features)) {
                features = options.features;
            } else if (options.features) {
                collection = options.features;
                features = collection.getArray();
            }
            if (!useSpatialIndex && collection === undefined) {
                collection = new Collection(features);
            }
            if (features !== undefined) {
                this.addFeaturesInternal(features);
            }
            if (collection !== undefined) {
                this.bindFeaturesCollection_(collection);
            }
        }
        addFeature(feature) {
            this.addFeatureInternal(feature);
            this.changed();
        }
        addFeatureInternal(feature) {
            const featureKey = getUid(feature);
            if (!this.addToIndex_(featureKey, feature)) {
                if (this.featuresCollection_) {
                    this.featuresCollection_.remove(feature);
                }
                return;
            }
            this.setupChangeEvents_(featureKey, feature);
            const geometry = feature.getGeometry();
            if (geometry) {
                const extent = geometry.getExtent();
                if (this.featuresRtree_) {
                    this.featuresRtree_.insert(extent, feature);
                }
            } else {
                this.nullGeometryFeatures_[featureKey] = feature;
            }
            this.dispatchEvent(new VectorSourceEvent(VectorEventType.ADDFEATURE, feature));
        }
        setupChangeEvents_(featureKey, feature) {
            if (feature instanceof RenderFeature) {
                return;
            }
            this.featureChangeKeys_[featureKey] = [ listen(feature, EventType.CHANGE, this.handleFeatureChange_, this), listen(feature, ObjectEventType.PROPERTYCHANGE, this.handleFeatureChange_, this) ];
        }
        addToIndex_(featureKey, feature) {
            let valid = true;
            if (feature.getId() !== undefined) {
                const id = String(feature.getId());
                if (!(id in this.idIndex_)) {
                    this.idIndex_[id] = feature;
                } else if (feature instanceof RenderFeature) {
                    const indexedFeature = this.idIndex_[id];
                    if (!(indexedFeature instanceof RenderFeature)) {
                        valid = false;
                    } else {
                        if (!Array.isArray(indexedFeature)) {
                            this.idIndex_[id] = [ indexedFeature, feature ];
                        } else {
                            indexedFeature.push(feature);
                        }
                    }
                } else {
                    valid = false;
                }
            }
            if (valid) {
                assert(!(featureKey in this.uidIndex_), "The passed `feature` was already added to the source");
                this.uidIndex_[featureKey] = feature;
            }
            return valid;
        }
        addFeatures(features) {
            this.addFeaturesInternal(features);
            this.changed();
        }
        addFeaturesInternal(features) {
            const extents = [];
            const newFeatures = [];
            const geometryFeatures = [];
            for (let i = 0, length = features.length; i < length; i++) {
                const feature = features[i];
                const featureKey = getUid(feature);
                if (this.addToIndex_(featureKey, feature)) {
                    newFeatures.push(feature);
                }
            }
            for (let i = 0, length = newFeatures.length; i < length; i++) {
                const feature = newFeatures[i];
                const featureKey = getUid(feature);
                this.setupChangeEvents_(featureKey, feature);
                const geometry = feature.getGeometry();
                if (geometry) {
                    const extent = geometry.getExtent();
                    extents.push(extent);
                    geometryFeatures.push(feature);
                } else {
                    this.nullGeometryFeatures_[featureKey] = feature;
                }
            }
            if (this.featuresRtree_) {
                this.featuresRtree_.load(extents, geometryFeatures);
            }
            if (this.hasListener(VectorEventType.ADDFEATURE)) {
                for (let i = 0, length = newFeatures.length; i < length; i++) {
                    this.dispatchEvent(new VectorSourceEvent(VectorEventType.ADDFEATURE, newFeatures[i]));
                }
            }
        }
        bindFeaturesCollection_(collection) {
            let modifyingCollection = false;
            this.addEventListener(VectorEventType.ADDFEATURE, function(evt) {
                if (!modifyingCollection) {
                    modifyingCollection = true;
                    collection.push(evt.feature);
                    modifyingCollection = false;
                }
            });
            this.addEventListener(VectorEventType.REMOVEFEATURE, function(evt) {
                if (!modifyingCollection) {
                    modifyingCollection = true;
                    collection.remove(evt.feature);
                    modifyingCollection = false;
                }
            });
            collection.addEventListener(CollectionEventType.ADD, evt => {
                if (!modifyingCollection) {
                    modifyingCollection = true;
                    this.addFeature(evt.element);
                    modifyingCollection = false;
                }
            });
            collection.addEventListener(CollectionEventType.REMOVE, evt => {
                if (!modifyingCollection) {
                    modifyingCollection = true;
                    this.removeFeature(evt.element);
                    modifyingCollection = false;
                }
            });
            this.featuresCollection_ = collection;
        }
        clear(fast) {
            if (fast) {
                for (const featureId in this.featureChangeKeys_) {
                    const keys = this.featureChangeKeys_[featureId];
                    keys.forEach(unlistenByKey);
                }
                if (!this.featuresCollection_) {
                    this.featureChangeKeys_ = {};
                    this.idIndex_ = {};
                    this.uidIndex_ = {};
                }
            } else {
                if (this.featuresRtree_) {
                    const removeAndIgnoreReturn = feature => {
                        this.removeFeatureInternal(feature);
                    };
                    this.featuresRtree_.forEach(removeAndIgnoreReturn);
                    for (const id in this.nullGeometryFeatures_) {
                        this.removeFeatureInternal(this.nullGeometryFeatures_[id]);
                    }
                }
            }
            if (this.featuresCollection_) {
                this.featuresCollection_.clear();
            }
            if (this.featuresRtree_) {
                this.featuresRtree_.clear();
            }
            this.nullGeometryFeatures_ = {};
            const clearEvent = new VectorSourceEvent(VectorEventType.CLEAR);
            this.dispatchEvent(clearEvent);
            this.changed();
        }
        forEachFeature(callback) {
            if (this.featuresRtree_) {
                return this.featuresRtree_.forEach(callback);
            }
            if (this.featuresCollection_) {
                this.featuresCollection_.forEach(callback);
            }
        }
        forEachFeatureAtCoordinateDirect(coordinate, callback) {
            const extent = [ coordinate[0], coordinate[1], coordinate[0], coordinate[1] ];
            return this.forEachFeatureInExtent(extent, function(feature) {
                const geometry = feature.getGeometry();
                if (geometry instanceof RenderFeature || geometry.intersectsCoordinate(coordinate)) {
                    return callback(feature);
                }
                return undefined;
            });
        }
        forEachFeatureInExtent(extent, callback) {
            if (this.featuresRtree_) {
                return this.featuresRtree_.forEachInExtent(extent, callback);
            }
            if (this.featuresCollection_) {
                this.featuresCollection_.forEach(callback);
            }
        }
        forEachFeatureIntersectingExtent(extent, callback) {
            return this.forEachFeatureInExtent(extent, function(feature) {
                const geometry = feature.getGeometry();
                if (geometry instanceof RenderFeature || geometry.intersectsExtent(extent)) {
                    const result = callback(feature);
                    if (result) {
                        return result;
                    }
                }
            });
        }
        getFeaturesCollection() {
            return this.featuresCollection_;
        }
        getFeatures() {
            let features;
            if (this.featuresCollection_) {
                features = this.featuresCollection_.getArray().slice(0);
            } else if (this.featuresRtree_) {
                features = this.featuresRtree_.getAll();
                if (!isEmpty$1(this.nullGeometryFeatures_)) {
                    extend$2(features, Object.values(this.nullGeometryFeatures_));
                }
            }
            return features;
        }
        getFeaturesAtCoordinate(coordinate) {
            const features = [];
            this.forEachFeatureAtCoordinateDirect(coordinate, function(feature) {
                features.push(feature);
            });
            return features;
        }
        getFeaturesInExtent(extent, projection) {
            if (this.featuresRtree_) {
                const multiWorld = projection && projection.canWrapX() && this.getWrapX();
                if (!multiWorld) {
                    return this.featuresRtree_.getInExtent(extent);
                }
                const extents = wrapAndSliceX(extent, projection);
                return [].concat(...extents.map(anExtent => this.featuresRtree_.getInExtent(anExtent)));
            }
            if (this.featuresCollection_) {
                return this.featuresCollection_.getArray().slice(0);
            }
            return [];
        }
        getClosestFeatureToCoordinate(coordinate, filter) {
            const x = coordinate[0];
            const y = coordinate[1];
            let closestFeature = null;
            const closestPoint = [ NaN, NaN ];
            let minSquaredDistance = Infinity;
            const extent = [ -Infinity, -Infinity, Infinity, Infinity ];
            filter = filter ? filter : TRUE;
            this.featuresRtree_.forEachInExtent(extent, function(feature) {
                if (filter(feature)) {
                    const geometry = feature.getGeometry();
                    const previousMinSquaredDistance = minSquaredDistance;
                    minSquaredDistance = geometry instanceof RenderFeature ? 0 : geometry.closestPointXY(x, y, closestPoint, minSquaredDistance);
                    if (minSquaredDistance < previousMinSquaredDistance) {
                        closestFeature = feature;
                        const minDistance = Math.sqrt(minSquaredDistance);
                        extent[0] = x - minDistance;
                        extent[1] = y - minDistance;
                        extent[2] = x + minDistance;
                        extent[3] = y + minDistance;
                    }
                }
            });
            return closestFeature;
        }
        getExtent(extent) {
            return this.featuresRtree_.getExtent(extent);
        }
        getFeatureById(id) {
            const feature = this.idIndex_[id.toString()];
            return feature !== undefined ? feature : null;
        }
        getFeatureByUid(uid) {
            const feature = this.uidIndex_[uid];
            return feature !== undefined ? feature : null;
        }
        getFormat() {
            return this.format_;
        }
        getOverlaps() {
            return this.overlaps_;
        }
        getUrl() {
            return this.url_;
        }
        handleFeatureChange_(event) {
            const feature = event.target;
            const featureKey = getUid(feature);
            const geometry = feature.getGeometry();
            if (!geometry) {
                if (!(featureKey in this.nullGeometryFeatures_)) {
                    if (this.featuresRtree_) {
                        this.featuresRtree_.remove(feature);
                    }
                    this.nullGeometryFeatures_[featureKey] = feature;
                }
            } else {
                const extent = geometry.getExtent();
                if (featureKey in this.nullGeometryFeatures_) {
                    delete this.nullGeometryFeatures_[featureKey];
                    if (this.featuresRtree_) {
                        this.featuresRtree_.insert(extent, feature);
                    }
                } else {
                    if (this.featuresRtree_) {
                        this.featuresRtree_.update(extent, feature);
                    }
                }
            }
            const id = feature.getId();
            if (id !== undefined) {
                const sid = id.toString();
                if (this.idIndex_[sid] !== feature) {
                    this.removeFromIdIndex_(feature);
                    this.idIndex_[sid] = feature;
                }
            } else {
                this.removeFromIdIndex_(feature);
                this.uidIndex_[featureKey] = feature;
            }
            this.changed();
            this.dispatchEvent(new VectorSourceEvent(VectorEventType.CHANGEFEATURE, feature));
        }
        hasFeature(feature) {
            const id = feature.getId();
            if (id !== undefined) {
                return id in this.idIndex_;
            }
            return getUid(feature) in this.uidIndex_;
        }
        isEmpty() {
            if (this.featuresRtree_) {
                return this.featuresRtree_.isEmpty() && isEmpty$1(this.nullGeometryFeatures_);
            }
            if (this.featuresCollection_) {
                return this.featuresCollection_.getLength() === 0;
            }
            return true;
        }
        loadFeatures(extent, resolution, projection) {
            const loadedExtentsRtree = this.loadedExtentsRtree_;
            const extentsToLoad = this.strategy_(extent, resolution, projection);
            for (let i = 0, ii = extentsToLoad.length; i < ii; ++i) {
                const extentToLoad = extentsToLoad[i];
                const alreadyLoaded = loadedExtentsRtree.forEachInExtent(extentToLoad, function(object) {
                    return containsExtent(object.extent, extentToLoad);
                });
                if (!alreadyLoaded) {
                    ++this.loadingExtentsCount_;
                    this.dispatchEvent(new VectorSourceEvent(VectorEventType.FEATURESLOADSTART));
                    this.loader_.call(this, extentToLoad, resolution, projection, features => {
                        --this.loadingExtentsCount_;
                        this.dispatchEvent(new VectorSourceEvent(VectorEventType.FEATURESLOADEND, undefined, features));
                    }, () => {
                        --this.loadingExtentsCount_;
                        this.dispatchEvent(new VectorSourceEvent(VectorEventType.FEATURESLOADERROR));
                    });
                    loadedExtentsRtree.insert(extentToLoad, {
                        extent: extentToLoad.slice()
                    });
                }
            }
            this.loading = this.loader_.length < 4 ? false : this.loadingExtentsCount_ > 0;
        }
        refresh() {
            this.clear(true);
            this.loadedExtentsRtree_.clear();
            super.refresh();
        }
        removeLoadedExtent(extent) {
            const loadedExtentsRtree = this.loadedExtentsRtree_;
            let obj;
            loadedExtentsRtree.forEachInExtent(extent, function(object) {
                if (equals$1(object.extent, extent)) {
                    obj = object;
                    return true;
                }
            });
            if (obj) {
                loadedExtentsRtree.remove(obj);
            }
        }
        removeFeatures(features) {
            const removedFeatures = [];
            for (let i = 0, ii = features.length; i < ii; ++i) {
                const feature = features[i];
                const removedFeature = this.removeFeatureInternal(feature);
                if (removedFeature) {
                    removedFeatures.push(removedFeature);
                }
            }
            if (removedFeatures.length > 0) {
                this.changed();
            }
        }
        removeFeature(feature) {
            if (!feature) {
                return;
            }
            const result = this.removeFeatureInternal(feature);
            if (result) {
                this.changed();
            }
        }
        removeFeatureInternal(feature) {
            const featureKey = getUid(feature);
            if (!(featureKey in this.uidIndex_)) {
                return;
            }
            if (featureKey in this.nullGeometryFeatures_) {
                delete this.nullGeometryFeatures_[featureKey];
            } else {
                if (this.featuresRtree_) {
                    this.featuresRtree_.remove(feature);
                }
            }
            const featureChangeKeys = this.featureChangeKeys_[featureKey];
            featureChangeKeys?.forEach(unlistenByKey);
            delete this.featureChangeKeys_[featureKey];
            const id = feature.getId();
            if (id !== undefined) {
                const idString = id.toString();
                const indexedFeature = this.idIndex_[idString];
                if (indexedFeature === feature) {
                    delete this.idIndex_[idString];
                } else if (Array.isArray(indexedFeature)) {
                    indexedFeature.splice(indexedFeature.indexOf(feature), 1);
                    if (indexedFeature.length === 1) {
                        this.idIndex_[idString] = indexedFeature[0];
                    }
                }
            }
            delete this.uidIndex_[featureKey];
            if (this.hasListener(VectorEventType.REMOVEFEATURE)) {
                this.dispatchEvent(new VectorSourceEvent(VectorEventType.REMOVEFEATURE, feature));
            }
            return feature;
        }
        removeFromIdIndex_(feature) {
            let removed = false;
            for (const id in this.idIndex_) {
                const indexedFeature = this.idIndex_[id];
                if (feature instanceof RenderFeature && Array.isArray(indexedFeature) && indexedFeature.includes(feature)) {
                    indexedFeature.splice(indexedFeature.indexOf(feature), 1);
                } else if (this.idIndex_[id] === feature) {
                    delete this.idIndex_[id];
                    removed = true;
                    break;
                }
            }
            return removed;
        }
        setLoader(loader) {
            this.loader_ = loader;
        }
        setUrl(url) {
            assert(this.format_, "`format` must be set when `url` is set");
            this.url_ = url;
            this.setLoader(xhr(url, this.format_));
        }
    }
    const ol = {};
    ol.Map = Map;
    ol.View = View;
    ol.Feature = Feature;
    ol.proj = {
        fromLonLat: fromLonLat,
        toLonLat: toLonLat
    };
    ol.geom = {
        Point: Point
    };
    ol.layer = {
        Tile: TileLayer,
        Vector: VectorLayer
    };
    ol.source = {
        OSM: OSM,
        Vector: VectorSource
    };
    ol.style = {
        Style: Style,
        Circle: CircleStyle,
        Fill: Fill,
        Stroke: Stroke
    };
    ol.control = {
        defaults: defaults$1
    };
    ol.interaction = {
        defaults: defaults,
        Pointer: PointerInteraction
    };
    return ol;
}();
