Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic support for WMS layers < 1.3.0, improve bbox handling #5266

Open
wants to merge 26 commits into
base: 3.12.x
Choose a base branch
from

Conversation

GeoSander
Copy link
Contributor

@GeoSander GeoSander commented Dec 15, 2020

This PR adds basic support for WMS services prior to version 1.3.0, which are not supported by OpenLayers out-of-the-box.

Parse missing properties

Some GetCapabilities properties of these older WMS services are ignored by the OL parser. This PR uses the xml2json lib to scan for and add the missing properties.

Improve BBOX creation

WMS requests require a valid bounding box. Unfortunately, even though some EPSG codes might be standard, a user could define their own Proj4 definition for it. Because of that, this could result in an invalid BBOX and therefore invalid WMS request.
This PR checks if the Proj4 definition set by the user is a custom one. If it is, it will use the geographic bounds of the WMS layer GetCapabilities and reproject these to a valid BBOX. If it's not custom, it will use the bounds from the GetCapabilities CRS list (if a match is found).

Maintain WMS layers after projection switch

Currently, WMS layers are not reprojected if the user uses the Projection Switcher in the viewer map. They are simply being ignored and re-appear if the user switches back to the original projection.
This PR tries to re-add all WMS layers once the projection has been switched.

if (!(code in proj4.defs) || isDefaultProjection(code)) {
return;
}
return _cachedDefs[code];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want to exhaust EPSG.io, so we keep a dictionary of cached Proj4 definitions

Comment on lines -310 to -329
/**
* @ngdoc method
* @methodOf gn_map.service:gnMap
* @name gnMap#importProj4js
*
* @description
* Import the proj4js projection that are specified in DB config.
*/
importProj4js: function() {
proj4.defs('EPSG:2154', '+proj=lcc +lat_1=49 +lat_2=44 +lat_0' +
'=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +' +
'towgs84=0,0,0,0,0,0,0 +units=m +no_defs');
if (proj4 && angular.isArray(gnConfig['map.proj4js'])) {
angular.forEach(gnConfig['map.proj4js'], function(item) {
proj4.defs(item.code, item.value);
});
}
ol.proj.proj4.register(proj4);
},

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer used anywhere.

@@ -785,7 +808,7 @@
advanced: options.advanced,
minResolution: options.minResolution,
maxResolution: options.maxResolution,
cextent: options.extent,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cextent is only used by GeoNetwork internally, but OpenLayers does not expect this property. Therefore, we replace it by extent instead.

Comment on lines +842 to +852
function(olEvent, target) {
var url = '[no tile URL found]';
if (olEvent.image) {
if (olEvent.image.getUrl) {
url = olEvent.image.getUrl();
} else if (olEvent.image.src_) {
url = olEvent.image.src_;
}
} else if (olEvent.tile && olEvent.tile.getKey) {
url = olEvent.tile.getKey();
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed and improved error handling for WMS ImageLayers

Comment on lines -871 to -894
var isLayerAvailableInMapProjection = false;
// OL3 only parse CRS from WMS 1.3 (and not SRS in WMS 1.1.x)
// so a WMS 1.1.x will always failed on this
// https://github.com/openlayers/ol3/blob/master/src/
// ol/format/wmscapabilitiesformat.js
/*
if (layer.CRS) {
var mapProjection = map.getView().getProjection().getCode();
for (var i = 0; i < layer.CRS.length; i++) {
if (layer.CRS[i] === mapProjection) {
isLayerAvailableInMapProjection = true;
break;
}
}
} else {
errors.push($translate.instant('layerCRSNotFound'));
console.warn($translate.instant('layerCRSNotFound'));
}
if (!isLayerAvailableInMapProjection) {
errors.push($translate.instant('layerNotAvailableInMapProj'));
console.warn($translate.instant('layerNotAvailableInMapProj'));
}
*/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This no longer applies.

Comment on lines 947 to 963
var bboxProps = gnOwsCapabilities.getWmsLayerExtentFromGetCap(map, getCapLayer, isCustomProj4);
var mapProj = map.getView().getProjection();
var projCode = bboxProps.epsg;
if (bboxProps.extent && projCode === 'EPSG:4326') {
// If the GetCap CRS list does not include the current map CRS,
// and an extent is returned from GetCap, it must be a WGS84 extent.
// Explicitly set the BBOX layer parameter in this case.
layerParams.BBOX = bboxProps.extent;
} else if (!bboxProps.extent) {
// If no valid extent could be determined, the WMS does not support the projection
// or the WGS84 bounds do not make sense (when reprojecting into a custom projection)
var msg = $translate.instant('layerNotAvailableInMapProj',{proj:projCode});
if (isCustomProj4) {
msg = $translate.instant('layerWmsInvalidExtent',{proj:projCode});
}
errors.push(msg);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve extent/BBOX handling for WMS layers and supply user with more useful error messages when something is (still) not right.

@josegar74 josegar74 requested a review from jahow December 15, 2020 15:46
Comment on lines 156 to 160
var proj = gnMap.getMapConfig().projection;
if (type === this.VIEWER_MAP) {
gnMap.checkProj4Def(proj);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whenever the viewer map is created, check if the Proj4 definition string is "standard" or custom.

layer.url = url;
layer.CRS = inheritedCrs;
layer.EX_GeographicBoundingBox = layer.EX_GeographicBoundingBox || bbox;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also inherit the geographic bounding box if the "child" does not have that property.

@@ -271,7 +341,7 @@
})
.success(function(data) {
try {
defer.resolve(displayFileContent(data, withGroupLayer, url));
defer.resolve(parseWMSCapabilities(data, withGroupLayer, url));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to be more in line with the other function names.

@@ -390,59 +460,69 @@
return defer.promise;
},

getLayerExtentFromGetCap: function(map, getCapLayer) {
getWmsLayerExtentFromGetCap: function(map, getCapLayer, isCustomDef) {
Copy link
Contributor Author

@GeoSander GeoSander Dec 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this function is now being used for WMS layers exclusively, its name should reflect that.
Also added the isCustomDef parameter.


getLayerInfoFromCap: function(layerName, capObj, uuid) {
var needles = [];
var layers = capObj.layers || capObj.Layer;

// Layer name may be a list of comma separated layers
layerList = layerName.split(',');
layersLoop:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused label

Comment on lines 115 to 123
var proj = map.getView().getProjection();
var extent = proj.getExtent();
if (gnViewerSettings.initialExtent && gnViewerSettings.mapConfig.projection &&
gnViewerSettings.mapConfig.projection !== proj.getCode()) {
// Use reprojected initial extent if it is defined
extent = ol.proj.transformExtent(
gnViewerSettings.initialExtent, gnViewerSettings.mapConfig.projection, proj, 8)
}
map.getView().fit(extent, map.getSize());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If an initial extent was defined in the viewer settings and its projection is different from the current map projection, use and reproject the initial extent. This makes more sense when switching to EPSG:3857 for instance, where the projection extent usually encapsulates the whole world.

@@ -69,6 +69,8 @@
this.changeLayerProjection(subLayer, oldProj,
newProj);
});
} else if (layer instanceof ol.layer.Image) {
gnMap.updateWmsLayerForProj(scope.map, layer, newProj);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also update and show WMS layers whenever the projection changes (they were ignored until now).

Comment on lines -111 to -113
if (newProj.getExtent()) {
newExtent = newProj.getExtent();
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes more sense to stay at the same place and zoom level.

msg: $translate.instant('layerAdded',
{
layer: layer.get('label'),
extent: layer.get('extent') ? layer.get('extent').toString() : null
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would throw an error when (c)extent was null.

}

var style = layer.getSource().getParams().STYLES;
$this.addWmsToMapFromCap(map, lyr, style);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the OpenLayers examples and docs, it should be enough to call layer.getSource().updateParams() to redraw the WMS using the new projection. Unfortunately, that didn't seem to do anything, which is why we're removing and re-adding the layer here.

Comment on lines -1797 to -1798
cextent: gnOwsCapabilities.getLayerExtentFromGetCap(map,
getCapLayer)
Copy link
Contributor Author

@GeoSander GeoSander Dec 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling the getLayerExtentFromGetCap() function always returns null for WMTS layers from what was observed, so it makes no sense to call it at all.
Note: WMTS layers do have an ows:BoundingBox property sometimes, but this is a deprecated element and it only exists inside TileMatrixSet elements, which are not being fully parsed by OL.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants