diff --git a/src/server/services/wms/qgswmsparameters.cpp b/src/server/services/wms/qgswmsparameters.cpp index ba6d0004350..ec07335026d 100644 --- a/src/server/services/wms/qgswmsparameters.cpp +++ b/src/server/services/wms/qgswmsparameters.cpp @@ -19,6 +19,8 @@ #include "qgsdatasourceuri.h" #include "qgsmessagelog.h" +const QString EXTERNAL_LAYER_PREFIX = QStringLiteral( "EXTERNAL_WMS:" ); + namespace QgsWms { // @@ -1408,6 +1410,10 @@ namespace QgsWms for ( int i = 0; i < layers.size(); i++ ) { QString layer = layers[i]; + + if ( isExternalLayer( layer ) ) + continue; + QgsWmsParametersLayer param; param.mNickname = layer; @@ -1492,6 +1498,21 @@ namespace QgsWms return params; } + QList QgsWmsParameters::externalLayersParameters() const + { + QList externalLayers; + + QStringList layers = allLayersNickname(); + QStringList::const_iterator rit = std::remove_if( layers.begin(), layers.end(), QgsWmsParameters::isExternalLayer ); + + for ( QStringList::const_iterator it = layers.begin(); it != rit; ++it ) + { + externalLayers << externalLayerParameter( *it ); + } + + return externalLayers; + } + QString QgsWmsParameters::backgroundColor() const { return mWmsParameters[ QgsWmsParameter::BGCOLOR ].toString(); @@ -1577,13 +1598,30 @@ namespace QgsWms } //layers - QStringList layers; + QStringList allLayers; wmsParam = idParameter( QgsWmsParameter::LAYERS, mapId ); if ( wmsParam.isValid() ) { - layers = wmsParam.toStringList(); + allLayers = wmsParam.toStringList(); } + // external layers + QStringList layers; + QList eParams; + + for ( const auto &layer : qgis::as_const( allLayers ) ) + { + if ( isExternalLayer( layer ) ) + { + eParams << externalLayerParameter( layer ); + } + else + { + layers << layer; + } + } + param.mExternalLayers = eParams; + QStringList styles; wmsParam = idParameter( QgsWmsParameter::STYLES, mapId ); if ( wmsParam.isValid() ) @@ -1763,4 +1801,20 @@ namespace QgsWms return p; } + + QgsWmsParametersExternalLayer QgsWmsParameters::externalLayerParameter( const QString &name ) const + { + QgsWmsParametersExternalLayer param; + + param.mName = name; + param.mName.remove( 0, EXTERNAL_LAYER_PREFIX.size() ); + param.mUri = externalWMSUri( param.mName ); + + return param; + } + + bool QgsWmsParameters::isExternalLayer( const QString &name ) + { + return name.startsWith( EXTERNAL_LAYER_PREFIX ); + } } diff --git a/src/server/services/wms/qgswmsparameters.h b/src/server/services/wms/qgswmsparameters.h index 3a09f0aa6df..f9c142ef406 100644 --- a/src/server/services/wms/qgswmsparameters.h +++ b/src/server/services/wms/qgswmsparameters.h @@ -55,6 +55,12 @@ namespace QgsWms QString mStyle; }; + struct QgsWmsParametersExternalLayer + { + QString mName; + QString mUri; + }; + struct QgsWmsParametersHighlightLayer { QString mName; @@ -79,6 +85,7 @@ namespace QgsWms float mGridX = 0; float mGridY = 0; QList mLayers; // list of layers for this composer map + QList mExternalLayers; // list of external layers for this composer map QList mHighlightLayers; // list of highlight layers for this composer map }; @@ -944,6 +951,12 @@ namespace QgsWms */ QList highlightLayersParameters() const; + /** + * Returns parameters for each external layer. + * \since QGIS 3.8 + */ + QList externalLayersParameters() const; + /** * Returns HIGHLIGHT_GEOM as a list of string in WKT. * \returns highlight geometries @@ -1165,6 +1178,8 @@ namespace QgsWms QStringList atlasPk() const; private: + static bool isExternalLayer( const QString &name ); + bool loadParameter( const QString &name, const QString &value ) override; void save( const QgsWmsParameter ¶meter, bool multi = false ); @@ -1174,6 +1189,8 @@ namespace QgsWms void raiseError( const QString &msg ) const; void log( const QString &msg ) const; + QgsWmsParametersExternalLayer externalLayerParameter( const QString &name ) const; + QMultiMap layerFilters( const QStringList &layers ) const; QMap mWmsParameters; diff --git a/src/server/services/wms/qgswmsrenderer.cpp b/src/server/services/wms/qgswmsrenderer.cpp index 7636e1618ee..290206abc49 100644 --- a/src/server/services/wms/qgswmsrenderer.cpp +++ b/src/server/services/wms/qgswmsrenderer.cpp @@ -653,13 +653,14 @@ namespace QgsWms if ( !map->keepLayerSet() ) { - if ( cMapParams.mLayers.isEmpty() ) + if ( cMapParams.mLayers.isEmpty() && cMapParams.mExternalLayers.isEmpty() ) { map->setLayers( mapSettings.layers() ); } else { QList layerSet = stylizedLayers( cMapParams.mLayers ); + layerSet << externalLayers( cMapParams.mExternalLayers ); layerSet << highlightLayers( cMapParams.mHighlightLayers ); std::reverse( layerSet.begin(), layerSet.end() ); map->setLayers( layerSet ); @@ -856,6 +857,9 @@ namespace QgsWms setLayerAccessControlFilter( layer ); } + // add external layers + layers = layers << externalLayers( mWmsParameters.externalLayersParameters() ); + // add highlight layers above others layers = layers << highlightLayers( mWmsParameters.highlightLayersParameters() ); @@ -2914,6 +2918,25 @@ namespace QgsWms return highlightLayers; } + QList QgsRenderer::externalLayers( const QList ¶ms ) + { + QList layers; + + for ( const QgsWmsParametersExternalLayer ¶m : params ) + { + std::unique_ptr layer = qgis::make_unique< QgsRasterLayer >( param.mUri, param.mName, QStringLiteral( "wms" ) ); + + if ( layer->isValid() ) + { + // to delete later + mTemporaryLayers.append( layer.release() ); + layers << mTemporaryLayers.last(); + } + } + + return layers; + } + QList QgsRenderer::sldStylizedLayers( const QString &sld ) const { QList layers; @@ -2976,19 +2999,7 @@ namespace QgsWms { QString nickname = param.mNickname; QString style = param.mStyle; - if ( nickname.startsWith( "EXTERNAL_WMS:" ) ) - { - QString externalLayerId = nickname; - externalLayerId.remove( 0, 13 ); - QgsMapLayer *externalWMSLayer = createExternalWMSLayer( externalLayerId ); - if ( externalWMSLayer ) - { - layers.append( externalWMSLayer ); - mNicknameLayers[nickname] = externalWMSLayer; //might be used later in GetPrint request - mTemporaryLayers.append( externalWMSLayer ); - } - } - else if ( mNicknameLayers.contains( nickname ) && !mRestrictedLayers.contains( nickname ) ) + if ( mNicknameLayers.contains( nickname ) && !mRestrictedLayers.contains( nickname ) ) { if ( !style.isEmpty() ) { @@ -3032,19 +3043,6 @@ namespace QgsWms return layers; } - QgsMapLayer *QgsRenderer::createExternalWMSLayer( const QString &externalLayerId ) const - { - QString wmsUri = mWmsParameters.externalWMSUri( externalLayerId.toUpper() ); - QgsMapLayer *wmsLayer = new QgsRasterLayer( wmsUri, externalLayerId, QStringLiteral( "wms" ) ); - if ( !wmsLayer->isValid() ) - { - delete wmsLayer; - return nullptr; - } - - return wmsLayer; - } - void QgsRenderer::removeTemporaryLayers() { qDeleteAll( mTemporaryLayers ); diff --git a/src/server/services/wms/qgswmsrenderer.h b/src/server/services/wms/qgswmsrenderer.h index c9f50004d68..bbe2832f27c 100644 --- a/src/server/services/wms/qgswmsrenderer.h +++ b/src/server/services/wms/qgswmsrenderer.h @@ -122,6 +122,9 @@ namespace QgsWms // Build and returns highlight layers QList highlightLayers( QList params ); + // Build and returns external layers + QList externalLayers( const QList ¶ms ); + // Init a map with nickname for layers' project void initNicknameLayers(); @@ -289,9 +292,6 @@ namespace QgsWms * */ bool configurePrintLayout( QgsPrintLayout *c, const QgsMapSettings &mapSettings, bool atlasPrint = false ); - //! Creates external WMS layer. Caller takes ownership - QgsMapLayer *createExternalWMSLayer( const QString &externalLayerId ) const; - void removeTemporaryLayers(); void handlePrintErrors( const QgsLayout *layout ) const; diff --git a/tests/src/python/test_qgsserver_wms_getmap.py b/tests/src/python/test_qgsserver_wms_getmap.py index efaf3b52383..229042aae61 100644 --- a/tests/src/python/test_qgsserver_wms_getmap.py +++ b/tests/src/python/test_qgsserver_wms_getmap.py @@ -1457,6 +1457,32 @@ class TestQgsServerWMSGetMap(QgsServerTestBase): self.assertTrue('ServerException' in str(r)) + @unittest.skipIf(os.environ.get('TRAVIS', '') == 'true', 'Can\'t rely on external resources for continuous integration') + def test_wms_getmap_external(self): + # 1 bits + qs = "?" + "&".join(["%s=%s" % i for i in list({ + "MAP": urllib.parse.quote(self.projectPath), + "SERVICE": "WMS", + "REQUEST": "GetMap", + "LAYERS": "EXTERNAL_WMS:landsat", + "landsat:layers": "GEBCO_LATEST", + "landsat:dpiMode": "7", + "landsat:url": "https://www.gebco.net/data_and_products/gebco_web_services/web_map_service/mapserv", + "landsat:crs": "EPSG:4326", + "landsat:styles": "default", + "landsat:format": "image/jpeg", + "landsat:bbox": "-90,-180,90,180", + "landsat:version": "1.3.0", + "STYLES": "", + "BBOX": "-90,-180,90,180", + "HEIGHT": "500", + "WIDTH": "500", + "CRS": "EPSG:4326" + }.items())]) + + r, h = self._result(self._execute_request(qs)) + self._img_diff_error(r, h, "WMS_GetMap_External", 20000) + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_qgsserver_wms_getprint.py b/tests/src/python/test_qgsserver_wms_getprint.py index fcae9b549de..9c117ee4609 100644 --- a/tests/src/python/test_qgsserver_wms_getprint.py +++ b/tests/src/python/test_qgsserver_wms_getprint.py @@ -437,6 +437,30 @@ class TestQgsServerWMSGetPrint(QgsServerTestBase): self.assertTrue('atlasEnabled="1"' in str(r)) self.assertTrue('' in str(r)) + @unittest.skipIf(os.environ.get('TRAVIS', '') == 'true', 'Can\'t rely on external resources for continuous integration') + def test_wms_getprint_external(self): + qs = "?" + "&".join(["%s=%s" % i for i in list({ + "MAP": urllib.parse.quote(self.projectPath), + "SERVICE": "WMS", + "VERSION": "1.1.1", + "REQUEST": "GetPrint", + "TEMPLATE": "layoutA4", + "map0:EXTENT": "-90,-180,90,180", + "map0:LAYERS": "EXTERNAL_WMS:landsat", + "landsat:layers": "GEBCO_LATEST", + "landsat:dpiMode": "7", + "landsat:url": "https://www.gebco.net/data_and_products/gebco_web_services/web_map_service/mapserv", + "landsat:crs": "EPSG:4326", + "landsat:styles": "default", + "landsat:format": "image/jpeg", + "landsat:bbox": "-90,-180,90,180", + "landsat:version": "1.3.0", + "CRS": "EPSG:4326" + }.items())]) + + r, h = self._result(self._execute_request(qs)) + self._img_diff_error(r, h, "WMS_GetPrint_External") + if __name__ == '__main__': unittest.main() diff --git a/tests/testdata/control_images/qgis_server/WMS_GetMap_External/WMS_GetMap_External.png b/tests/testdata/control_images/qgis_server/WMS_GetMap_External/WMS_GetMap_External.png new file mode 100644 index 00000000000..f8b3ed8a34d Binary files /dev/null and b/tests/testdata/control_images/qgis_server/WMS_GetMap_External/WMS_GetMap_External.png differ diff --git a/tests/testdata/control_images/qgis_server/WMS_GetPrint_External/WMS_GetPrint_External.png b/tests/testdata/control_images/qgis_server/WMS_GetPrint_External/WMS_GetPrint_External.png new file mode 100644 index 00000000000..f833de7cd77 Binary files /dev/null and b/tests/testdata/control_images/qgis_server/WMS_GetPrint_External/WMS_GetPrint_External.png differ