/**
 * @module ol/source/WMTS
 */
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
import TileImage from './TileImage.js';
import { appendParams } from '../uri.js';
import { assign } from '../obj.js';
import { containsExtent } from '../extent.js';
import { createFromCapabilitiesMatrixSet } from '../tilegrid/WMTS.js';
import { createFromTileUrlFunctions, expandUrl } from '../tileurlfunction.js';
import { equivalent, get as getProjection, transformExtent } from '../proj.js';
import { find, findIndex, includes } from '../array.js';
/**
 * Request encoding. One of 'KVP', 'REST'.
 * @typedef {'KVP' | 'REST'} RequestEncoding
 */
/**
 * @typedef {Object} Options
 * @property {import("./Source.js").AttributionLike} [attributions] Attributions.
 * @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
 * @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least the number of tiles in the viewport.
 * @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images.  Note that
 * you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
 * See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.
 * @property {boolean} [imageSmoothing=true] Deprecated.  Use the `interpolate` option instead.
 * @property {boolean} [interpolate=true] Use interpolated values when resampling.  By default,
 * linear interpolation is used when resampling.  Set to false to use the nearest neighbor instead.
 * @property {import("../tilegrid/WMTS.js").default} tileGrid Tile grid.
 * @property {import("../proj.js").ProjectionLike} [projection] Projection. Default is the view projection.
 * @property {number} [reprojectionErrorThreshold=0.5] Maximum allowed reprojection error (in pixels).
 * Higher values can increase reprojection performance, but decrease precision.
 * @property {RequestEncoding} [requestEncoding='KVP'] Request encoding.
 * @property {string} layer Layer name as advertised in the WMTS capabilities.
 * @property {string} style Style name as advertised in the WMTS capabilities.
 * @property {typeof import("../ImageTile.js").default} [tileClass]  Class used to instantiate image tiles. Default is {@link module:ol/ImageTile~ImageTile}.
 * @property {number} [tilePixelRatio=1] The pixel ratio used by the tile service.
 * For example, if the tile service advertizes 256px by 256px tiles but actually sends 512px
 * by 512px images (for retina/hidpi devices) then `tilePixelRatio`
 * should be set to `2`.
 * @property {string} [format='image/jpeg'] Image format. Only used when `requestEncoding` is `'KVP'`.
 * @property {string} [version='1.0.0'] WMTS version.
 * @property {string} matrixSet Matrix set.
 * @property {!Object} [dimensions] Additional "dimensions" for tile requests.
 * This is an object with properties named like the advertised WMTS dimensions.
 * @property {string} [url]  A URL for the service.
 * For the RESTful request encoding, this is a URL
 * template.  For KVP encoding, it is normal URL. A `{?-?}` template pattern,
 * for example `subdomain{a-f}.domain.com`, may be used instead of defining
 * each one separately in the `urls` option.
 * @property {import("../Tile.js").LoadFunction} [tileLoadFunction] Optional function to load a tile given a URL. The default is
 * ```js
 * function(imageTile, src) {
 *   imageTile.getImage().src = src;
 * };
 * ```
 * @property {Array<string>} [urls] An array of URLs.
 * Requests will be distributed among the URLs in this array.
 * @property {boolean} [wrapX=false] Whether to wrap the world horizontally.
 * @property {number} [transition] Duration of the opacity transition for rendering.
 * To disable the opacity transition, pass `transition: 0`.
 * @property {number|import("../array.js").NearestDirectionFunction} [zDirection=0]
 * Choose whether to use tiles with a higher or lower zoom level when between integer
 * zoom levels. See {@link module:ol/tilegrid/TileGrid~TileGrid#getZForResolution}.
 */
/**
 * @classdesc
 * Layer source for tile data from WMTS servers.
 * @api
 */
var WMTS = /** @class */ (function (_super) {
    __extends(WMTS, _super);
    /**
     * @param {Options} options WMTS options.
     */
    function WMTS(options) {
        var _this = this;
        var interpolate = options.imageSmoothing !== undefined ? options.imageSmoothing : true;
        if (options.interpolate !== undefined) {
            interpolate = options.interpolate;
        }
        // TODO: add support for TileMatrixLimits
        var requestEncoding = options.requestEncoding !== undefined ? options.requestEncoding : 'KVP';
        // FIXME: should we create a default tileGrid?
        // we could issue a getCapabilities xhr to retrieve missing configuration
        var tileGrid = options.tileGrid;
        var urls = options.urls;
        if (urls === undefined && options.url !== undefined) {
            urls = expandUrl(options.url);
        }
        _this = _super.call(this, {
            attributions: options.attributions,
            attributionsCollapsible: options.attributionsCollapsible,
            cacheSize: options.cacheSize,
            crossOrigin: options.crossOrigin,
            interpolate: interpolate,
            projection: options.projection,
            reprojectionErrorThreshold: options.reprojectionErrorThreshold,
            tileClass: options.tileClass,
            tileGrid: tileGrid,
            tileLoadFunction: options.tileLoadFunction,
            tilePixelRatio: options.tilePixelRatio,
            urls: urls,
            wrapX: options.wrapX !== undefined ? options.wrapX : false,
            transition: options.transition,
            zDirection: options.zDirection,
        }) || this;
        /**
         * @private
         * @type {string}
         */
        _this.version_ = options.version !== undefined ? options.version : '1.0.0';
        /**
         * @private
         * @type {string}
         */
        _this.format_ = options.format !== undefined ? options.format : 'image/jpeg';
        /**
         * @private
         * @type {!Object}
         */
        _this.dimensions_ =
            options.dimensions !== undefined ? options.dimensions : {};
        /**
         * @private
         * @type {string}
         */
        _this.layer_ = options.layer;
        /**
         * @private
         * @type {string}
         */
        _this.matrixSet_ = options.matrixSet;
        /**
         * @private
         * @type {string}
         */
        _this.style_ = options.style;
        // FIXME: should we guess this requestEncoding from options.url(s)
        //        structure? that would mean KVP only if a template is not provided.
        /**
         * @private
         * @type {RequestEncoding}
         */
        _this.requestEncoding_ = requestEncoding;
        _this.setKey(_this.getKeyForDimensions_());
        if (urls && urls.length > 0) {
            _this.tileUrlFunction = createFromTileUrlFunctions(urls.map(_this.createFromWMTSTemplate.bind(_this)));
        }
        return _this;
    }
    /**
     * Set the URLs to use for requests.
     * URLs may contain OGC conform URL Template Variables: {TileMatrix}, {TileRow}, {TileCol}.
     * @param {Array<string>} urls URLs.
     */
    WMTS.prototype.setUrls = function (urls) {
        this.urls = urls;
        var key = urls.join('\n');
        this.setTileUrlFunction(createFromTileUrlFunctions(urls.map(this.createFromWMTSTemplate.bind(this))), key);
    };
    /**
     * Get the dimensions, i.e. those passed to the constructor through the
     * "dimensions" option, and possibly updated using the updateDimensions
     * method.
     * @return {!Object} Dimensions.
     * @api
     */
    WMTS.prototype.getDimensions = function () {
        return this.dimensions_;
    };
    /**
     * Return the image format of the WMTS source.
     * @return {string} Format.
     * @api
     */
    WMTS.prototype.getFormat = function () {
        return this.format_;
    };
    /**
     * Return the layer of the WMTS source.
     * @return {string} Layer.
     * @api
     */
    WMTS.prototype.getLayer = function () {
        return this.layer_;
    };
    /**
     * Return the matrix set of the WMTS source.
     * @return {string} MatrixSet.
     * @api
     */
    WMTS.prototype.getMatrixSet = function () {
        return this.matrixSet_;
    };
    /**
     * Return the request encoding, either "KVP" or "REST".
     * @return {RequestEncoding} Request encoding.
     * @api
     */
    WMTS.prototype.getRequestEncoding = function () {
        return this.requestEncoding_;
    };
    /**
     * Return the style of the WMTS source.
     * @return {string} Style.
     * @api
     */
    WMTS.prototype.getStyle = function () {
        return this.style_;
    };
    /**
     * Return the version of the WMTS source.
     * @return {string} Version.
     * @api
     */
    WMTS.prototype.getVersion = function () {
        return this.version_;
    };
    /**
     * @private
     * @return {string} The key for the current dimensions.
     */
    WMTS.prototype.getKeyForDimensions_ = function () {
        var i = 0;
        var res = [];
        for (var key in this.dimensions_) {
            res[i++] = key + '-' + this.dimensions_[key];
        }
        return res.join('/');
    };
    /**
     * Update the dimensions.
     * @param {Object} dimensions Dimensions.
     * @api
     */
    WMTS.prototype.updateDimensions = function (dimensions) {
        assign(this.dimensions_, dimensions);
        this.setKey(this.getKeyForDimensions_());
    };
    /**
     * @param {string} template Template.
     * @return {import("../Tile.js").UrlFunction} Tile URL function.
     */
    WMTS.prototype.createFromWMTSTemplate = function (template) {
        var requestEncoding = this.requestEncoding_;
        // context property names are lower case to allow for a case insensitive
        // replacement as some services use different naming conventions
        var context = {
            'layer': this.layer_,
            'style': this.style_,
            'tilematrixset': this.matrixSet_,
        };
        if (requestEncoding == 'KVP') {
            assign(context, {
                'Service': 'WMTS',
                'Request': 'GetTile',
                'Version': this.version_,
                'Format': this.format_,
            });
        }
        // TODO: we may want to create our own appendParams function so that params
        // order conforms to wmts spec guidance, and so that we can avoid to escape
        // special template params
        template =
            requestEncoding == 'KVP'
                ? appendParams(template, context)
                : template.replace(/\{(\w+?)\}/g, function (m, p) {
                    return p.toLowerCase() in context ? context[p.toLowerCase()] : m;
                });
        var tileGrid = /** @type {import("../tilegrid/WMTS.js").default} */ (this.tileGrid);
        var dimensions = this.dimensions_;
        return (
        /**
         * @param {import("../tilecoord.js").TileCoord} tileCoord Tile coordinate.
         * @param {number} pixelRatio Pixel ratio.
         * @param {import("../proj/Projection.js").default} projection Projection.
         * @return {string|undefined} Tile URL.
         */
        function (tileCoord, pixelRatio, projection) {
            if (!tileCoord) {
                return undefined;
            }
            else {
                var localContext_1 = {
                    'TileMatrix': tileGrid.getMatrixId(tileCoord[0]),
                    'TileCol': tileCoord[1],
                    'TileRow': tileCoord[2],
                };
                assign(localContext_1, dimensions);
                var url = template;
                if (requestEncoding == 'KVP') {
                    url = appendParams(url, localContext_1);
                }
                else {
                    url = url.replace(/\{(\w+?)\}/g, function (m, p) {
                        return localContext_1[p];
                    });
                }
                return url;
            }
        });
    };
    return WMTS;
}(TileImage));
export default WMTS;
/**
 * Generate source options from a capabilities object.
 * @param {Object} wmtsCap An object representing the capabilities document.
 * @param {!Object} config Configuration properties for the layer.  Defaults for
 *                  the layer will apply if not provided.
 *
 * Required config properties:
 *  - layer - {string} The layer identifier.
 *
 * Optional config properties:
 *  - matrixSet - {string} The matrix set identifier, required if there is
 *       more than one matrix set in the layer capabilities.
 *  - projection - {string} The desired CRS when no matrixSet is specified.
 *       eg: "EPSG:3857". If the desired projection is not available,
 *       an error is thrown.
 *  - requestEncoding - {string} url encoding format for the layer. Default is
 *       the first tile url format found in the GetCapabilities response.
 *  - style - {string} The name of the style
 *  - format - {string} Image format for the layer. Default is the first
 *       format returned in the GetCapabilities response.
 *  - crossOrigin - {string|null|undefined} Cross origin. Default is `undefined`.
 * @return {Options|null} WMTS source options object or `null` if the layer was not found.
 * @api
 */
export function optionsFromCapabilities(wmtsCap, config) {
    var layers = wmtsCap['Contents']['Layer'];
    var l = find(layers, function (elt, index, array) {
        return elt['Identifier'] == config['layer'];
    });
    if (l === null) {
        return null;
    }
    var tileMatrixSets = wmtsCap['Contents']['TileMatrixSet'];
    var idx;
    if (l['TileMatrixSetLink'].length > 1) {
        if ('projection' in config) {
            idx = findIndex(l['TileMatrixSetLink'], function (elt, index, array) {
                var tileMatrixSet = find(tileMatrixSets, function (el) {
                    return el['Identifier'] == elt['TileMatrixSet'];
                });
                var supportedCRS = tileMatrixSet['SupportedCRS'];
                var proj1 = getProjection(supportedCRS);
                var proj2 = getProjection(config['projection']);
                if (proj1 && proj2) {
                    return equivalent(proj1, proj2);
                }
                else {
                    return supportedCRS == config['projection'];
                }
            });
        }
        else {
            idx = findIndex(l['TileMatrixSetLink'], function (elt, index, array) {
                return elt['TileMatrixSet'] == config['matrixSet'];
            });
        }
    }
    else {
        idx = 0;
    }
    if (idx < 0) {
        idx = 0;
    }
    var matrixSet = 
    /** @type {string} */
    (l['TileMatrixSetLink'][idx]['TileMatrixSet']);
    var matrixLimits = 
    /** @type {Array<Object>} */
    (l['TileMatrixSetLink'][idx]['TileMatrixSetLimits']);
    var format = /** @type {string} */ (l['Format'][0]);
    if ('format' in config) {
        format = config['format'];
    }
    idx = findIndex(l['Style'], function (elt, index, array) {
        if ('style' in config) {
            return elt['Title'] == config['style'];
        }
        else {
            return elt['isDefault'];
        }
    });
    if (idx < 0) {
        idx = 0;
    }
    var style = /** @type {string} */ (l['Style'][idx]['Identifier']);
    var dimensions = {};
    if ('Dimension' in l) {
        l['Dimension'].forEach(function (elt, index, array) {
            var key = elt['Identifier'];
            var value = elt['Default'];
            if (value === undefined) {
                value = elt['Value'][0];
            }
            dimensions[key] = value;
        });
    }
    var matrixSets = wmtsCap['Contents']['TileMatrixSet'];
    var matrixSetObj = find(matrixSets, function (elt, index, array) {
        return elt['Identifier'] == matrixSet;
    });
    var projection;
    var code = matrixSetObj['SupportedCRS'];
    if (code) {
        projection = getProjection(code);
    }
    if ('projection' in config) {
        var projConfig = getProjection(config['projection']);
        if (projConfig) {
            if (!projection || equivalent(projConfig, projection)) {
                projection = projConfig;
            }
        }
    }
    var wrapX = false;
    var switchXY = projection.getAxisOrientation().substr(0, 2) == 'ne';
    var matrix = matrixSetObj.TileMatrix[0];
    // create default matrixLimit
    var selectedMatrixLimit = {
        MinTileCol: 0,
        MinTileRow: 0,
        // subtract one to end up at tile top left
        MaxTileCol: matrix.MatrixWidth - 1,
        MaxTileRow: matrix.MatrixHeight - 1,
    };
    //in case of matrix limits, use matrix limits to calculate extent
    if (matrixLimits) {
        selectedMatrixLimit = matrixLimits[matrixLimits.length - 1];
        var m = find(matrixSetObj.TileMatrix, function (tileMatrixValue) {
            return tileMatrixValue.Identifier === selectedMatrixLimit.TileMatrix ||
                matrixSetObj.Identifier + ':' + tileMatrixValue.Identifier ===
                    selectedMatrixLimit.TileMatrix;
        });
        if (m) {
            matrix = m;
        }
    }
    var resolution = (matrix.ScaleDenominator * 0.00028) / projection.getMetersPerUnit(); // WMTS 1.0.0: standardized rendering pixel size
    var origin = switchXY
        ? [matrix.TopLeftCorner[1], matrix.TopLeftCorner[0]]
        : matrix.TopLeftCorner;
    var tileSpanX = matrix.TileWidth * resolution;
    var tileSpanY = matrix.TileHeight * resolution;
    var matrixSetExtent = matrixSetObj['BoundingBox'];
    if (matrixSetExtent && switchXY) {
        matrixSetExtent = [
            matrixSetExtent[1],
            matrixSetExtent[0],
            matrixSetExtent[3],
            matrixSetExtent[2],
        ];
    }
    var extent = [
        origin[0] + tileSpanX * selectedMatrixLimit.MinTileCol,
        // add one to get proper bottom/right coordinate
        origin[1] - tileSpanY * (1 + selectedMatrixLimit.MaxTileRow),
        origin[0] + tileSpanX * (1 + selectedMatrixLimit.MaxTileCol),
        origin[1] - tileSpanY * selectedMatrixLimit.MinTileRow,
    ];
    if (matrixSetExtent !== undefined &&
        !containsExtent(matrixSetExtent, extent)) {
        var wgs84BoundingBox = l['WGS84BoundingBox'];
        var wgs84ProjectionExtent = getProjection('EPSG:4326').getExtent();
        extent = matrixSetExtent;
        if (wgs84BoundingBox) {
            wrapX =
                wgs84BoundingBox[0] === wgs84ProjectionExtent[0] &&
                    wgs84BoundingBox[2] === wgs84ProjectionExtent[2];
        }
        else {
            var wgs84MatrixSetExtent = transformExtent(matrixSetExtent, matrixSetObj['SupportedCRS'], 'EPSG:4326');
            // Ignore slight deviation from the correct x limits
            wrapX =
                wgs84MatrixSetExtent[0] - 1e-10 <= wgs84ProjectionExtent[0] &&
                    wgs84MatrixSetExtent[2] + 1e-10 >= wgs84ProjectionExtent[2];
        }
    }
    var tileGrid = createFromCapabilitiesMatrixSet(matrixSetObj, extent, matrixLimits);
    /** @type {!Array<string>} */
    var urls = [];
    var requestEncoding = config['requestEncoding'];
    requestEncoding = requestEncoding !== undefined ? requestEncoding : '';
    if ('OperationsMetadata' in wmtsCap &&
        'GetTile' in wmtsCap['OperationsMetadata']) {
        var gets = wmtsCap['OperationsMetadata']['GetTile']['DCP']['HTTP']['Get'];
        for (var i = 0, ii = gets.length; i < ii; ++i) {
            if (gets[i]['Constraint']) {
                var constraint = find(gets[i]['Constraint'], function (element) {
                    return element['name'] == 'GetEncoding';
                });
                var encodings = constraint['AllowedValues']['Value'];
                if (requestEncoding === '') {
                    // requestEncoding not provided, use the first encoding from the list
                    requestEncoding = encodings[0];
                }
                if (requestEncoding === 'KVP') {
                    if (includes(encodings, 'KVP')) {
                        urls.push(/** @type {string} */ (gets[i]['href']));
                    }
                }
                else {
                    break;
                }
            }
            else if (gets[i]['href']) {
                requestEncoding = 'KVP';
                urls.push(/** @type {string} */ (gets[i]['href']));
            }
        }
    }
    if (urls.length === 0) {
        requestEncoding = 'REST';
        l['ResourceURL'].forEach(function (element) {
            if (element['resourceType'] === 'tile') {
                format = element['format'];
                urls.push(/** @type {string} */ (element['template']));
            }
        });
    }
    return {
        urls: urls,
        layer: config['layer'],
        matrixSet: matrixSet,
        format: format,
        projection: projection,
        requestEncoding: requestEncoding,
        tileGrid: tileGrid,
        style: style,
        dimensions: dimensions,
        wrapX: wrapX,
        crossOrigin: config['crossOrigin'],
    };
}
//# sourceMappingURL=WMTS.js.map