diff --git a/src/mapserver/qgswmsserver.cpp b/src/mapserver/qgswmsserver.cpp index 079fe844456..08cc094212f 100644 --- a/src/mapserver/qgswmsserver.cpp +++ b/src/mapserver/qgswmsserver.cpp @@ -121,21 +121,21 @@ QDomDocument QgsWMSServer::getCapabilities() QList > queryItems = mapUrl.queryItems(); QList >::const_iterator queryIt = queryItems.constBegin(); - for(; queryIt != queryItems.constEnd(); ++queryIt ) + for ( ; queryIt != queryItems.constEnd(); ++queryIt ) { - if( queryIt->first.compare("REQUEST", Qt::CaseInsensitive ) == 0 ) + if ( queryIt->first.compare( "REQUEST", Qt::CaseInsensitive ) == 0 ) { mapUrl.removeQueryItem( queryIt->first ); } - else if( queryIt->first.compare("VERSION", Qt::CaseInsensitive ) == 0 ) + else if ( queryIt->first.compare( "VERSION", Qt::CaseInsensitive ) == 0 ) { mapUrl.removeQueryItem( queryIt->first ); } - else if( queryIt->first.compare("SERVICE", Qt::CaseInsensitive ) == 0 ) + else if ( queryIt->first.compare( "SERVICE", Qt::CaseInsensitive ) == 0 ) { mapUrl.removeQueryItem( queryIt->first ); } - else if( queryIt->first.compare("_DC", Qt::CaseInsensitive ) == 0 ) + else if ( queryIt->first.compare( "_DC", Qt::CaseInsensitive ) == 0 ) { mapUrl.removeQueryItem( queryIt->first ); } @@ -636,11 +636,10 @@ int QgsWMSServer::getFeatureInfo( QDomDocument& result ) if ( i_it == mParameterMap.end() ) { std::map::const_iterator x_it = mParameterMap.find( "X" ); - if ( x_it == mParameterMap.end() ) + if ( x_it != mParameterMap.end() ) { - return 5; + iString = x_it->second; } - iString = x_it->second; } else { @@ -649,18 +648,17 @@ int QgsWMSServer::getFeatureInfo( QDomDocument& result ) i = iString.toInt( &conversionSuccess ); if ( !conversionSuccess ) { - return 6; + i = -1; } std::map::const_iterator j_it = mParameterMap.find( "J" ); if ( j_it == mParameterMap.end() ) { std::map::const_iterator y_it = mParameterMap.find( "Y" ); - if ( y_it == mParameterMap.end() ) + if ( y_it != mParameterMap.end() ) { - return 7; + jString = y_it->second; } - jString = y_it->second; } else { @@ -669,9 +667,34 @@ int QgsWMSServer::getFeatureInfo( QDomDocument& result ) j = jString.toInt( &conversionSuccess ); if ( !conversionSuccess ) { - return 8; + j = -1; } + //Normally, I/J or X/Y are mandatory parameters. + //However, in order to make attribute only queries via the FILTER parameter, it is allowed to skip them if the FILTER parameter is there + + QgsRectangle* featuresRect = 0; + QgsPoint* infoPoint = 0; + if ( i == -1 || j == -1 ) + { + if ( mParameterMap.find( "FILTER" ) != mParameterMap.end() ) + { + featuresRect = new QgsRectangle(); + } + else + { + throw QgsMapServiceException( "ParameterMissing", "I/J parameters are required for GetFeatureInfo" ); + } + } + else + { + infoPoint = new QgsPoint(); + } + + //get the layer registered in QgsMapLayerRegistry and apply possible filters + QStringList layerIds = layerSet( layersList, stylesList, mMapRenderer->destinationCrs() ); + QMap originalLayerFilters = applyRequestedLayerFilters( layersList, layerIds ); + QDomElement getFeatureInfoElement = result.createElement( "GetFeatureInfoResponse" ); result.appendChild( getFeatureInfoElement ); @@ -681,72 +704,86 @@ int QgsWMSServer::getFeatureInfo( QDomDocument& result ) QList layerList; QgsMapLayer* currentLayer = 0; - QgsPoint infoPoint; //info point in coordinates of the layer QStringList::const_iterator layerIt; for ( layerIt = queryLayerList.constBegin(); layerIt != queryLayerList.constEnd(); ++layerIt ) { //create maplayers from sld parser (several layers are possible in case of feature info on a group) layerList = mConfigParser->mapLayerFromStyle( *layerIt, "" ); QList::iterator layerListIt = layerList.begin(); - for(; layerListIt != layerList.end(); ++layerListIt ) + for ( ; layerListIt != layerList.end(); ++layerListIt ) { - currentLayer = *layerListIt; - if ( !currentLayer || nonIdentifiableLayers.contains( currentLayer->id() ) ) - { + currentLayer = *layerListIt; + if ( !currentLayer || nonIdentifiableLayers.contains( currentLayer->id() ) ) + { continue; - } - if ( infoPointToLayerCoordinates( i, j, infoPoint, mMapRenderer, currentLayer ) != 0 ) - { + } + + if ( infoPoint && infoPointToLayerCoordinates( i, j, infoPoint, mMapRenderer, currentLayer ) != 0 ) + { continue; - } - QgsDebugMsg( "Info point in layer crs: " + QString::number( infoPoint.x() ) + "//" + QString::number( infoPoint.y() ) ); + } - QDomElement layerElement = result.createElement( "Layer" ); - layerElement.setAttribute( "name", currentLayer->name() ); - getFeatureInfoElement.appendChild( layerElement ); + QDomElement layerElement = result.createElement( "Layer" ); + layerElement.setAttribute( "name", currentLayer->name() ); + getFeatureInfoElement.appendChild( layerElement ); - //switch depending on vector or raster - QgsVectorLayer* vectorLayer = dynamic_cast( currentLayer ); - if ( vectorLayer ) + //switch depending on vector or raster + QgsVectorLayer* vectorLayer = dynamic_cast( currentLayer ); + if ( vectorLayer ) + { + //is there alias info for this vector layer? + QMap< int, QString > layerAliasInfo; + QMap< QString, QMap< int, QString > >::const_iterator aliasIt = aliasInfo.find( currentLayer->id() ); + if ( aliasIt != aliasInfo.constEnd() ) { - //is there alias info for this vector layer? - QMap< int, QString > layerAliasInfo; - QMap< QString, QMap< int, QString > >::const_iterator aliasIt = aliasInfo.find( currentLayer->id() ); - if ( aliasIt != aliasInfo.constEnd() ) - { - layerAliasInfo = aliasIt.value(); - } - - //hidden attributes for this layer - QSet layerHiddenAttributes; - QMap< QString, QSet >::const_iterator hiddenIt = hiddenAttributes.find( currentLayer->id() ); - if ( hiddenIt != hiddenAttributes.constEnd() ) - { - layerHiddenAttributes = hiddenIt.value(); - } - - if ( featureInfoFromVectorLayer( vectorLayer, infoPoint, featureCount, result, layerElement, mMapRenderer, layerAliasInfo, layerHiddenAttributes ) != 0 ) - { - continue; - } + layerAliasInfo = aliasIt.value(); } - else //raster layer + + //hidden attributes for this layer + QSet layerHiddenAttributes; + QMap< QString, QSet >::const_iterator hiddenIt = hiddenAttributes.find( currentLayer->id() ); + if ( hiddenIt != hiddenAttributes.constEnd() ) { - QgsRasterLayer* rasterLayer = dynamic_cast( currentLayer ); - if ( rasterLayer ) - { - if ( featureInfoFromRasterLayer( rasterLayer, infoPoint, result, layerElement ) != 0 ) - { - continue; - } - } - else - { + layerHiddenAttributes = hiddenIt.value(); + } + + if ( featureInfoFromVectorLayer( vectorLayer, infoPoint, featureCount, result, layerElement, mMapRenderer, layerAliasInfo, layerHiddenAttributes, featuresRect ) != 0 ) + { + continue; + } + } + else //raster layer + { + QgsRasterLayer* rasterLayer = dynamic_cast( currentLayer ); + if ( rasterLayer ) + { + if ( featureInfoFromRasterLayer( rasterLayer, infoPoint, result, layerElement ) != 0 ) + { continue; - } + } } + else + { + continue; + } + } } } + + if ( featuresRect ) + { + QDomElement bBoxElem = result.createElement( "BoundingBox" ); + bBoxElem.setAttribute( "CRS", mMapRenderer->destinationCrs().authid() ); + bBoxElem.setAttribute( "minx", featuresRect->xMinimum() ); + bBoxElem.setAttribute( "maxx", featuresRect->xMaximum() ); + bBoxElem.setAttribute( "miny", featuresRect->yMinimum() ); + bBoxElem.setAttribute( "maxy", featuresRect->yMaximum() ); + getFeatureInfoElement.insertBefore( bBoxElem, QDomNode() ); //insert as first child + } + + restoreLayerFilters( originalLayerFilters ); + delete featuresRect; + delete infoPoint; return 0; } @@ -1101,10 +1138,10 @@ int QgsWMSServer::initializeSLDParser( QStringList& layersList, QStringList& sty return 0; } -int QgsWMSServer::infoPointToLayerCoordinates( int i, int j, QgsPoint& layerCoords, QgsMapRenderer* mapRender, +int QgsWMSServer::infoPointToLayerCoordinates( int i, int j, QgsPoint* layerCoords, QgsMapRenderer* mapRender, QgsMapLayer* layer ) const { - if ( !mapRender || !layer || !mapRender->coordinateTransform() ) + if ( !layerCoords || !mapRender || !layer || !mapRender->coordinateTransform() ) { return 1; } @@ -1112,18 +1149,18 @@ int QgsWMSServer::infoPointToLayerCoordinates( int i, int j, QgsPoint& layerCoor //first transform i,j to map output coordinates QgsPoint mapPoint = mapRender->coordinateTransform()->toMapCoordinates( i, j ); //and then to layer coordinates - layerCoords = mapRender->mapToLayerCoordinates( layer, mapPoint ); + *layerCoords = mapRender->mapToLayerCoordinates( layer, mapPoint ); return 0; } int QgsWMSServer::featureInfoFromVectorLayer( QgsVectorLayer* layer, - const QgsPoint& infoPoint, + const QgsPoint* infoPoint, int nFeatures, QDomDocument& infoDocument, QDomElement& layerElement, QgsMapRenderer* mapRender, QMap& aliasMap, - QSet& hiddenAttributes ) const + QSet& hiddenAttributes, QgsRectangle* featureBBox ) const { if ( !layer || !mapRender ) { @@ -1133,9 +1170,16 @@ int QgsWMSServer::featureInfoFromVectorLayer( QgsVectorLayer* layer, //we need a selection rect (0.01 of map width) QgsRectangle mapRect = mapRender->extent(); QgsRectangle layerRect = mapRender->mapToLayerCoordinates( layer, mapRect ); - double searchRadius = ( layerRect.xMaximum() - layerRect.xMinimum() ) / 200; - QgsRectangle searchRect( infoPoint.x() - searchRadius, infoPoint.y() - searchRadius, - infoPoint.x() + searchRadius, infoPoint.y() + searchRadius ); + + QgsRectangle searchRect; + + //info point could be 0 in case there is only an attribute filter + if ( infoPoint ) + { + double searchRadius = ( layerRect.xMaximum() - layerRect.xMinimum() ) / 200; + searchRect.set( infoPoint->x() - searchRadius, infoPoint->y() - searchRadius, + infoPoint->x() + searchRadius, infoPoint->y() + searchRadius ); + } //do a select with searchRect and go through all the features QgsVectorDataProvider* provider = layer->dataProvider(); @@ -1150,7 +1194,7 @@ int QgsWMSServer::featureInfoFromVectorLayer( QgsVectorLayer* layer, const QgsFieldMap& fields = provider->fields(); bool addWktGeometry = mConfigParser && mConfigParser->featureInfoWithWktGeometry(); - provider->select( provider->attributeIndexes(), searchRect, addWktGeometry, true ); + provider->select( provider->attributeIndexes(), searchRect, addWktGeometry || featureBBox, true ); while ( provider->nextFeature( feature ) ) { if ( featureCounter > nFeatures ) @@ -1188,16 +1232,25 @@ int QgsWMSServer::featureInfoFromVectorLayer( QgsVectorLayer* layer, } //also append the wkt geometry as an attribute - if ( addWktGeometry ) + QgsGeometry* geom = feature.geometry(); + if ( addWktGeometry && geom ) { - QgsGeometry* geom = feature.geometry(); - if ( geom ) + QDomElement geometryElement = infoDocument.createElement( "Attribute" ); + geometryElement.setAttribute( "name", "geometry" ); + geometryElement.setAttribute( "value", geom->exportToWkt() ); + geometryElement.setAttribute( "type", "derived" ); + featureElement.appendChild( geometryElement ); + } + if ( featureBBox && geom && mapRender ) //extend feature info bounding box if requested + { + QgsRectangle box = mapRender->layerExtentToOutputExtent( layer, geom->boundingBox() ); + if ( featureBBox->isEmpty() ) { - QDomElement geometryElement = infoDocument.createElement( "Attribute" ); - geometryElement.setAttribute( "name", "geometry" ); - geometryElement.setAttribute( "value", geom->exportToWkt() ); - geometryElement.setAttribute( "type", "derived" ); - featureElement.appendChild( geometryElement ); + *featureBBox = box; + } + else + { + featureBBox->combineExtentWith( &box ); } } } @@ -1207,17 +1260,17 @@ int QgsWMSServer::featureInfoFromVectorLayer( QgsVectorLayer* layer, } int QgsWMSServer::featureInfoFromRasterLayer( QgsRasterLayer* layer, - const QgsPoint& infoPoint, + const QgsPoint* infoPoint, QDomDocument& infoDocument, QDomElement& layerElement ) const { - if ( !layer ) + if ( !infoPoint || !layer ) { return 1; } QMap attributes; - layer->identify( infoPoint, attributes ); + layer->identify( *infoPoint, attributes ); for ( QMap::const_iterator it = attributes.constBegin(); it != attributes.constEnd(); ++it ) { diff --git a/src/mapserver/qgswmsserver.h b/src/mapserver/qgswmsserver.h index 7003ce3881c..c452c7d30bd 100644 --- a/src/mapserver/qgswmsserver.h +++ b/src/mapserver/qgswmsserver.h @@ -27,11 +27,12 @@ class QgsCoordinateReferenceSystem; class QgsComposerLayerItem; class QgsComposerLegendItem; class QgsComposition; +class QgsConfigParser; class QgsMapLayer; class QgsMapRenderer; class QgsPoint; class QgsRasterLayer; -class QgsConfigParser; +class QgsRectangle; class QgsVectorLayer; class QgsSymbol; class QFile; @@ -106,14 +107,15 @@ class QgsWMSServer @param j pixel y-coordinate @param layerCoords calculated layer coordinates are assigned to this point @return 0 in case of success*/ - int infoPointToLayerCoordinates( int i, int j, QgsPoint& layerCoords, QgsMapRenderer* mapRender, + int infoPointToLayerCoordinates( int i, int j, QgsPoint* layerCoords, QgsMapRenderer* mapRender, QgsMapLayer* layer ) const; /**Appends feature info xml for the layer to the layer element of the feature info dom document - @return 0 in case of success*/ - int featureInfoFromVectorLayer( QgsVectorLayer* layer, const QgsPoint& infoPoint, int nFeatures, QDomDocument& infoDocument, QDomElement& layerElement, QgsMapRenderer* mapRender, - QMap& aliasMap, QSet& hiddenAttributes ) const; + @param featureBBox the bounding box of the selected features in output CRS + @return 0 in case of success*/ + int featureInfoFromVectorLayer( QgsVectorLayer* layer, const QgsPoint* infoPoint, int nFeatures, QDomDocument& infoDocument, QDomElement& layerElement, QgsMapRenderer* mapRender, + QMap& aliasMap, QSet& hiddenAttributes, QgsRectangle* featureBBox = 0 ) const; /**Appends feature info xml for the layer to the layer element of the dom document*/ - int featureInfoFromRasterLayer( QgsRasterLayer* layer, const QgsPoint& infoPoint, QDomDocument& infoDocument, QDomElement& layerElement ) const; + int featureInfoFromRasterLayer( QgsRasterLayer* layer, const QgsPoint* infoPoint, QDomDocument& infoDocument, QDomElement& layerElement ) const; /**Creates a layer set and returns a stringlist with layer ids that can be passed to a QgsMapRenderer. Usually used in conjunction with readLayersAndStyles*/ QStringList layerSet( const QStringList& layersList, const QStringList& stylesList, const QgsCoordinateReferenceSystem& destCRS ) const;