diff --git a/.gitignore b/.gitignore index 2dde7225cb6..2af53a5afad 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.iml *.launch *.old +*.log .DS_Store .project git*.properties @@ -26,7 +27,6 @@ docs/schema-loc-*.rst eclipse/ es/elasticsearch-* es/es-dashboards/kibana-* -harvesters/harvester_*.log idea/ jcs_caching/ node_modules diff --git a/web-ui/src/main/resources/catalog/components/admin/uiconfig/ProjectionService.js b/web-ui/src/main/resources/catalog/components/admin/uiconfig/ProjectionService.js index cbd3dfcaac2..733f4c3eaaa 100644 --- a/web-ui/src/main/resources/catalog/components/admin/uiconfig/ProjectionService.js +++ b/web-ui/src/main/resources/catalog/components/admin/uiconfig/ProjectionService.js @@ -35,9 +35,9 @@ var EPSG_REGEX = new RegExp('^EPSG:\\d{4,6}$'); module.provider('gnProjService', function() { - this.$get = ['$http', '$q', '$translate', 'gnUrlUtils', + this.$get = ['$http', '$q', '$translate', 'gnUrlUtils', function($http, $q, $translate, gnUrlUtils) { - + var buildRestUrl = function(searchTerm) { // Add GET query parameters to serviceUrl if (searchTerm.toUpperCase().startsWith('EPSG:')) { @@ -50,6 +50,10 @@ })); }; + var isDefaultProjection = function(code) { + return code === EPSG_LL_WGS84 || code === EPSG_WEB_MERCATOR; + } + var parseProjDef = function(data) { var code = 'EPSG:'; @@ -90,7 +94,7 @@ } // transform world extent to projected extent extent = ol.proj.transformExtent(worldExtent, EPSG_LL_WGS84, code, 8); - } + } return { label: firstResult.name, @@ -108,12 +112,10 @@ // Get API service name without http(s) prefix for display purposes return { value: SERVICE_REST_URL.substr(SERVICE_REST_URL.indexOf('://') + 3) - } + } }, - isDefaultProjection: function(code) { - return code === EPSG_LL_WGS84 || code === EPSG_WEB_MERCATOR; - }, + isDefaultProjection: isDefaultProjection, isValidEpsgCode: function(code) { return code.search(EPSG_REGEX) >= 0; @@ -121,7 +123,7 @@ }, getProjectionSettings: function(searchTerm) { - var defer = $q.defer(); + var defer = $q.defer(); var url = buildRestUrl(searchTerm); // send request and decode result @@ -140,11 +142,11 @@ }) .error(function(data, status) { defer.reject( - $translate.instant( - status === 401 ? 'checkProjectionUrlUnauthorized' : 'checkProjectionUrl', - {url: url, status: status})); + $translate.instant( + status === 401 ? 'checkProjectionUrlUnauthorized' : 'checkProjectionUrl', + {url: url, status: status})); }); - } + } return defer.promise; } }; diff --git a/web-ui/src/main/resources/catalog/components/admin/uiconfig/UiConfigModule.js b/web-ui/src/main/resources/catalog/components/admin/uiconfig/UiConfigModule.js index ec4fc303390..539da3f9ab3 100644 --- a/web-ui/src/main/resources/catalog/components/admin/uiconfig/UiConfigModule.js +++ b/web-ui/src/main/resources/catalog/components/admin/uiconfig/UiConfigModule.js @@ -25,7 +25,7 @@ goog.provide('gn_ui_config'); goog.require('gn_ui_config_directive'); - goog.require('gn_projection_service') + goog.require('gn_projection_service'); angular.module('gn_ui_config', [ 'gn_ui_config_directive', diff --git a/web-ui/src/main/resources/catalog/components/common/map/mapService.js b/web-ui/src/main/resources/catalog/components/common/map/mapService.js index 8b5f3ae74f2..e7bcea91acd 100644 --- a/web-ui/src/main/resources/catalog/components/common/map/mapService.js +++ b/web-ui/src/main/resources/catalog/components/common/map/mapService.js @@ -27,12 +27,14 @@ goog.require('gn_ows'); goog.require('gn_wfs_service'); goog.require('gn_esri_service'); + goog.require('gn_projection_service'); var module = angular.module('gn_map_service', [ 'gn_ows', 'gn_wfs_service', - 'gn_esri_service' + 'gn_esri_service', + 'gn_projection_service' ]); /** @@ -66,11 +68,12 @@ 'gnAlertService', '$http', 'gnEsriUtils', + 'gnProjService', function(olDecorateLayer, gnOwsCapabilities, gnConfig, $log, gnSearchLocation, $rootScope, gnUrlUtils, $q, $translate, gnWmsQueue, gnSearchManagerService, Metadata, gnWfsService, gnGlobalSettings, gnViewerSettings, gnViewerService, gnAlertService, - $http, gnEsriUtils) { + $http, gnEsriUtils, gnProjService) { /** * @description @@ -307,26 +310,6 @@ return this.createLayerFromProperties({ type: 'osm' }); }, - /** - * @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); - }, - /** * @ngdoc method * @methodOf gn_map.service:gnMap @@ -351,6 +334,58 @@ } }, + /** + * @ngdoc method + * @methodOf gn_map.service:gnMap + * @name gnMap#getCorrectAxisOrder + * + * @description + * Returns the correct axis orientation code if the given projection + * has a missing or standard axis orientation "enu" (east/north/up), + * even though it should have "neu" (north/east/up) instead, for example. + * This especially applies to transverse Mercator projections, + * like all UTM-based projections (e.g. EPSG:3006). + * Returns null if the projection already has a correct orientation. + * + * @param {ol.proj.ProjectionLike} proj for which to check the axis order + * + * @return {string} axis orientation code + */ + getCorrectAxisOrder: function(proj) { + if (typeof proj.getCode === 'function') { + proj = proj.getCode(); + } + var proj4Def = proj4.defs[proj]; + if (proj4Def.projName === 'utm' && proj4Def.axis !== 'neu') { + // TODO: add more cases in the future (e.g. South-African projections) + return 'neu'; + } + }, + + /** + * @ngdoc method + * @methodOf gn_map.service:gnMap + * @name gnMap#patchOlProjection + * + * @description + * Returns a "patched" copy of the given OL projection if the original + * has a bad or missing axis orientation. + * If the given projection is fine, the original will be returned. + * + * @param {ol.proj.Projection} proj for which to check the axis order + * + * @return {ol.proj.Projection} OL Projection object + */ + patchOlProjection: function(proj) { + var axisCode = this.getCorrectAxisOrder(proj); + if (axisCode) { + var patchedProj = angular.copy(proj); + patchedProj.axisOrientation_ = axisCode; + return patchedProj; + } + return proj; + }, + /** * @ngdoc method * @methodOf gn_map.service:gnMap @@ -771,6 +806,17 @@ }); } + // Determine layer extent (for zoom purposes) + var mapProj = map.getView().getProjection(); + var layerExtent = null; + if (options.extent) { + if (layerOptions.projection === 'EPSG:4326') { + layerExtent = ol.proj.transformExtent(options.extent, layerOptions.projection, mapProj.getCode(), 8); + } else { + layerExtent = options.extent; + } + } + var layerOptions = { url: options.url, type: 'WMS', @@ -785,7 +831,7 @@ advanced: options.advanced, minResolution: options.minResolution, maxResolution: options.maxResolution, - cextent: options.extent, + extent: layerExtent, name: layerParams.LAYERS }; if (gnViewerSettings.singleTileWMS) { @@ -802,7 +848,7 @@ if (!uuid) { var res = new RegExp(/\#\/metadata\/(.*)/g). exec(options.metadata); - if (angular.isArray(res) && res.length == 2) { + if (angular.isArray(res) && res.length === 2) { uuid = res[1]; } } @@ -816,13 +862,21 @@ var unregisterEventKey = olLayer.getSource().on( (gnViewerSettings.singleTileWMS) ? 'imageloaderror' : 'tileloaderror', - function(tileEvent, target) { - var url = tileEvent.tile && tileEvent.tile.getKey ? - tileEvent.tile.getKey() : '- no tile URL found-'; + 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(); + } - var layer = tileEvent.currentTarget && - tileEvent.currentTarget.getParams ? - tileEvent.currentTarget.getParams().LAYERS : + var layer = olEvent.currentTarget && + olEvent.currentTarget.getParams ? + olEvent.currentTarget.getParams().LAYERS : layerParams.LAYERS; var msg = $translate.instant('layerTileLoadError', { @@ -868,30 +922,6 @@ var legend, attribution, attributionUrl, metadata, errors = []; if (getCapLayer) { - 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')); - } - */ - // TODO: parse better legend & attribution var requestedStyle = null; var legendUrl; @@ -930,46 +960,41 @@ metadata = getCapLayer.MetadataURL[0].OnlineResource; } - var layerParam = {LAYERS: getCapLayer.Name}; + var layerParams = {LAYERS: getCapLayer.Name}; if (getCapLayer.version) { - layerParam.VERSION = getCapLayer.version; + layerParams.VERSION = getCapLayer.version; } - layerParam.STYLES = requestedStyle?requestedStyle.Name:''; + layerParams.STYLES = requestedStyle ? requestedStyle.Name : ''; - var projCode = map.getView().getProjection().getCode(); - if (getCapLayer.CRS) { - if (!getCapLayer.CRS.includes(projCode)) { - if (projCode == 'EPSG:3857' && - getCapLayer.CRS.includes('EPSG:900913')) { - } - else if (getCapLayer.CRS.includes('EPSG:4326')) { - projCode = 'EPSG:4326'; - } - } + var mapProj = map.getView().getProjection(); + var bboxProps = gnOwsCapabilities.getWmsLayerExtentFromGetCap( + mapProj, getCapLayer, this.patchOlProjection(mapProj)); + var targetProj = bboxProps.projection; + if (!bboxProps.extent) { + // If no valid extent could be determined, the WMS does not support the projection + var projCode = angular.isDefined(targetProj.getCode) ? targetProj.getCode() : targetProj; + errors.push($translate.instant('layerNotAvailableInMapProj',{proj:projCode})); } url = getCapLayer.url || url; if (url.slice(-1) === '?') { url = url.substring(0, url.length-1); } - var layer = this.createOlWMS(map, layerParam, { + var layer = this.createOlWMS(map, layerParams, { url: url, label: getCapLayer.Title, attribution: attribution, attributionUrl: attributionUrl, - projection: projCode, + projection: targetProj, legend: legend, group: getCapLayer.group, metadata: metadata, - extent: gnOwsCapabilities.getLayerExtentFromGetCap(map, - getCapLayer), + extent: bboxProps.extent, minResolution: this.getResolutionFromScale( - map.getView().getProjection(), - getCapLayer.MinScaleDenominator), + targetProj, getCapLayer.MinScaleDenominator), maxResolution: this.getResolutionFromScale( - map.getView().getProjection(), - getCapLayer.MaxScaleDenominator), + targetProj, getCapLayer.MaxScaleDenominator), useProxy: getCapLayer.useProxy }); @@ -1101,7 +1126,7 @@ } if (!isLayerAvailableInMapProjection) { - gnAlertService.addAlert({ + gnAlertService.addAlert({ msg: $translate.instant('layerNotAvailableInMapProj',{proj:mapProjection}), delay: 5000, type: 'warning'}); @@ -1732,7 +1757,6 @@ */ createOlWMTSFromCap: function(map, getCapLayer, capabilities) { - var legend, attribution, metadata; if (getCapLayer && capabilities) { //Asking WMTS service about capabilities @@ -1748,12 +1772,19 @@ var options; + // Patch projection before the call to OL's optionsFromCapabilities() + // if needed (if axis orientation is incorrect/missing). + var mapProj = this.patchOlProjection(map.getView().getProjection()); + try { options = ol.source.WMTS.optionsFromCapabilities(cap, { layer: getCapLayer.Identifier, - matrixSet: map.getView().getProjection().getCode(), - projection: map.getView().getProjection().getCode() + projection: mapProj }); + if (!options || !options.matrixSet || + !proj4.defs.hasOwnProperty(options.projection.getCode())) { + throw Error('No suitable matrixset found in GetCapabilities'); + } } catch (e) { gnAlertService.addAlert({ msg: $translate.instant('wmtsLayerNoUsableMatrixSet'), @@ -1763,6 +1794,22 @@ return; } + // Fix tileGrid origins for projections without reverse axis order (e.g. UTM): + // For some reason, OL *does* create a rotated tileGrid extent, but the origins + // remain inverted... + var tileExtent = options.tileGrid.extent_; + if (this.getCorrectAxisOrder(map.getView().getProjection()) === 'neu') { + var topLeft = ol.extent.getTopLeft(tileExtent); + if (options.tileGrid.origin_) { + options.tileGrid.origin_ = topLeft; + } + if (angular.isArray(options.tileGrid.origins_)) { + options.tileGrid.origins_.forEach(function (item, index, array) { + array[index] = topLeft; + }); + } + } + //Configuring url for service var url = capabilities.operationsMetadata.GetCapabilities.DCP.HTTP.Get[0].href; @@ -1771,7 +1818,7 @@ var urlCapType = capabilities.operationsMetadata.GetCapabilities. DCP.HTTP.Get[0].Constraint[0].AllowedValues.Value[0].toLowerCase(); - if (urlCapType == 'restful') { + if (urlCapType === 'restful') { if (urlCap.indexOf('/1.0.0/WMTSCapabilities.xml') == -1) { urlCap = urlCap + '/1.0.0/WMTSCapabilities.xml'; } @@ -1785,17 +1832,15 @@ version: '1.0.0'})); } - //Create layer + // Create layer var olLayer = new ol.layer.Tile({ - extent: map.getView().getProjection().getExtent(), name: getCapLayer.Identifier, title: getCapLayer.Title, label: getCapLayer.Title, source: new ol.source.WMTS(options), url: url, urlCap: urlCap, - cextent: gnOwsCapabilities.getLayerExtentFromGetCap(map, - getCapLayer) + extent: tileExtent }); //Add GN extras to layer @@ -1813,7 +1858,7 @@ if (!uuid) { var res = new RegExp(/\#\/metadata\/(.*)/g). exec(metadata); - if (angular.isArray(res) && res.length == 2) { + if (angular.isArray(res) && res.length === 2) { uuid = res[1]; } } @@ -1873,15 +1918,15 @@ * * @description * Zoom map to the layer extent if defined. The layer extent - * is gotten from capabilities and store in cextent property + * is gotten from capabilities and store in the extent property * of the layer. * * @param {ol.Layer} layer for the extent * @param {ol.map} map obj */ zoomLayerToExtent: function(layer, map) { - if (layer.get('cextent')) { - map.getView().fit(layer.get('cextent'), map.getSize()); + if (layer.get('extent')) { + map.getView().fit(layer.get('extent'), map.getSize()); } }, diff --git a/web-ui/src/main/resources/catalog/components/common/map/mapsManager.js b/web-ui/src/main/resources/catalog/components/common/map/mapsManager.js index 96fda5aa45c..159e509ec90 100644 --- a/web-ui/src/main/resources/catalog/components/common/map/mapsManager.js +++ b/web-ui/src/main/resources/catalog/components/common/map/mapsManager.js @@ -152,11 +152,13 @@ */ createMap: function(type) { var config = gnMap.getMapConfig()['map-' + type]; + + var proj = gnMap.getMapConfig().projection; var map = new ol.Map({ layers: [], view: new ol.View({ center: [0, 0], - projection: gnMap.getMapConfig().projection, + projection: proj, zoom: 2 }), // show zoom control in editor maps only diff --git a/web-ui/src/main/resources/catalog/components/common/ows/OWSService.js b/web-ui/src/main/resources/catalog/components/common/ows/OWSService.js index f149cd6a33e..352d10bf2ec 100644 --- a/web-ui/src/main/resources/catalog/components/common/ows/OWSService.js +++ b/web-ui/src/main/resources/catalog/components/common/ows/OWSService.js @@ -94,19 +94,87 @@ 'gnUrlUtils', 'gnGlobalSettings', function($http, $q, $translate, gnUrlUtils, gnGlobalSettings) { - var displayFileContent = function(data, withGroupLayer, getCapabilitiesUrl) { + + var getLayerBounds = function(layer) { + var bboxProp; + ['EX_GeographicBoundingBox', 'WGS84BoundingBox'].forEach( + function(prop) { + if (angular.isArray(layer[prop])) { + bboxProp = layer[prop]; + } + }); + return bboxProp; + }; + + var getWMSCapabilities = function(data) { + var ol_parser = new ol.format.WMSCapabilities(); + var result = ol_parser.read(data); + var ol_layer = result.Capability.Layer; + + // If no CRS property is available, we're probably dealing with an older WMS (e.g. 1.1.1) + // WMS versions prior to 1.3.0 are not (fully) supported by OL's WMSCapabilities parser + if (!result.Capability.Layer.CRS) { + var parser = new X2JS(); + var jsonData = parser.xml_str2json(data); + + for (var p in jsonData) { + // Get root element + if (jsonData.hasOwnProperty(p)) { + var layer = jsonData[p].Capability.Layer; + + // Copy SRS to Layer.CRS + ol_layer.CRS = layer.SRS || layer._SRS; + + // Copy _SRS to BoundingBox.crs for each BoundingBox (if equal array length) + if (layer.BoundingBox && layer.BoundingBox.length > 0 && + layer.BoundingBox.length === ol_layer.BoundingBox.length) { + + for (var i = 0; i < layer.BoundingBox.length; i++) { + var bbox = layer.BoundingBox[i]; + ol_layer.BoundingBox[i].crs = bbox._SRS || bbox.SRS; + } + } + + // Create EX_GeographicBoundingBox extent from LatLonBoundingBox properties + if (layer.LatLonBoundingBox) { + var minx = layer.LatLonBoundingBox._minx; + var miny = layer.LatLonBoundingBox._miny; + var maxx = layer.LatLonBoundingBox._maxx; + var maxy = layer.LatLonBoundingBox._maxy; + if (angular.isDefined(minx) && angular.isDefined(miny) && + angular.isDefined(maxx) && angular.isDefined(maxy)) { + ol_layer.EX_GeographicBoundingBox = ol.extent.boundingExtent([ + [parseFloat(minx), parseFloat(miny)], + [parseFloat(maxx), parseFloat(maxy)] + ]); + } + } + break; + } + } + } + + return result; + }; + + var parseWMSCapabilities = function(data, withGroupLayer, getCapabilitiesUrl) { + + var resolvedUrl = gnUrlUtils.urlResolve(getCapabilitiesUrl); + resolvedUrl.port = ''; + getCapabilitiesUrl = resolvedUrl.href; + if (!cachedGetCapabilitiesUrls.hasOwnProperty(getCapabilitiesUrl)) { - var parser = new ol.format.WMSCapabilities(); - cachedGetCapabilitiesUrls[getCapabilitiesUrl] = parser.read(data); + cachedGetCapabilitiesUrls[getCapabilitiesUrl] = getWMSCapabilities(data); } + var result = angular.copy(cachedGetCapabilitiesUrls[getCapabilitiesUrl], {}); var layers = []; var url = result.Capability.Request.GetMap. DCPType[0].HTTP.Get.OnlineResource; - + var bbox = getLayerBounds(result.Capability.Layer); // Push all leaves into a flat array of Layers - // Also adjust crs (by inheritance) and url + // Also adjust CRS (by inheritance) and set URL and geographic bounds var getFlatLayers = function(layer, inheritedCrs) { if (angular.isArray(layer)) { if (withGroupLayer && layer.Name) { @@ -116,14 +184,16 @@ getFlatLayers(layer[i], inheritedCrs); } } else if (angular.isDefined(layer)) { + // replace with complete CRS list if available if (layer.CRS && layer.CRS.length > inheritedCrs.length) { inheritedCrs = layer.CRS; } - // add to flat layer array if we're on a leave (layer w/o child) + // add to flat layer array if we're on a leaf (layer w/o child) layer.url = url; layer.CRS = inheritedCrs; + layer.EX_GeographicBoundingBox = layer.EX_GeographicBoundingBox || bbox; layers.push(layer); // make sure Layer element is an array @@ -242,7 +312,7 @@ for (var p in urlParams) { defaultParams[p] = urlParams[p]; if (defaultParams.hasOwnProperty(p.toLowerCase()) && - p != p.toLowerCase()) { + p != p.toLowerCase()) { delete defaultParams[p.toLowerCase()]; } } @@ -271,7 +341,7 @@ }) .success(function(data) { try { - defer.resolve(displayFileContent(data, withGroupLayer, url)); + defer.resolve(parseWMSCapabilities(data, withGroupLayer, url)); } catch (e) { defer.reject( $translate.instant('failedToParseCapabilities')); @@ -390,51 +460,50 @@ return defer.promise; }, - getLayerExtentFromGetCap: function(map, getCapLayer) { + getWmsLayerExtentFromGetCap: function(mapProj, getCapLayer, patchedProj) { var extent = null; var layer = getCapLayer; - var proj = map.getView().getProjection(); - - //var ext = layer.BoundingBox[0].extent; - //var olExtent = [ext[1],ext[0],ext[3],ext[2]]; - // TODO fix using layer.BoundingBox[0].extent - // when sextant fix his capabilities - - var bboxProp; - ['EX_GeographicBoundingBox', 'WGS84BoundingBox'].forEach( - function(prop) { - if (angular.isArray(layer[prop])) { - bboxProp = layer[prop]; - } - }); - - if (bboxProp) { - extent = proj.getWorldExtent() && ol.extent.containsExtent(proj.getWorldExtent(), - bboxProp) ? - ol.proj.transformExtent(bboxProp, 'EPSG:4326', proj) : - proj.getExtent(); - } else if (angular.isArray(layer.BoundingBox)) { + var projCode = mapProj.getCode(); + var wmsProj = projCode; + + // Try and fetch extent from layer for current CRS first + if (angular.isArray(layer.BoundingBox)) { for (var i = 0; i < layer.BoundingBox.length; i++) { var bbox = layer.BoundingBox[i]; - // Use the bbox with the code matching the map projection - // or the first one. - if (bbox.crs === proj.getCode() || - layer.BoundingBox.length === 1) { - - extent = - ol.extent.containsExtent( - proj.getWorldExtent(), - bbox.extent) ? - ol.proj.transformExtent(bbox.extent, - bbox.crs || 'EPSG:4326', proj) : - proj.getExtent(); - break; + + if (!bbox.extent) { + continue; } + + if (bbox.crs === projCode || (projCode === 'EPSG:3857' && + (bbox.crs === 'EPSG:3857' || bbox.crs === 'EPSG:900913' || + bbox.crs === 'EPSG:3785' || bbox.crs === 'EPSG:102113'))) { + // Get the bbox matching the map projection + // Also match to Web Mercator alternatives + extent = bbox.extent; + wmsProj = bbox.crs; + } } } - return extent; - }, + if (extent) { + if (layer.version && layer.version >= '1.3.0' && projCode !== 'EPSG:3857') { + // Reverse extent coordinates for non-Web Mercator projections and new WMS versions + extent = extent.reverse(); + } + // Reproject to (patched) map extent + extent = ol.proj.transformExtent(extent, patchedProj, wmsProj, 8); + if (projCode !== 'EPSG:3857') { + // Output (patched) map extent for WMS on-the-fly reprojection + wmsProj = patchedProj; + } + } + + return { + projection: wmsProj, + extent: extent + }; + }, getLayerInfoFromCap: function(layerName, capObj, uuid) { var needles = []; @@ -442,7 +511,7 @@ // Layer name may be a list of comma separated layers layerList = layerName.split(','); - layersLoop: + for (var j = 0; j < layerList.length; j ++) { var name = layerList[j]; //non namespaced lowercase name @@ -522,7 +591,6 @@ } }, - getLayerInfoFromWfsCap: function(name, capObj, uuid) { var needles = []; var layers = capObj.featureTypeList.featureType; diff --git a/web-ui/src/main/resources/catalog/components/viewer/ViewerDirective.js b/web-ui/src/main/resources/catalog/components/viewer/ViewerDirective.js index 20965c5669c..19492d2cf7e 100644 --- a/web-ui/src/main/resources/catalog/components/viewer/ViewerDirective.js +++ b/web-ui/src/main/resources/catalog/components/viewer/ViewerDirective.js @@ -99,7 +99,7 @@ if(gnViewerSettings.mapConfig.switcherProjectionList.length < 2) { scope.disabledTools.projectionSwitcher = true; } - + /** wps process tabs */ scope.wpsTabs = { byUrl: true, @@ -110,9 +110,10 @@ scope.zoom = function(map, delta) { gnMap.zoom(map, delta); }; + scope.zoomToMaxExtent = function(map) { - map.getView().fit(map.getView(). - getProjection().getExtent(), map.getSize()); + var extent = map.getView().getProjection().getExtent(); + map.getView().fit(extent, map.getSize()); }; scope.ol3d = null; @@ -192,7 +193,7 @@ if (layer) { gnMap.feedLayerWithRelated(layer, config.group); - var extent = layer.get('cextent') || layer.get('extent'); + var extent = layer.get('extent'); var autofit = gnViewerSettings.mapConfig.autoFitOnLayer; var message = extent && !autofit ? 'layerAdded' : 'layerAddedNoExtent'; @@ -236,7 +237,7 @@ var layer = gnMap.getLayerInMap(scope.map, config.name, config.url); if (layer !== null) { - var extent = layer.get('cextent') || layer.get('extent'); + var extent = layer.get('extent'); gnAlertService.addAlert({ msg: $translate.instant('layerIsAlreadyInMap', { layer: config.name, diff --git a/web-ui/src/main/resources/catalog/components/viewer/gfi/FeaturesLoader.js b/web-ui/src/main/resources/catalog/components/viewer/gfi/FeaturesLoader.js index bf157ae1ee2..66166ec1061 100644 --- a/web-ui/src/main/resources/catalog/components/viewer/gfi/FeaturesLoader.js +++ b/web-ui/src/main/resources/catalog/components/viewer/gfi/FeaturesLoader.js @@ -147,7 +147,9 @@ } }).then(function(response) { - if(infoFormat && + if (!response.data) { + this.features = []; + } else if(infoFormat && (infoFormat.toLowerCase().localeCompare('application/json') == 0 || infoFormat.toLowerCase().localeCompare('application/geojson') == 0 )) { var jsonf = new ol.format.GeoJSON(); diff --git a/web-ui/src/main/resources/catalog/components/viewer/layermanager/LayerManagerDirective.js b/web-ui/src/main/resources/catalog/components/viewer/layermanager/LayerManagerDirective.js index 2440995f6c2..7b631d589cf 100644 --- a/web-ui/src/main/resources/catalog/components/viewer/layermanager/LayerManagerDirective.js +++ b/web-ui/src/main/resources/catalog/components/viewer/layermanager/LayerManagerDirective.js @@ -174,9 +174,7 @@ layer.set('currentStyle', style); }; scope.zoomToExtent = function(layer, map) { - if (layer.get('cextent')) { - map.getView().fit(layer.get('cextent'), map.getSize()); - } else if (layer.get('extent')) { + if (layer.get('extent')) { map.getView().fit(layer.get('extent'), map.getSize()); } }; diff --git a/web-ui/src/main/resources/catalog/components/viewer/owscontext/OwsContextService.js b/web-ui/src/main/resources/catalog/components/viewer/owscontext/OwsContextService.js index 25547f6372d..d254e6a29f2 100644 --- a/web-ui/src/main/resources/catalog/components/viewer/owscontext/OwsContextService.js +++ b/web-ui/src/main/resources/catalog/components/viewer/owscontext/OwsContextService.js @@ -137,7 +137,6 @@ } var extent = ll.concat(ur); - gnViewerSettings.initialExtent = extent; // save this extent for later use (for example if the map // is not currently visible) diff --git a/web-ui/src/main/resources/catalog/components/viewer/projectionSwitcher/ProjectionSwitcher.js b/web-ui/src/main/resources/catalog/components/viewer/projectionSwitcher/ProjectionSwitcher.js index 7cc7aaa12a1..88fb365f4c7 100644 --- a/web-ui/src/main/resources/catalog/components/viewer/projectionSwitcher/ProjectionSwitcher.js +++ b/web-ui/src/main/resources/catalog/components/viewer/projectionSwitcher/ProjectionSwitcher.js @@ -60,24 +60,57 @@ scope.listOpen = !scope.listOpen; } - // Change the projection of an existing layer - scope.changeLayerProjection = function (layer, oldProj, - newProj) { - if (layer instanceof ol.layer.Group) { - layer.getLayers().forEach( - function (subLayer) { - this.changeLayerProjection(subLayer, oldProj, - newProj); - }); - } else if (layer instanceof ol.layer.Tile) { - var tileLoadFunc = layer.getSource() - .getTileLoadFunction(); - layer.getSource().setTileLoadFunction(tileLoadFunc); - } else if (layer instanceof ol.layer.Vector) { - var features = layer.getSource().getFeatures(); - for (var i = 0; i < features.length; i += 1) { - features[i].getGeometry().transform(oldProj, - newProj); + // Function to fully reload WM(T)S layers + // Simply calling the tile/imageLoadFunction does not always work well + scope.reloadOwsLayer = function(map, layer) { + var layerUrl = layer.get('url'); + var layerName = layer.get('name'); + var layerType = layer.get('type') ? layer.get('type').toLowerCase() : ''; + + if (!layerType) { + // Try and extract layer type from GetCapabilities URL if type was not set + var getCapUrl = layer.get('urlCap'); + if (getCapUrl) { + var match = getCapUrl.match(/service=(wm[t]?s)/i); + if (match && match[1]) { + layerType = match[1].toLowerCase(); + } + } + } + + if (!layerUrl || !layerName || !layerType.startsWith('wm')) { + // Return if properties are missing or we're not dealing with a WM(T)S + return; + } + + // Reload the layer (with potentially updated extent, projection, matrixSet etc.) + var promise = null; + if (layerType === 'wms') { + promise = gnMap.addWmsFromScratch(map, layerUrl, layerName); + } else { + promise = gnMap.addWmtsFromScratch(map, layerUrl, layerName, undefined, layer.get('md')); + } + + promise.then(function(result) { + // Remove the original layer from the map if it was re-added successfully + map.removeLayer(layer); + }); + }; + + // Change the projection for all supported layers + scope.changeLayerProjection = function (obj, oldProj, newProj) { + var layers = obj.getLayers().getArray().reverse(); + for (var i = layers.length - 1; i >= 0; --i) { + var layer = layers[i]; + if (layer instanceof ol.layer.Group) { + this.changeLayerProjection(layer, oldProj, newProj); + } else if (layer instanceof ol.layer.Tile || layer instanceof ol.layer.Image) { + this.reloadOwsLayer(scope.map, layer); + } else if (layer instanceof ol.layer.Vector) { + var features = layer.getSource().getFeatures(); + for (var i = 0; i < features.length; i += 1) { + features[i].getGeometry().transform(oldProj, newProj); + } } } }; @@ -93,8 +126,7 @@ return; } - // First, get all the info to populate the map - + // Get all the info to populate the map var projectionConfig = {}; gnViewerSettings.mapConfig.switcherProjectionList @@ -104,16 +136,21 @@ } }); - var newExtent = ol.proj.transformExtent(view - .calculateExtent(scope.map.getSize()), oldProj, - newProj); - - if (newProj.getExtent()) { - newExtent = newProj.getExtent(); - } + // Reproject old extent to new one: + // The old or new projection might need to be patched + // to make up for incorrect axis orientation settings, + // which means we'll first transform to a temporary extent + var oldExtent = view.calculateExtent(scope.map.getSize()); + var tmpExtent = ol.proj.transformExtent( + oldExtent, gnMap.patchOlProjection(oldProj), + gnMap.patchOlProjection(newProj), 8 + ); + var newExtent = ol.proj.transformExtent( + tmpExtent, gnMap.patchOlProjection(newProj), + newProj, 8); var mapsConfig = { - projection : newProj + projection : ol.proj.get(projection) }; if (projectionConfig.resolutions @@ -126,6 +163,7 @@ // Set the view var newView = new ol.View(mapsConfig); + newView.setViewportSize(scope.map.getSize()); scope.map.setView(newView); // Rearrange base layers to adapt (if possible) to new @@ -192,11 +230,7 @@ gnViewerSettings.bgLayers = bgLayers; // Reproject all layers in the map - scope.map.getLayers().forEach( - function (layer) { - scope.changeLayerProjection(layer, oldProj, - newProj); - }); + scope.changeLayerProjection(scope.map, oldProj, newProj); // Reproject all controls in the map scope.map.getControls().forEach(function (control) { @@ -205,12 +239,10 @@ } }); - // Relocate map to extent - scope.map.getView().fit(newExtent, - scope.map.getSize()); - scope.listOpen = false; + // Fit view to new extent + newView.fit(newExtent, scope.map.getSize()); }; } ], diff --git a/web-ui/src/main/resources/catalog/components/viewer/wmsimport/WmsImportDirective.js b/web-ui/src/main/resources/catalog/components/viewer/wmsimport/WmsImportDirective.js index 2815993741e..72a9b52ad36 100644 --- a/web-ui/src/main/resources/catalog/components/viewer/wmsimport/WmsImportDirective.js +++ b/web-ui/src/main/resources/catalog/components/viewer/wmsimport/WmsImportDirective.js @@ -78,22 +78,34 @@ if ($.inArray(url, gnGlobalSettings.requireProxy) >= 0) { getCapLayer.useProxy = true; } - if ($scope.format == 'wms') { - var layer = - gnMap.addWmsToMapFromCap($scope.map, getCapLayer, style); + if ($scope.format === 'wms') { + var layer = gnMap.addWmsToMapFromCap($scope.map, getCapLayer, style); + var wmsErrors = layer.get('errors'); + if (!wmsErrors || wmsErrors.length === 0) { + gnAlertService.addAlert({ + msg: $translate.instant('layerAdded', + { + layer: layer.get('label'), + extent: layer.get('extent') ? layer.get('extent').toString() : null + }), + type: 'success' + }, 5); + } else { + wmsErrors.forEach(function (errMsg) { gnAlertService.addAlert({ - msg: $translate.instant('layerAdded', - {layer: layer.get('label'), extent: layer.get('cextent').toString()}), - type: 'success' - },4); + msg: errMsg, + type: 'warning' + }, 5); + }); + } gnMap.feedLayerMd(layer); return layer; - } else if ($scope.format == 'wfs') { + } else if ($scope.format === 'wfs') { var layer = gnMap.addWfsToMapFromCap($scope.map, getCapLayer, $scope.url); gnMap.feedLayerMd(layer); return layer; - } else if ($scope.format == 'wmts') { + } else if ($scope.format === 'wmts') { return gnMap.addWmtsToMapFromCap($scope.map, getCapLayer, $scope.capability); } else { @@ -425,7 +437,7 @@ collection: '=' }, template: "