From 0f00a5235dc2e7279e34adadf501a73bbed167e6 Mon Sep 17 00:00:00 2001 From: rldhont Date: Thu, 25 May 2017 14:55:46 +0200 Subject: [PATCH] [Server] WMS GetContext refactoring --- src/server/services/wms/qgswmsgetcontext.cpp | 396 ++++++++++++++++++- 1 file changed, 393 insertions(+), 3 deletions(-) diff --git a/src/server/services/wms/qgswmsgetcontext.cpp b/src/server/services/wms/qgswmsgetcontext.cpp index 58aa9ecef9d..4a3067f1c3e 100644 --- a/src/server/services/wms/qgswmsgetcontext.cpp +++ b/src/server/services/wms/qgswmsgetcontext.cpp @@ -20,9 +20,36 @@ ***************************************************************************/ #include "qgswmsutils.h" #include "qgswmsgetcontext.h" +#include "qgsserverprojectutils.h" + +#include "qgslayertreenode.h" +#include "qgslayertreegroup.h" +#include "qgslayertreelayer.h" +#include "qgslayertreemodel.h" +#include "qgslayertree.h" +#include "qgsmaplayerstylemanager.h" + +#include "qgscsexception.h" namespace QgsWms { + namespace + { + void appendOwsLayerStyles( QDomDocument &doc, QDomElement &layerElem, QgsMapLayer *currentLayer ); + + void appendOwsLayersFromTreeGroup( QDomDocument &doc, + QDomElement &parentLayer, + QgsServerInterface *serverIface, + const QgsProject *project, + const QgsServerRequest &request, + const QgsLayerTreeGroup *layerTreeGroup, + QgsRectangle &combinedBBox, + const QString &strGroup ); + + void appendOwsGeneralAndResourceList( QDomDocument &doc, QDomElement &parentElement, + QgsServerInterface *serverIface, const QgsProject *project, + const QgsServerRequest &request ); + } void writeGetContext( QgsServerInterface *serverIface, const QgsProject *project, const QString &version, const QgsServerRequest &request, @@ -40,7 +67,7 @@ namespace QgsWms { Q_UNUSED( version ); - QgsWmsConfigParser *configParser = getConfigParser( serverIface ); + //QgsWmsConfigParser *configParser = getConfigParser( serverIface ); QDomDocument doc; QDomProcessingInstruction xmlDeclaration = doc.createProcessingInstruction( QStringLiteral( "xml" ), @@ -64,11 +91,374 @@ namespace QgsWms owsContextElem.setAttribute( QStringLiteral( "version" ), QStringLiteral( "0.3.1" ) ); doc.appendChild( owsContextElem ); - QString hrefString = serviceUrl( request, project ).toString( QUrl::FullyDecoded ); - configParser->owsGeneralAndResourceList( owsContextElem, doc, hrefString ); + appendOwsGeneralAndResourceList( doc, owsContextElem, serverIface, project, request ); return doc; } + namespace + { + void appendOwsGeneralAndResourceList( QDomDocument &doc, QDomElement &parentElement, + QgsServerInterface *serverIface, const QgsProject *project, + const QgsServerRequest &request ) + { + parentElement.setAttribute( QStringLiteral( "id" ), "ows-context-" + project->fileInfo().baseName() ); + + // OWSContext General element + QDomElement generalElem = doc.createElement( QStringLiteral( "General" ) ); + + // OWSContext Window element + QDomElement windowElem = doc.createElement( QStringLiteral( "Window" ) ); + windowElem.setAttribute( QStringLiteral( "height" ), QStringLiteral( "600" ) ); + windowElem.setAttribute( QStringLiteral( "width" ), QStringLiteral( "800" ) ); + generalElem.appendChild( windowElem ); + + //OWS title + //why not use project title ? + QString title = QgsServerProjectUtils::owsServiceTitle( *project ); + if ( !title.isEmpty() ) + { + QDomElement titleElem = doc.createElement( QStringLiteral( "ows:Title" ) ); + QDomText titleText = doc.createTextNode( title ); + titleElem.appendChild( titleText ); + generalElem.appendChild( titleElem ); + } + + //OWS abstract + QString abstract = QgsServerProjectUtils::owsServiceAbstract( *project ); + if ( !abstract.isEmpty() ) + { + QDomElement abstractElem = doc.createElement( QStringLiteral( "ows:Abstract" ) ); + QDomText abstractText = doc.createCDATASection( abstract ); + abstractElem.appendChild( abstractText ); + generalElem.appendChild( abstractElem ); + } + + //OWS Keywords + QStringList keywords = QgsServerProjectUtils::owsServiceKeywords( *project ); + if ( !keywords.isEmpty() ) + { + bool sia2045 = QgsServerProjectUtils::wmsInfoFormatSia2045( *project ); + + QDomElement keywordsElem = doc.createElement( QStringLiteral( "ows:Keywords" ) ); + + for ( int i = 0; i < keywords.size(); ++i ) + { + QString keyword = keywords.at( i ); + if ( !keyword.isEmpty() ) + { + QDomElement keywordElem = doc.createElement( QStringLiteral( "ows:Keyword" ) ); + QDomText keywordText = doc.createTextNode( keyword ); + keywordElem.appendChild( keywordText ); + if ( sia2045 ) + { + keywordElem.setAttribute( QStringLiteral( "vocabulary" ), QStringLiteral( "SIA_Geo405" ) ); + } + keywordsElem.appendChild( keywordElem ); + } + } + generalElem.appendChild( keywordsElem ); + } + + // OWSContext General element is complete + parentElement.appendChild( generalElem ); + + // OWSContext ResourceList element + QDomElement resourceListElem = doc.createElement( QStringLiteral( "ResourceList" ) ); + const QgsLayerTree *projectLayerTreeRoot = project->layerTreeRoot(); + QgsRectangle combinedBBox; + appendOwsLayersFromTreeGroup( doc, resourceListElem, serverIface, project, request, projectLayerTreeRoot, combinedBBox, QString() ); + parentElement.appendChild( resourceListElem ); + + // OWSContext BoundingBox + QgsCoordinateReferenceSystem projectCrs = project->crs(); + QgsRectangle mapRect = QgsServerProjectUtils::wmsExtent( *project ); + if ( mapRect.isEmpty() ) + { + mapRect = combinedBBox; + } + QDomElement bboxElem = doc.createElement( QStringLiteral( "ows:BoundingBox" ) ); + bboxElem.setAttribute( QStringLiteral( "crs" ), projectCrs.authid() ); + if ( projectCrs.hasAxisInverted() ) + { + mapRect.invert(); + } + QDomElement lowerCornerElem = doc.createElement( QStringLiteral( "ows:LowerCorner" ) ); + QDomText lowerCornerText = doc.createTextNode( QString::number( mapRect.xMinimum() ) + " " + QString::number( mapRect.yMinimum() ) ); + lowerCornerElem.appendChild( lowerCornerText ); + bboxElem.appendChild( lowerCornerElem ); + QDomElement upperCornerElem = doc.createElement( QStringLiteral( "ows:UpperCorner" ) ); + QDomText upperCornerText = doc.createTextNode( QString::number( mapRect.xMaximum() ) + " " + QString::number( mapRect.yMaximum() ) ); + upperCornerElem.appendChild( upperCornerText ); + bboxElem.appendChild( upperCornerElem ); + generalElem.appendChild( bboxElem ); + } + + void appendOwsLayersFromTreeGroup( QDomDocument &doc, + QDomElement &parentLayer, + QgsServerInterface *serverIface, + const QgsProject *project, + const QgsServerRequest &request, + const QgsLayerTreeGroup *layerTreeGroup, + QgsRectangle &combinedBBox, + const QString &strGroup ) + { + QStringList restrictedLayers = QgsServerProjectUtils::wmsRestrictedLayers( *project ); + + QList< QgsLayerTreeNode * > layerTreeGroupChildren = layerTreeGroup->children(); + for ( int i = 0; i < layerTreeGroupChildren.size(); ++i ) + { + QgsLayerTreeNode *treeNode = layerTreeGroupChildren.at( i ); + + if ( treeNode->nodeType() == QgsLayerTreeNode::NodeGroup ) + { + QgsLayerTreeGroup *treeGroupChild = static_cast( treeNode ); + + QString name = treeGroupChild->name(); + if ( restrictedLayers.contains( name ) ) //unpublished group + { + continue; + } + + QString group; + if ( strGroup.isEmpty() ) + { + group = name; + } + else + { + group = strGroup + "/" + name; + } + + appendOwsLayersFromTreeGroup( doc, parentLayer, serverIface, project, request, treeGroupChild, combinedBBox, group ); + } + else + { + QgsLayerTreeLayer *treeLayer = static_cast( treeNode ); + QgsMapLayer *l = treeLayer->layer(); + if ( restrictedLayers.contains( l->name() ) ) //unpublished layer + { + continue; + } + + QgsAccessControl *accessControl = serverIface->accessControls(); + if ( accessControl && !accessControl->layerReadPermission( l ) ) + { + continue; + } + + QDomElement layerElem = doc.createElement( QStringLiteral( "Layer" ) ); + + // queryable layer + if ( project->nonIdentifiableLayers().contains( l->id() ) ) + { + layerElem.setAttribute( QStringLiteral( "queryable" ), QStringLiteral( "false" ) ); + } + else + { + layerElem.setAttribute( QStringLiteral( "queryable" ), QStringLiteral( "true" ) ); + } + + // visibility + if ( treeLayer->itemVisibilityChecked() ) + { + layerElem.setAttribute( QStringLiteral( "hidden" ), QStringLiteral( "true" ) ); + } + else + { + layerElem.setAttribute( QStringLiteral( "hidden" ), QStringLiteral( "false" ) ); + } + + // layer group + if ( !strGroup.isEmpty() ) + { + layerElem.setAttribute( QStringLiteral( "group" ), strGroup ); + } + + // Because Layer transparency is used for the rendering + // OWSContext Layer opacity is set to 1 + layerElem.setAttribute( QStringLiteral( "opacity" ), 1 ); + + QString wmsName = l->name(); + if ( QgsServerProjectUtils::wmsUseLayerIds( *project ) ) + { + wmsName = l->id(); + } + else if ( !l->shortName().isEmpty() ) + { + wmsName = l->shortName(); + } + // layer wms name + layerElem.setAttribute( QStringLiteral( "name" ), wmsName ); + // define an id based on layer wms name + layerElem.setAttribute( QStringLiteral( "id" ), wmsName.replace( QRegExp( "[\\W]" ), QStringLiteral( "_" ) ) ); + + // layer title + QDomElement titleElem = doc.createElement( QStringLiteral( "ows:Title" ) ); + QString title = l->title(); + if ( title.isEmpty() ) + { + title = l->name(); + } + QDomText titleText = doc.createTextNode( title ); + titleElem.appendChild( titleText ); + layerElem.appendChild( titleElem ); + + // WMS GetMap output format + QDomElement formatElem = doc.createElement( QStringLiteral( "ows:OutputFormat" ) ); + QDomText formatText = doc.createTextNode( QStringLiteral( "image/png" ) ); + formatElem.appendChild( formatText ); + layerElem.appendChild( formatElem ); + + // Get WMS service URL for Server Element + QUrl href = serviceUrl( request, project ); + + //href needs to be a prefix + QString hrefString = href.toString( QUrl::FullyDecoded ); + hrefString.append( href.hasQuery() ? "&" : "?" ); + + // COntext Server Element with WMS service URL + QDomElement serverElem = doc.createElement( QStringLiteral( "Server" ) ); + serverElem.setAttribute( QStringLiteral( "service" ), QStringLiteral( "urn:ogc:serviceType:WMS" ) ); + serverElem.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.3.0" ) ); + serverElem.setAttribute( QStringLiteral( "default" ), QStringLiteral( "true" ) ); + QDomElement orServerElem = doc.createElement( QStringLiteral( "OnlineResource" ) ); + orServerElem.setAttribute( QStringLiteral( "xlink:href" ), hrefString ); + serverElem.appendChild( orServerElem ); + layerElem.appendChild( serverElem ); + + QString abstract = l->abstract(); + if ( !abstract.isEmpty() ) + { + QDomElement abstractElem = doc.createElement( QStringLiteral( "ows:Abstract" ) ); + QDomText abstractText = doc.createTextNode( abstract ); + abstractElem.appendChild( abstractText ); + layerElem.appendChild( abstractElem ); + } + + //min/max scale denominatorScaleBasedVisibility + if ( l->hasScaleBasedVisibility() ) + { + QString minScaleString = QString::number( l->minimumScale() ); + QString maxScaleString = QString::number( l->maximumScale() ); + QDomElement minScaleElem = doc.createElement( QStringLiteral( "sld:MinScaleDenominator" ) ); + QDomText minScaleText = doc.createTextNode( minScaleString ); + minScaleElem.appendChild( minScaleText ); + layerElem.appendChild( minScaleElem ); + QDomElement maxScaleElem = doc.createElement( QStringLiteral( "sld:MaxScaleDenominator" ) ); + QDomText maxScaleText = doc.createTextNode( maxScaleString ); + maxScaleElem.appendChild( maxScaleText ); + layerElem.appendChild( maxScaleElem ); + } + + // Style list + appendOwsLayerStyles( doc, layerElem, l ); + + //keyword list + if ( !l->keywordList().isEmpty() ) + { + QStringList keywordStringList = l->keywordList().split( QStringLiteral( "," ) ); + bool sia2045 = QgsServerProjectUtils::wmsInfoFormatSia2045( *project ); + + QDomElement keywordsElem = doc.createElement( QStringLiteral( "ows:Keywords" ) ); + for ( int i = 0; i < keywordStringList.size(); ++i ) + { + QDomElement keywordElem = doc.createElement( QStringLiteral( "ows:Keyword" ) ); + QDomText keywordText = doc.createTextNode( keywordStringList.at( i ).trimmed() ); + keywordElem.appendChild( keywordText ); + if ( sia2045 ) + { + keywordElem.setAttribute( QStringLiteral( "vocabulary" ), QStringLiteral( "SIA_Geo405" ) ); + } + keywordsElem.appendChild( keywordElem ); + } + layerElem.appendChild( keywordsElem ); + } + + // layer data URL + QString dataUrl = l->dataUrl(); + if ( !dataUrl.isEmpty() ) + { + QDomElement dataUrlElem = doc.createElement( QStringLiteral( "DataURL" ) ); + QString dataUrlFormat = l->dataUrlFormat(); + dataUrlElem.setAttribute( QStringLiteral( "format" ), dataUrlFormat ); + QDomElement dataORElem = doc.createElement( QStringLiteral( "OnlineResource" ) ); + dataORElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) ); + dataORElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) ); + dataORElem.setAttribute( QStringLiteral( "xlink:href" ), dataUrl ); + dataUrlElem.appendChild( dataORElem ); + layerElem.appendChild( dataUrlElem ); + } + + // layer metadata URL + QString metadataUrl = l->metadataUrl(); + if ( !metadataUrl.isEmpty() ) + { + QDomElement metaUrlElem = doc.createElement( QStringLiteral( "MetadataURL" ) ); + QString metadataUrlFormat = l->metadataUrlFormat(); + metaUrlElem.setAttribute( QStringLiteral( "format" ), metadataUrlFormat ); + QDomElement metaUrlORElem = doc.createElement( QStringLiteral( "OnlineResource" ) ); + metaUrlORElem.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) ); + metaUrlORElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) ); + metaUrlORElem.setAttribute( QStringLiteral( "xlink:href" ), metadataUrl ); + metaUrlElem.appendChild( metaUrlORElem ); + layerElem.appendChild( metaUrlElem ); + } + + // update combineBBox + try + { + QgsCoordinateTransform t( l->crs(), project->crs() ); + QgsRectangle BBox = t.transformBoundingBox( l->extent() ); + if ( combinedBBox.isEmpty() ) + { + combinedBBox = BBox; + } + else + { + combinedBBox.combineExtentWith( BBox ); + } + } + catch ( const QgsCsException &cse ) + { + Q_UNUSED( cse ); + } + + if ( parentLayer.hasChildNodes() ) + { + parentLayer.insertBefore( layerElem, parentLayer.firstChild() ); + } + else + { + parentLayer.appendChild( layerElem ); + } + }// end of treeNode type + }// end of for + } + + void appendOwsLayerStyles( QDomDocument &doc, QDomElement &layerElem, QgsMapLayer *currentLayer ) + { + Q_FOREACH ( QString styleName, currentLayer->styleManager()->styles() ) + { + if ( styleName.isEmpty() ) + styleName = EMPTY_STYLE_NAME; + + QDomElement styleListElem = doc.createElement( QStringLiteral( "StyleList" ) ); + //only one default style in project file mode + QDomElement styleElem = doc.createElement( QStringLiteral( "Style" ) ); + styleElem.setAttribute( QStringLiteral( "current" ), QStringLiteral( "true" ) ); + QDomElement styleNameElem = doc.createElement( QStringLiteral( "Name" ) ); + QDomText styleNameText = doc.createTextNode( styleName ); + styleNameElem.appendChild( styleNameText ); + QDomElement styleTitleElem = doc.createElement( QStringLiteral( "Title" ) ); + QDomText styleTitleText = doc.createTextNode( styleName ); + styleTitleElem.appendChild( styleTitleText ); + styleElem.appendChild( styleNameElem ); + styleElem.appendChild( styleTitleElem ); + styleListElem.appendChild( styleElem ); + layerElem.appendChild( styleListElem ); + } + } + } } // samespace QgsWms