Skip to content

Commit

Permalink
Fixes #1699: identify support for WMTS layers (#1804)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbarto authored May 11, 2017
1 parent 01583b4 commit a99bac6
Show file tree
Hide file tree
Showing 10 changed files with 335 additions and 181 deletions.
20 changes: 5 additions & 15 deletions web/client/actions/mapInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
27 changes: 2 additions & 25 deletions web/client/components/map/openlayers/plugins/WMTSLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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({
Expand Down
44 changes: 44 additions & 0 deletions web/client/utils/CoordinatesUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
128 changes: 8 additions & 120 deletions web/client/utils/MapInfoUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
/**
Expand Down Expand Up @@ -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 {};
},
Expand Down Expand Up @@ -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')
}
};

Expand Down
22 changes: 21 additions & 1 deletion web/client/utils/WMTSUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions web/client/utils/__tests__/CoordinatesUtils-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
Loading

0 comments on commit a99bac6

Please sign in to comment.