Skip to content

Commit

Permalink
Add feature bounding box if called with filter and without I/J
Browse files Browse the repository at this point in the history
  • Loading branch information
mhugent committed Aug 17, 2011
1 parent 57c4ee7 commit f32cd21
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 86 deletions.
213 changes: 133 additions & 80 deletions src/mapserver/qgswmsserver.cpp
Expand Up @@ -121,21 +121,21 @@ QDomDocument QgsWMSServer::getCapabilities()

QList<QPair<QString, QString> > queryItems = mapUrl.queryItems();
QList<QPair<QString, QString> >::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 );
}
Expand Down Expand Up @@ -636,11 +636,10 @@ int QgsWMSServer::getFeatureInfo( QDomDocument& result )
if ( i_it == mParameterMap.end() )
{
std::map<QString, QString>::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
{
Expand All @@ -649,18 +648,17 @@ int QgsWMSServer::getFeatureInfo( QDomDocument& result )
i = iString.toInt( &conversionSuccess );
if ( !conversionSuccess )
{
return 6;
i = -1;
}

std::map<QString, QString>::const_iterator j_it = mParameterMap.find( "J" );
if ( j_it == mParameterMap.end() )
{
std::map<QString, QString>::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
{
Expand All @@ -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<QString, QString> originalLayerFilters = applyRequestedLayerFilters( layersList, layerIds );

QDomElement getFeatureInfoElement = result.createElement( "GetFeatureInfoResponse" );
result.appendChild( getFeatureInfoElement );

Expand All @@ -681,72 +704,86 @@ int QgsWMSServer::getFeatureInfo( QDomDocument& result )

QList<QgsMapLayer*> 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<QgsMapLayer*>::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;
}

QDomElement layerElement = result.createElement( "Layer" );
layerElement.setAttribute( "name", currentLayer->name() );
getFeatureInfoElement.appendChild( layerElement );

//switch depending on vector or raster
QgsVectorLayer* vectorLayer = dynamic_cast<QgsVectorLayer*>( 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() )
{
layerAliasInfo = aliasIt.value();
}
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 );
//hidden attributes for this layer
QSet<QString> layerHiddenAttributes;
QMap< QString, QSet<QString> >::const_iterator hiddenIt = hiddenAttributes.find( currentLayer->id() );
if ( hiddenIt != hiddenAttributes.constEnd() )
{
layerHiddenAttributes = hiddenIt.value();
}

//switch depending on vector or raster
QgsVectorLayer* vectorLayer = dynamic_cast<QgsVectorLayer*>( currentLayer );
if ( vectorLayer )
if ( featureInfoFromVectorLayer( vectorLayer, infoPoint, featureCount, result, layerElement, mMapRenderer, layerAliasInfo, layerHiddenAttributes, featuresRect ) != 0 )
{
//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<QString> layerHiddenAttributes;
QMap< QString, QSet<QString> >::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;
}
continue;
}
else //raster layer
}
else //raster layer
{
QgsRasterLayer* rasterLayer = dynamic_cast<QgsRasterLayer*>( currentLayer );
if ( rasterLayer )
{
QgsRasterLayer* rasterLayer = dynamic_cast<QgsRasterLayer*>( currentLayer );
if ( rasterLayer )
{
if ( featureInfoFromRasterLayer( rasterLayer, infoPoint, result, layerElement ) != 0 )
{
continue;
}
}
else
{
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;
}

Expand Down Expand Up @@ -1101,29 +1138,29 @@ 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;
}

//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<int, QString>& aliasMap,
QSet<QString>& hiddenAttributes ) const
QSet<QString>& hiddenAttributes, QgsRectangle* featureBBox ) const
{
if ( !layer || !mapRender )
{
Expand All @@ -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();
Expand All @@ -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 )
Expand Down Expand Up @@ -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() )
{
*featureBBox = box;
}
else
{
QDomElement geometryElement = infoDocument.createElement( "Attribute" );
geometryElement.setAttribute( "name", "geometry" );
geometryElement.setAttribute( "value", geom->exportToWkt() );
geometryElement.setAttribute( "type", "derived" );
featureElement.appendChild( geometryElement );
featureBBox->combineExtentWith( &box );
}
}
}
Expand All @@ -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<QString, QString> attributes;
layer->identify( infoPoint, attributes );
layer->identify( *infoPoint, attributes );

for ( QMap<QString, QString>::const_iterator it = attributes.constBegin(); it != attributes.constEnd(); ++it )
{
Expand Down
14 changes: 8 additions & 6 deletions src/mapserver/qgswmsserver.h
Expand Up @@ -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;
Expand Down Expand Up @@ -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<int, QString>& aliasMap, QSet<QString>& 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<int, QString>& aliasMap, QSet<QString>& 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;
Expand Down

0 comments on commit f32cd21

Please sign in to comment.