diff --git a/web/client/actions/mapInfo.js b/web/client/actions/mapInfo.js index 3403abab17..0182edcfb2 100644 --- a/web/client/actions/mapInfo.js +++ b/web/client/actions/mapInfo.js @@ -100,28 +100,18 @@ function getVectorInfo(layer, request, metadata) { /** - * Sends a wms GetFeatureInfo request and dispatches the right action + * Sends a GetFeatureInfo request and dispatches the right action * in case of success, error or exceptions. * - * @param wmsBasePath {string} base path to the wms service + * @param basePath {string} base path to the service * @param requestParams {object} map of params for a getfeatureinfo request. */ -function getFeatureInfo(wmsBasePath, requestParams, lMetaData, options = {}) { - const defaultParams = assign({ - service: 'WMS', - version: '1.1.1', - request: 'GetFeatureInfo', - srs: 'EPSG:4326', - info_format: 'application/json', - x: 0, - y: 0, - exceptions: 'application/json' - }, options); - const param = assign({}, defaultParams, requestParams); +function getFeatureInfo(basePath, requestParams, lMetaData, options = {}) { + const param = assign({}, options, requestParams); const reqId = uuid.v1(); return (dispatch) => { dispatch(newMapInfoRequest(reqId, param)); - axios.get(wmsBasePath, {params: param}).then((response) => { + axios.get(basePath, {params: param}).then((response) => { if (response.data.exceptions) { dispatch(exceptionsFeatureInfo(reqId, response.data.exceptions, requestParams, lMetaData)); } else { diff --git a/web/client/components/map/openlayers/plugins/WMTSLayer.js b/web/client/components/map/openlayers/plugins/WMTSLayer.js index b730f64132..3812af626f 100644 --- a/web/client/components/map/openlayers/plugins/WMTSLayer.js +++ b/web/client/components/map/openlayers/plugins/WMTSLayer.js @@ -8,7 +8,7 @@ var Layers = require('../../../../utils/openlayers/Layers'); var ol = require('openlayers'); -const {isArray, isObject, slice} = require('lodash'); +const {isArray} = require('lodash'); // const SecurityUtils = require('../../../../utils/SecurityUtils'); const WMTSUtils = require('../../../../utils/WMTSUtils'); const CoordinatesUtils = require('../../../../utils/CoordinatesUtils'); @@ -19,36 +19,13 @@ function getWMSURLs( urls ) { return urls.map((url) => url.split("\?")[0]); } -function getDefaultMatrixId(options) { - let matrixIds = new Array(30); - for (let z = 0; z < 30; ++z) { - // generate matrixIds arrays for this WMTS - matrixIds[z] = options.tileMatrixPrefix + z; - } - return matrixIds; -} - -function getMatrixIds(matrix, srs) { - return (isObject(matrix) && matrix[srs] || matrix).map((el) => el.identifier); -} - -const limitMatrix = (matrix, len) => { - if (matrix.length > len) { - return slice(matrix, 0, len); - } - if (matrix.length < len) { - return matrix.concat(new Array(len - matrix.length)); - } - return matrix; -}; - Layers.registerType('wmts', { create: (options) => { const urls = getWMSURLs(isArray(options.url) ? options.url : [options.url]); const srs = CoordinatesUtils.normalizeSRS(options.srs || 'EPSG:3857', options.allowedSRS); const tileMatrixSet = WMTSUtils.getTileMatrixSet(options.tileMatrixSet, srs, options.allowedSRS); const resolutions = options.resolutions || mapUtils.getResolutions(); - const matrixIds = limitMatrix(options.matrixIds && getMatrixIds(options.matrixIds, tileMatrixSet || srs) || getDefaultMatrixId(options), resolutions.length); + const matrixIds = WMTSUtils.limitMatrix(options.matrixIds && WMTSUtils.getMatrixIds(options.matrixIds, tileMatrixSet || srs) || WMTSUtils.getDefaultMatrixId(options), resolutions.length); const extent = options.bbox ? ol.extent.applyTransform([parseFloat(options.bbox.bounds.minx), parseFloat(options.bbox.bounds.miny), parseFloat(options.bbox.bounds.maxx), parseFloat(options.bbox.bounds.maxy)], ol.proj.getTransform(options.bbox.crs, options.srs)) : null; // urls.forEach(url => SecurityUtils.addAuthenticationParameter(url, queryParameters)); return new ol.layer.Tile({ diff --git a/web/client/utils/CoordinatesUtils.js b/web/client/utils/CoordinatesUtils.js index 3ee29dc62b..d25fe0e347 100644 --- a/web/client/utils/CoordinatesUtils.js +++ b/web/client/utils/CoordinatesUtils.js @@ -71,6 +71,50 @@ const CoordinatesUtils = { } return null; }, + /** + * Creates a bbox of size dimensions areund the center point given to it given the + * resolution and the rotation + * @param center {object} the x,y coordinate of the point + * @param resolution {number} the resolution of the map + * @param rotation {number} the optional rotation of the new bbox + * @param size {object} width,height of the desired bbox + * @return {object} the desired bbox {minx, miny, maxx, maxy} + */ + getProjectedBBox: function(center, resolution, rotation = 0, size) { + let dx = resolution * size[0] / 2; + let dy = resolution * size[1] / 2; + let cosRotation = Math.cos(rotation); + let sinRotation = Math.sin(rotation); + let xCos = dx * cosRotation; + let xSin = dx * sinRotation; + let yCos = dy * cosRotation; + let ySin = dy * sinRotation; + let x = center.x; + let y = center.y; + let x0 = x - xCos + ySin; + let x1 = x - xCos - ySin; + let x2 = x + xCos - ySin; + let x3 = x + xCos + ySin; + let y0 = y - xSin - yCos; + let y1 = y - xSin + yCos; + let y2 = y + xSin + yCos; + let y3 = y + xSin - yCos; + let bounds = CoordinatesUtils.createBBox( + Math.min(x0, x1, x2, x3), Math.min(y0, y1, y2, y3), + Math.max(x0, x1, x2, x3), Math.max(y0, y1, y2, y3)); + return bounds; + }, + /** + * Returns a bounds object. + * @param {number} minX Minimum X. + * @param {number} minY Minimum Y. + * @param {number} maxX Maximum X. + * @param {number} maxY Maximum Y. + * @return {Object} Extent. + */ + createBBox(minX, minY, maxX, maxY) { + return { minx: minX, miny: minY, maxx: maxX, maxy: maxY }; + }, /** * Reprojects a geojson from a crs into another */ diff --git a/web/client/utils/MapInfoUtils.js b/web/client/utils/MapInfoUtils.js index 8398f6477c..0bf998f6ac 100644 --- a/web/client/utils/MapInfoUtils.js +++ b/web/client/utils/MapInfoUtils.js @@ -9,12 +9,7 @@ const FeatureInfoUtils = require("./FeatureInfoUtils"); const INFO_FORMATS = FeatureInfoUtils.INFO_FORMATS; const INFO_FORMATS_BY_MIME_TYPE = FeatureInfoUtils.INFO_FORMATS_BY_MIME_TYPE; - -const {isArray} = require('lodash'); -const assign = require('object-assign'); const pointOnSurface = require('turf-point-on-surface'); -const CoordinatesUtils = require('./CoordinatesUtils'); -const MapUtils = require('./MapUtils'); const MapInfoUtils = { /** @@ -91,121 +86,9 @@ const MapInfoUtils = { ...otherParams }; }, - /** - * Returns a bounds object. - * @param {number} minX Minimum X. - * @param {number} minY Minimum Y. - * @param {number} maxX Maximum X. - * @param {number} maxY Maximum Y. - * @return {Object} Extent. - */ - createBBox(minX, minY, maxX, maxY) { - return { minx: minX, miny: minY, maxx: maxX, maxy: maxY }; - }, - /** - * Creates a bbox of size dimensions areund the center point given to it given the - * resolution and the rotation - * @param center {object} the x,y coordinate of the point - * @param resolution {number} the resolution of the map - * @param rotation {number} the optional rotation of the new bbox - * @param size {object} width,height of the desired bbox - * @return {object} the desired bbox {minx, miny, maxx, maxy} - */ - getProjectedBBox(center, resolution, rotation = 0, size) { - let dx = resolution * size[0] / 2; - let dy = resolution * size[1] / 2; - let cosRotation = Math.cos(rotation); - let sinRotation = Math.sin(rotation); - let xCos = dx * cosRotation; - let xSin = dx * sinRotation; - let yCos = dy * cosRotation; - let ySin = dy * sinRotation; - let x = center.x; - let y = center.y; - let x0 = x - xCos + ySin; - let x1 = x - xCos - ySin; - let x2 = x + xCos - ySin; - let x3 = x + xCos + ySin; - let y0 = y - xSin - yCos; - let y1 = y - xSin + yCos; - let y2 = y + xSin + yCos; - let y3 = y + xSin - yCos; - let bounds = MapInfoUtils.createBBox( - Math.min(x0, x1, x2, x3), Math.min(y0, y1, y2, y3), - Math.max(x0, x1, x2, x3), Math.max(y0, y1, y2, y3)); - return bounds; - }, - buildIdentifyVectorRequest(layer, props) { - return { - request: { - lat: props.point.latlng.lat, - lng: props.point.latlng.lng - }, - metadata: { - fields: Object.keys(layer.features[0].properties), - title: layer.name, - resolution: props.map && props.map && props.map.zoom && MapUtils.getCurrentResolution(props.map.zoom, 0, 21, 96), - buffer: props.buffer, - units: props.map && props.map.units - }, - url: "" - }; - }, - buildIdentifyWMSRequest(layer, props) { - /* In order to create a valid feature info request - * we create a bbox of 101x101 pixel that wrap the point. - * center point is repojected then is built a box of 101x101pixel around it - */ - const heightBBox = (props && props.sizeBBox && props.sizeBBox.height) || 101; - const widthBBox = (props && props.sizeBBox && props.sizeBBox.width) || 101; - const size = [heightBBox, widthBBox]; - const rotation = 0; - const resolution = MapUtils.getCurrentResolution(Math.ceil(props.map.zoom), 0, 21, 96); - let wrongLng = props.point.latlng.lng; - // longitude restricted to the [-180°,+180°] range - let lngCorrected = wrongLng - (360) * Math.floor(wrongLng / (360) + 0.5); - const center = {x: lngCorrected, y: props.point.latlng.lat}; - let centerProjected = CoordinatesUtils.reproject(center, 'EPSG:4326', props.map.projection); - let bounds = MapInfoUtils.getProjectedBBox(centerProjected, resolution, rotation, size, null); - let queryLayers = layer.name; - if (layer.queryLayers) { - queryLayers = layer.queryLayers.join(","); - } - - return { - request: { - id: layer.id, - layers: layer.name, - query_layers: queryLayers, - styles: layer.style, - x: ((widthBBox % 2) === 1) ? Math.ceil(widthBBox / 2) : widthBBox / 2, - y: ((widthBBox % 2) === 1) ? Math.ceil(widthBBox / 2) : widthBBox / 2, - height: heightBBox, - width: widthBBox, - srs: CoordinatesUtils.normalizeSRS(props.map.projection), - bbox: bounds.minx + "," + - bounds.miny + "," + - bounds.maxx + "," + - bounds.maxy, - feature_count: props.maxItems, - info_format: props.format, - ...assign({}, layer.baseParams, layer.params, props.params) - }, - metadata: { - title: layer.title, - regex: layer.featureInfoRegex - }, - url: isArray(layer.url) ? - layer.url[0] : - layer.url.replace(/[?].*$/g, '') - }; - }, buildIdentifyRequest(layer, props) { - if (layer.type === 'wms') { - return MapInfoUtils.buildIdentifyWMSRequest(layer, props); - } - if (layer.type === 'vector') { - return MapInfoUtils.buildIdentifyVectorRequest(layer, props); + if (MapInfoUtils.services[layer.type]) { + return MapInfoUtils.services[layer.type].buildRequest(layer, props); } return {}; }, @@ -238,10 +121,15 @@ const MapInfoUtils = { }, defaultQueryableFilter(l) { return l.visibility && - (l.type === 'wms' || l.type === 'vector') && + (MapInfoUtils.services[l.type]) && (l.queryable === undefined || l.queryable) && l.group !== "background" ; + }, + services: { + wms: require('./mapinfo/wms'), + wmts: require('./mapinfo/wmts'), + vector: require('./mapinfo/vector') } }; diff --git a/web/client/utils/WMTSUtils.js b/web/client/utils/WMTSUtils.js index b4c0f6d130..a83a37b721 100644 --- a/web/client/utils/WMTSUtils.js +++ b/web/client/utils/WMTSUtils.js @@ -8,9 +8,29 @@ const CoordinatesUtils = require('./CoordinatesUtils'); -const {isString, isArray, isObject, head} = require('lodash'); +const {isString, isArray, isObject, head, slice} = require('lodash'); const WMTSUtils = { + getDefaultMatrixId: (options) => { + let matrixIds = new Array(30); + for (let z = 0; z < 30; ++z) { + // generate matrixIds arrays for this WMTS + matrixIds[z] = options.tileMatrixPrefix + z; + } + return matrixIds; + }, + getMatrixIds: (matrix, srs) => { + return (isObject(matrix) && matrix[srs] || matrix).map((el) => el.identifier); + }, + limitMatrix: (matrix, len) => { + if (matrix.length > len) { + return slice(matrix, 0, len); + } + if (matrix.length < len) { + return matrix.concat(new Array(len - matrix.length)); + } + return matrix; + }, getTileMatrixSet: (tileMatrixSet, srs, allowedSRS, matrixIds = {}) => { if (tileMatrixSet && isString(tileMatrixSet)) { return tileMatrixSet; diff --git a/web/client/utils/__tests__/CoordinatesUtils-test.js b/web/client/utils/__tests__/CoordinatesUtils-test.js index 02a498fa48..adcea1d4b6 100644 --- a/web/client/utils/__tests__/CoordinatesUtils-test.js +++ b/web/client/utils/__tests__/CoordinatesUtils-test.js @@ -29,6 +29,17 @@ describe('CoordinatesUtils', () => { expect(transformed.y).toNotBe(13); expect(transformed.srs).toBe('EPSG:900913'); }); + it('it should tests the creation of a bbox given the center, resolution and size', () => { + let center = {x: 0, y: 0}; + let resolution = 1; + let rotation = 0; + let size = [10, 10]; + let bbox = CoordinatesUtils.getProjectedBBox(center, resolution, rotation, size); + expect(bbox).toExist(); + expect(bbox.maxx).toBeGreaterThan(bbox.minx); + expect(bbox.maxy).toBeGreaterThan(bbox.miny); + }); + it('convert lat lon bbox to marcator bbox', () => { var bbox = [44, 12, 45, 13]; var projbbox = CoordinatesUtils.reprojectBbox(bbox, 'EPSG:4326', 'EPSG:900913'); diff --git a/web/client/utils/__tests__/MapInfoUtils-test.js b/web/client/utils/__tests__/MapInfoUtils-test.js index a68070f4b8..1c04455584 100644 --- a/web/client/utils/__tests__/MapInfoUtils-test.js +++ b/web/client/utils/__tests__/MapInfoUtils-test.js @@ -12,11 +12,11 @@ var { getAvailableInfoFormatLabels, getAvailableInfoFormatValues, getDefaultInfoFormatValue, - createBBox, - getProjectedBBox, - buildIdentifyWMSRequest + buildIdentifyRequest } = require('../MapInfoUtils'); +const CoordinatesUtils = require('../CoordinatesUtils'); + describe('MapInfoUtils', () => { it('getAvailableInfoFormat', () => { @@ -66,23 +66,11 @@ describe('MapInfoUtils', () => { }); it('it should returns a bbox', () => { - let results = createBBox(-10, 10, -10, 10); + let results = CoordinatesUtils.createBBox(-10, 10, -10, 10); expect(results).toExist(); expect(results.maxx).toBe(-10); }); - - it('it should tests the creation of a bbox given the center, resolution and size', () => { - let center = {x: 0, y: 0}; - let resolution = 1; - let rotation = 0; - let size = [10, 10]; - let bbox = getProjectedBBox(center, resolution, rotation, size); - expect(bbox).toExist(); - expect(bbox.maxx).toBeGreaterThan(bbox.minx); - expect(bbox.maxy).toBeGreaterThan(bbox.miny); - }); - - it('buildIdentifyWMSRequest should honour queryLayers', () => { + it('buildIdentifyRequest should honour queryLayers', () => { let props = { map: { zoom: 0, @@ -101,7 +89,7 @@ describe('MapInfoUtils', () => { name: "layer", url: "http://localhost" }; - let req1 = buildIdentifyWMSRequest(layer1, props); + let req1 = buildIdentifyRequest(layer1, props); expect(req1.request).toExist(); expect(req1.request.query_layers).toEqual("sublayer1,sublayer2"); @@ -110,7 +98,7 @@ describe('MapInfoUtils', () => { name: "layer", url: "http://localhost" }; - let req2 = buildIdentifyWMSRequest(layer2, props); + let req2 = buildIdentifyRequest(layer2, props); expect(req2.request).toExist(); expect(req2.request.query_layers).toEqual("layer"); @@ -120,8 +108,78 @@ describe('MapInfoUtils', () => { queryLayers: [], url: "http://localhost" }; - let req3 = buildIdentifyWMSRequest(layer3, props); + let req3 = buildIdentifyRequest(layer3, props); expect(req3.request).toExist(); expect(req3.request.query_layers).toEqual(""); }); + + it('buildIdentifyRequest works for wms layer', () => { + let props = { + map: { + zoom: 0, + projection: 'EPSG:4326' + }, + point: { + latlng: { + lat: 0, + lng: 0 + } + } + }; + let layer1 = { + type: "wms", + queryLayers: ["sublayer1", "sublayer2"], + name: "layer", + url: "http://localhost" + }; + let req1 = buildIdentifyRequest(layer1, props); + expect(req1.request).toExist(); + expect(req1.request.service).toBe('WMS'); + }); + + it('buildIdentifyRequest works for wmts layer', () => { + let props = { + map: { + zoom: 0, + projection: 'EPSG:4326' + }, + point: { + latlng: { + lat: 0, + lng: 0 + } + } + }; + let layer1 = { + type: "wmts", + name: "layer", + url: "http://localhost" + }; + let req1 = buildIdentifyRequest(layer1, props); + expect(req1.request).toExist(); + expect(req1.request.service).toBe('WMTS'); + }); + + it('buildIdentifyRequest works for vector layer', () => { + let props = { + map: { + zoom: 0, + projection: 'EPSG:4326' + }, + point: { + latlng: { + lat: 43, + lng: 0 + } + } + }; + let layer1 = { + type: "vector", + name: "layer", + features: [{properties: {}}] + }; + let req1 = buildIdentifyRequest(layer1, props); + expect(req1.request).toExist(); + expect(req1.request.lat).toBe(43); + }); }); diff --git a/web/client/utils/mapinfo/vector.js b/web/client/utils/mapinfo/vector.js new file mode 100644 index 0000000000..78b28357a7 --- /dev/null +++ b/web/client/utils/mapinfo/vector.js @@ -0,0 +1,28 @@ +/** + * Copyright 2017, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +const MapUtils = require('../MapUtils'); + +module.exports = { + buildRequest: (layer, props) => { + return { + request: { + lat: props.point.latlng.lat, + lng: props.point.latlng.lng + }, + metadata: { + fields: Object.keys(layer.features[0].properties), + title: layer.name, + resolution: props.map && props.map && props.map.zoom && MapUtils.getCurrentResolution(props.map.zoom, 0, 21, 96), + buffer: props.buffer, + units: props.map && props.map.units + }, + url: "" + }; + } +}; diff --git a/web/client/utils/mapinfo/wms.js b/web/client/utils/mapinfo/wms.js new file mode 100644 index 0000000000..205c8647f3 --- /dev/null +++ b/web/client/utils/mapinfo/wms.js @@ -0,0 +1,69 @@ +/** + * Copyright 2017, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +const MapUtils = require('../MapUtils'); +const CoordinatesUtils = require('../CoordinatesUtils'); +const {isArray} = require('lodash'); + +const assign = require('object-assign'); + +module.exports = { + buildRequest: (layer, props) => { + /* In order to create a valid feature info request + * we create a bbox of 101x101 pixel that wrap the point. + * center point is repojected then is built a box of 101x101pixel around it + */ + const heightBBox = (props && props.sizeBBox && props.sizeBBox.height) || 101; + const widthBBox = (props && props.sizeBBox && props.sizeBBox.width) || 101; + const size = [heightBBox, widthBBox]; + const rotation = 0; + const resolution = MapUtils.getCurrentResolution(Math.ceil(props.map.zoom), 0, 21, 96); + let wrongLng = props.point.latlng.lng; + // longitude restricted to the [-180°,+180°] range + let lngCorrected = wrongLng - (360) * Math.floor(wrongLng / (360) + 0.5); + const center = {x: lngCorrected, y: props.point.latlng.lat}; + let centerProjected = CoordinatesUtils.reproject(center, 'EPSG:4326', props.map.projection); + let bounds = CoordinatesUtils.getProjectedBBox(centerProjected, resolution, rotation, size, null); + let queryLayers = layer.name; + if (layer.queryLayers) { + queryLayers = layer.queryLayers.join(","); + } + + return { + request: { + service: 'WMS', + version: '1.1.1', + request: 'GetFeatureInfo', + exceptions: 'application/json', + id: layer.id, + layers: layer.name, + query_layers: queryLayers, + styles: layer.style, + x: ((widthBBox % 2) === 1) ? Math.ceil(widthBBox / 2) : widthBBox / 2, + y: ((widthBBox % 2) === 1) ? Math.ceil(widthBBox / 2) : widthBBox / 2, + height: heightBBox, + width: widthBBox, + srs: CoordinatesUtils.normalizeSRS(props.map.projection) || 'EPSG:4326', + bbox: bounds.minx + "," + + bounds.miny + "," + + bounds.maxx + "," + + bounds.maxy, + feature_count: props.maxItems, + info_format: props.format || 'application/json', + ...assign({}, layer.baseParams, layer.params, props.params) + }, + metadata: { + title: layer.title, + regex: layer.featureInfoRegex + }, + url: isArray(layer.url) ? + layer.url[0] : + layer.url.replace(/[?].*$/g, '') + }; + } +}; diff --git a/web/client/utils/mapinfo/wmts.js b/web/client/utils/mapinfo/wmts.js new file mode 100644 index 0000000000..c1dd812251 --- /dev/null +++ b/web/client/utils/mapinfo/wmts.js @@ -0,0 +1,69 @@ +/** + * Copyright 2017, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +const MapUtils = require('../MapUtils'); +const CoordinatesUtils = require('../CoordinatesUtils'); +const WMTSUtils = require('../WMTSUtils'); + +const {isArray} = require('lodash'); + +const assign = require('object-assign'); + +module.exports = { + buildRequest: (layer, props) => { + const resolution = MapUtils.getCurrentResolution(Math.ceil(props.map.zoom), 0, 21, 96); + const resolutions = layer.resolutions || MapUtils.getResolutions(); + const tileSize = layer.tileSize || 256; // tilegrid.getTileSize(props.map.zoom); + const tileOrigin = [ + layer.originX || -20037508.3428, + layer.originY || 20037508.3428 + ]; + + const wrongLng = props.point.latlng.lng; + // longitude restricted to the [-180°,+180°] range + const lngCorrected = wrongLng - (360) * Math.floor(wrongLng / (360) + 0.5); + const center = {x: lngCorrected, y: props.point.latlng.lat}; + let centerProjected = CoordinatesUtils.reproject(center, 'EPSG:4326', props.map.projection); + + const srs = CoordinatesUtils.normalizeSRS(layer.srs || props.map.projection || 'EPSG:3857', layer.allowedSRS); + const tileMatrixSet = WMTSUtils.getTileMatrixSet(layer.tileMatrixSet, srs, layer.allowedSRS); + + const fx = (centerProjected.x - tileOrigin[0]) / (resolution * tileSize); + const fy = (tileOrigin[1] - centerProjected.y) / (resolution * tileSize); + const tileCol = Math.floor(fx); + const tileRow = Math.floor(fy); + const tileI = Math.floor((fx - tileCol) * tileSize); + const tileJ = Math.floor((fy - tileRow) * tileSize); + + const matrixIds = WMTSUtils.limitMatrix(layer.matrixIds && WMTSUtils.getMatrixIds(layer.matrixIds, tileMatrixSet || srs) || WMTSUtils.getDefaultMatrixId(layer), resolutions.length); + + return { + request: { + service: 'WMTS', + request: 'GetFeatureInfo', + layer: layer.name, + infoformat: props.format, + style: layer.style || '', + ...assign({}, layer.baseParams, layer.params, props.params), + tilecol: tileCol, + tilerow: tileRow, + tilematrix: matrixIds[props.map.zoom], + tilematrixset: tileMatrixSet, + i: tileI, + j: tileJ + }, + metadata: { + title: layer.title, + regex: layer.featureInfoRegex + }, + url: isArray(layer.url) ? + layer.url[0] : + layer.url.replace(/[?].*$/g, '') + }; + } +};