diff --git a/src/core/qgsgeometry.cpp b/src/core/qgsgeometry.cpp index 9a1ea7f..7f5127c 100644 --- a/src/core/qgsgeometry.cpp +++ b/src/core/qgsgeometry.cpp @@ -536,14 +536,24 @@ QgsGeometry* QgsGeometry::fromRect( const QgsRectangle& rect ) static const QString GML_NAMESPACE = "http://www.opengis.net/gml"; QgsGeometry* QgsGeometry::fromGML2( const QDomNode& geometryNode ) { - QDomNode geometryChild = geometryNode.firstChild(); - if ( geometryChild.isNull() ) - { - return 0; - } QgsGeometry* g = new QgsGeometry(); - QDomElement geometryTypeElement = geometryChild.toElement(); + QDomElement geometryTypeElement = geometryNode.toElement(); QString geomType = geometryTypeElement.tagName(); + + if ( !( geomType == "Point" || geomType == "LineString" || geomType == "Polygon" || geomType == "MultiPoint" || geomType == "MultiLineString" || geomType == "MultiPolygon") ) + { + QDomNode geometryChild = geometryNode.firstChild(); + if ( geometryChild.isNull() ) + { + return 0; + } + geometryTypeElement = geometryChild.toElement(); + geomType = geometryTypeElement.tagName(); + } + + if ( !( geomType == "Point" || geomType == "LineString" || geomType == "Polygon" || geomType == "MultiPoint" || geomType == "MultiLineString" || geomType == "MultiPolygon") ) + return 0; + if ( geomType == "Point" ) { g->setFromGML2Point( geometryTypeElement ); diff --git a/src/mapserver/CMakeLists.txt b/src/mapserver/CMakeLists.txt index fecaa01..604b83f 100644 --- a/src/mapserver/CMakeLists.txt +++ b/src/mapserver/CMakeLists.txt @@ -34,6 +34,7 @@ SET ( qgis_mapserv_SRCS qgsbetweenfilter.cpp qgscomparisonfilter.cpp qgslogicalfilter.cpp + qgsspatialfilter.cpp qgsftptransaction.cpp qgsmslayerbuilder.cpp qgshostedvdsbuilder.cpp diff --git a/src/mapserver/qgsfilter.cpp b/src/mapserver/qgsfilter.cpp index 1afad5f..3de507c 100644 --- a/src/mapserver/qgsfilter.cpp +++ b/src/mapserver/qgsfilter.cpp @@ -19,6 +19,7 @@ #include "qgsbetweenfilter.h" #include "qgscomparisonfilter.h" #include "qgslogicalfilter.h" +#include "qgsspatialfilter.h" #include "qgsvectordataprovider.h" #include #include @@ -56,7 +57,8 @@ QgsFilter* QgsFilter::createFilterFromXml( const QDomElement& filterElem, QgsVec } //Name of the element indicates the type of filter - QString filterName = filterElem.localName(); + //using tagName and to upper for interoperability + QString filterName = filterElem.tagName().toUpper(); if ( filterName == "AND" || filterName == "OR" || filterName == "NOT" ) { @@ -91,6 +93,112 @@ QgsFilter* QgsFilter::createFilterFromXml( const QDomElement& filterElem, QgsVec } delete subFilter1; delete subFilter2; return 0; } + // if the filter is spatial + if ( filterName == "BBOX" || filterName == "CONTAINS" || filterName == "CROSSES" || filterName == "DISJOINT" || filterName == "EQUALS" || filterName == "INTERSECTS" || filterName == "OVERLAPS" || filterName == "TOUCHES" || filterName == "WITHIN" ) + { + QgsGeometry* geom; + + QDomNodeList bNodes = filterElem.elementsByTagName( "Box" ); + if ( bNodes.size() > 0 ) { + QDomElement bElem = bNodes.at( 0 ).toElement().firstChild().toElement(); + QString coordSeparator = ","; + QString tupelSeparator = " "; + if ( bElem.hasAttribute( "cs" ) ) + { + coordSeparator = bElem.attribute( "cs" ); + } + if ( bElem.hasAttribute( "ts" ) ) + { + tupelSeparator = bElem.attribute( "ts" ); + } + + QString bString = bElem.text(); + bool conversionSuccess; + double minx = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 0, 0 ).toDouble( &conversionSuccess ); + double miny = bString.section( tupelSeparator, 0, 0 ).section( coordSeparator, 1, 1 ).toDouble( &conversionSuccess ); + double maxx = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 0, 0 ).toDouble( &conversionSuccess ); + double maxy = bString.section( tupelSeparator, 1, 1 ).section( coordSeparator, 1, 1 ).toDouble( &conversionSuccess ); + QgsRectangle* rect = new QgsRectangle( minx, miny, maxx, maxy ); + geom = QgsGeometry::fromRect( *rect ); + } + + if ( !geom ) + { + QDomNodeList gNodes = filterElem.elementsByTagName( "Point" ); + if ( gNodes.size() > 0 ) { + QDomElement gElem = gNodes.at( 0 ).toElement(); + geom = QgsGeometry::fromGML2( gElem ); + } + } + + if ( !geom ) + { + QDomNodeList gNodes = filterElem.elementsByTagName( "LineString" ); + if ( gNodes.size() > 0 ) { + QDomElement gElem = gNodes.at( 0 ).toElement(); + geom = QgsGeometry::fromGML2( gElem ); + } + } + + if ( !geom ) + { + QDomNodeList gNodes = filterElem.elementsByTagName( "Polygon" ); + if ( gNodes.size() > 0 ) { + QDomElement gElem = gNodes.at( 0 ).toElement(); + geom = QgsGeometry::fromGML2( gElem ); + } + } + + if ( !geom ) + { + QDomNodeList gNodes = filterElem.elementsByTagName( "MultiPoint" ); + if ( gNodes.size() > 0 ) { + QDomElement gElem = gNodes.at( 0 ).toElement(); + geom = QgsGeometry::fromGML2( gElem ); + } + } + + if ( !geom ) + { + QDomNodeList gNodes = filterElem.elementsByTagName( "MultiLineString" ); + if ( gNodes.size() > 0 ) { + QDomElement gElem = gNodes.at( 0 ).toElement(); + geom = QgsGeometry::fromGML2( gElem ); + } + } + + if ( !geom ) + { + QDomNodeList gNodes = filterElem.elementsByTagName( "MultiPolygon" ); + if ( gNodes.size() > 0 ) { + QDomElement gElem = gNodes.at( 0 ).toElement(); + geom = QgsGeometry::fromGML2( gElem ); + } + } + + if ( !geom ) + return 0; + + if ( filterName == "BBOX" ) + return new QgsSpatialFilter( QgsSpatialFilter::BBOX, geom ); + if ( filterName == "CONTAINS" ) + return new QgsSpatialFilter( QgsSpatialFilter::CONTAINS, geom ); + if ( filterName == "CROSSES" ) + return new QgsSpatialFilter( QgsSpatialFilter::CROSSES, geom ); + if ( filterName == "DISJOINT" ) + return new QgsSpatialFilter( QgsSpatialFilter::DISJOINT, geom ); + if ( filterName == "EQUALS" ) + return new QgsSpatialFilter( QgsSpatialFilter::EQUALS, geom ); + if ( filterName == "INTERSECTS" ) + return new QgsSpatialFilter( QgsSpatialFilter::INTERSECTS, geom ); + if ( filterName == "OVERLAPS" ) + return new QgsSpatialFilter( QgsSpatialFilter::OVERLAPS, geom ); + if ( filterName == "TOUCHES" ) + return new QgsSpatialFilter( QgsSpatialFilter::TOUCHES, geom ); + if ( filterName == "WITHIN" ) + return new QgsSpatialFilter( QgsSpatialFilter::WITHIN, geom ); + return 0; + } //assume it must be a comparison filter @@ -153,33 +261,33 @@ QgsFilter* QgsFilter::createFilterFromXml( const QDomElement& filterElem, QgsVec //now create the filter //comparison filter? - if ( filterName == "PropertyIsEqualTo" ) + if ( filterName == "PROPERTYISEQUALTO" ) { return new QgsComparisonFilter( attributeIndex, QgsComparisonFilter::EQUAL, literalList.at( 0 ) ); } - else if ( filterName == "PropertyIsNotEqualTo" ) + else if ( filterName == "PROPERTYISNOTEQUALTO" ) { return new QgsComparisonFilter( attributeIndex, QgsComparisonFilter::NOT_EQUAL, literalList.at( 0 ) ); } - else if ( filterName == "PropertyIsLessThan" ) + else if ( filterName == "PROPERTYISLESSTHAN" ) { return new QgsComparisonFilter( attributeIndex, QgsComparisonFilter::LESSER, literalList.at( 0 ) ); } - else if ( filterName == "PropertyIsGreaterThan" ) + else if ( filterName == "PROPERTYISGREATERTHAN" ) { return new QgsComparisonFilter( attributeIndex, QgsComparisonFilter::GREATER, literalList.at( 0 ) ); } - else if ( filterName == "PropertyIsLessThanOrEqualTo" ) + else if ( filterName == "PROPERTYISLESSTHANOREQUALTO" ) { return new QgsComparisonFilter( attributeIndex, QgsComparisonFilter::LESSER_OR_EQUAL, literalList.at( 0 ) ); } - else if ( filterName == "PropertyIsGreaterThanOrEqualTo" ) + else if ( filterName == "PROPERTYISGREATERTHANOREQUALTO" ) { return new QgsComparisonFilter( attributeIndex, QgsComparisonFilter::GREATER_OR_EQUAL, literalList.at( 0 ) ); } //between filter? - else if ( filterName == "PropertyIsBetween" ) + else if ( filterName == "PROPERTYISBETWEEN" ) { return new QgsBetweenFilter( attributeIndex, literalList.at( 0 ), literalList.at( 1 ) ); } diff --git a/src/mapserver/qgspostrequesthandler.cpp b/src/mapserver/qgspostrequesthandler.cpp index 933ee12..3e9eb39 100644 --- a/src/mapserver/qgspostrequesthandler.cpp +++ b/src/mapserver/qgspostrequesthandler.cpp @@ -60,7 +60,7 @@ QMap QgsPostRequestHandler::parseInput() QDomElement docElem = doc.documentElement(); parameters.insert( "VERSION", docElem.attribute( "version" ) ); parameters.insert( "SERVICE", docElem.attribute( "service" ) ); - parameters.insert( "REQUEST", docElem.localName() ); + parameters.insert( "REQUEST", docElem.tagName() ); parameters.insert( "REQUEST_BODY", inputString ); } diff --git a/src/mapserver/qgsspatialfilter.cpp b/src/mapserver/qgsspatialfilter.cpp new file mode 100644 index 0000000..aa2153c --- /dev/null +++ b/src/mapserver/qgsspatialfilter.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** + qgsspatialfilter.cpp + ----------------------- + begin : Oct 19, 2012 + copyright : (C) 2012 by René-Luc D'Hont + email : rldhont at 3liz dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsspatialfilter.h" +#include "qgsgeometry.h" +#include + +QgsSpatialFilter::QgsSpatialFilter(): QgsFilter(), mSpatialType( QgsSpatialFilter::UNKNOWN ), mGeom( 0 ) +{ +} + +QgsSpatialFilter::QgsSpatialFilter( SPATIAL_TYPE st, QgsGeometry* geom ): QgsFilter(), mSpatialType( st ), mGeom( geom ) +{ +} + +QgsSpatialFilter::~QgsSpatialFilter() +{ + delete mGeom; +} + +bool QgsSpatialFilter::evaluate( const QgsFeature& f ) const +{ + if ( !mGeom ) + { + return true; + } + + QgsGeometry* geom = ( new QgsFeature( f ) )->geometry(); + switch ( mSpatialType ) + { + case BBOX: + return geom->intersects( mGeom->boundingBox() ); + break; + case CONTAINS: + return geom->contains( mGeom ); + break; + case CROSSES: + return geom->crosses( mGeom ); + break; + case DISJOINT: + return geom->disjoint( mGeom ); + break; + case EQUALS: + return geom->equals( mGeom ); + break; + case INTERSECTS: + return geom->intersects( mGeom ); + break; + case OVERLAPS: + return geom->overlaps( mGeom ); + break; + case TOUCHES: + return geom->touches( mGeom ); + break; + case WITHIN: + return geom->within( mGeom ); + break; + case UNKNOWN: + default: + break; + } + return false; +} diff --git a/src/mapserver/qgsspatialfilter.h b/src/mapserver/qgsspatialfilter.h new file mode 100644 index 0000000..c5e42fa --- /dev/null +++ b/src/mapserver/qgsspatialfilter.h @@ -0,0 +1,73 @@ +/*************************************************************************** + qgsspatialfilter.h + --------------------- + begin : Oct 19, 2012 + copyright : (C) 2012 by René-Luc D'Hont + email : rldhont at 3liz dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSSPATIALFILTER_H +#define QGSSPATIALFILTER_H + +#include +#include +#include + +/**A filter for spatial filter (bbox, intersects, within, disjoint) +Sample xml fragment: + + + +135.45,-47.425 157.95,-36.175 + + + +*/ +class QgsSpatialFilter: public QgsFilter +{ + public: + enum SPATIAL_TYPE + { + BBOX, + CONTAINS, + CROSSES, + EQUALS, + DISJOINT, + INTERSECTS, + OVERLAPS, + TOUCHES, + WITHIN, + UNKNOWN + }; + + QgsSpatialFilter(); + QgsSpatialFilter( SPATIAL_TYPE st, QgsGeometry* geom ); + ~QgsSpatialFilter(); + + /**Evaluates a feature against the filter. + @return true if the filter applies for the feature*/ + bool evaluate( const QgsFeature& f ) const; + + //setters and getters + SPATIAL_TYPE spatialType() const {return mSpatialType;} + void setSpatialType( SPATIAL_TYPE t ) {mSpatialType = t;} + + //setters and getters + QgsGeometry* geometry() const {return mGeom;} + void setGeometry( QgsGeometry* g ) {mGeom = g;} + + private: + SPATIAL_TYPE mSpatialType; + QgsGeometry* mGeom; +}; + +#endif //QGSSPATIALFILTER_H diff --git a/src/mapserver/qgswfsserver.cpp b/src/mapserver/qgswfsserver.cpp index d76df93..d5edd23 100644 --- a/src/mapserver/qgswfsserver.cpp +++ b/src/mapserver/qgswfsserver.cpp @@ -203,8 +203,11 @@ QDomDocument QgsWFSServer::getCapabilities() getFeatureFormatElement.appendChild( gmlFormatElement ); QDomElement geojsonFormatElement = doc.createElement( "GeoJSON" );/*wfs:GeoJSON*/ getFeatureFormatElement.appendChild( geojsonFormatElement ); - QDomElement getFeatureDhcTypeElement = dcpTypeElement.cloneNode().toElement();//this is the same as for 'GetCapabilities' - getFeatureElement.appendChild( getFeatureDhcTypeElement ); + QDomElement getFeatureDhcTypeGetElement = dcpTypeElement.cloneNode().toElement();//this is the same as for 'GetCapabilities' + getFeatureElement.appendChild( getFeatureDhcTypeGetElement ); + QDomElement getFeatureDhcTypePostElement = dcpTypeElement.cloneNode().toElement();//this is the same as for 'GetCapabilities' + getFeatureDhcTypePostElement.firstChild().firstChild().toElement().setTagName( "Post" ); + getFeatureElement.appendChild( getFeatureDhcTypePostElement ); //wfs:Transaction QDomElement transactionElement = doc.createElement( "Transaction"/*wfs:Transaction*/ ); @@ -240,14 +243,19 @@ QDomDocument QgsWFSServer::getCapabilities() filterCapabilitiesElement.appendChild( spatialCapabilitiesElement ); QDomElement spatialOperatorsElement = doc.createElement( "ogc:Spatial_Operators"/*ogc:Spatial_Operators*/ ); spatialCapabilitiesElement.appendChild( spatialOperatorsElement ); - QDomElement ogcBboxElement = doc.createElement( "ogc:BBOX"/*ogc:BBOX*/ ); - spatialOperatorsElement.appendChild( ogcBboxElement ); + spatialOperatorsElement.appendChild( doc.createElement( "ogc:BBOX"/*ogc:BBOX*/ ) ); + spatialOperatorsElement.appendChild( doc.createElement( "ogc:Disjoint"/*ogc:Disjoint*/ ) ); + spatialOperatorsElement.appendChild( doc.createElement( "ogc:Intersects"/*ogc:Intersects*/ ) ); + spatialOperatorsElement.appendChild( doc.createElement( "ogc:Touches"/*ogc:Touches*/ ) ); + spatialOperatorsElement.appendChild( doc.createElement( "ogc:Crosses"/*ogc:Crosses*/ ) ); + spatialOperatorsElement.appendChild( doc.createElement( "ogc:Contains"/*ogc:Contains*/ ) ); + spatialOperatorsElement.appendChild( doc.createElement( "ogc:Overlaps"/*ogc:Overlaps*/ ) ); QDomElement scalarCapabilitiesElement = doc.createElement( "ogc:Scalar_Capabilities"/*ogc:Scalar_Capabilities*/ ); filterCapabilitiesElement.appendChild( scalarCapabilitiesElement ); QDomElement comparisonOperatorsElement = doc.createElement( "ogc:Comparison_Operators"/*ogc:Comparison_Operators*/ ); scalarCapabilitiesElement.appendChild( comparisonOperatorsElement ); - QDomElement simpleComparisonsElement = doc.createElement( "ogc:Simple_Comparisons"/*ogc:Simple_Comparisons*/ ); - comparisonOperatorsElement.appendChild( simpleComparisonsElement ); + comparisonOperatorsElement.appendChild( doc.createElement( "ogc:Simple_Comparisons"/*ogc:Simple_Comparisons*/ ) ); + comparisonOperatorsElement.appendChild( doc.createElement( "ogc:Between"/*ogc:Simple_Comparisons*/ ) ); return doc; } @@ -292,6 +300,178 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format { QgsDebugMsg( "Info format is:" + format ); + QStringList wfsLayersId = mConfigParser->wfsLayers(); + + QList layerList; + QgsMapLayer* currentLayer = 0; + + QDomDocument doc; + QString errorMsg; + if ( doc.setContent( mParameterMap.value( "REQUEST_BODY" ), true, &errorMsg ) ) + { + QDomElement docElem = doc.documentElement(); + + long maxFeat = 0; + long featureCounter = 0; + long maxFeatures = 0; + if ( docElem.hasAttribute( "maxFeatures" ) ) + maxFeatures = docElem.attribute( "maxFeatures" ).toLong(); + + QDomNodeList queryNodes = docElem.elementsByTagName( "Query" ); + for ( int i = 0; i < queryNodes.size(); i++ ) + { + QDomElement queryElem = queryNodes.at( 0 ).toElement(); + mTypeName = queryElem.attribute( "typeName", "" ); + if ( mTypeName.contains( ":" ) ) + { + mTypeName = mTypeName.section( ":", 1, 1 ); + } + + layerList = mConfigParser->mapLayerFromStyle( mTypeName, "" ); + currentLayer = layerList.at( 0 ); + QgsVectorLayer* layer = dynamic_cast( currentLayer ); + if ( layer && wfsLayersId.contains( layer->id() ) ) + { + //is there alias info for this vector layer? + QMap< int, QString > layerAliasInfo; + const QMap< QString, QString >& aliasMap = layer->attributeAliases(); + QMap< QString, QString >::const_iterator aliasIt = aliasMap.constBegin(); + for ( ; aliasIt != aliasMap.constEnd(); ++aliasIt ) + { + int attrIndex = layer->fieldNameIndex( aliasIt.key() ); + if ( attrIndex != -1 ) + { + layerAliasInfo.insert( attrIndex, aliasIt.value() ); + } + } + + //excluded attributes for this layer + const QSet& layerExcludedAttributes = layer->excludeAttributesWFS(); + + //do a select with searchRect and go through all the features + QgsVectorDataProvider* provider = layer->dataProvider(); + if ( !provider ) + { + return 2; + } + + QgsFeature feature; + QgsAttributeMap featureAttributes; + const QgsFieldMap& fields = provider->fields(); + + mWithGeom = true; + QgsAttributeList attrIndexes = provider->attributeIndexes(); + + QDomNodeList queryChildNodes = queryElem.childNodes(); + if ( queryChildNodes.size() ) + { + mWithGeom = false; + QStringList::const_iterator alstIt; + QList idxList; + QMap fieldMap = provider->fieldNameMap(); + QMap::const_iterator fieldIt; + QString fieldName; + QDomElement propertyElem; + for ( int q = 0; q < queryChildNodes.size(); q++ ) + { + QDomElement queryChildElem = queryChildNodes.at( q ).toElement(); + if ( queryChildElem.tagName() == "PropertyName" ) + { + fieldName = queryChildElem.text(); + if ( fieldName.contains( ":" ) ) + { + fieldName = fieldName.section( ":", 1, 1 ); + } + fieldIt = fieldMap.find( fieldName ); + if ( fieldIt != fieldMap.end() ) + { + idxList.append( fieldIt.value() ); + } + else if ( fieldName == "geometry" ) + { + mWithGeom = true; + } + } + } + if ( idxList.size() > 0 || mWithGeom ) + { + attrIndexes = idxList; + } + else + { + mWithGeom = true; + } + } + + //map extent + QgsRectangle searchRect = layer->extent(); + searchRect.set( searchRect.xMinimum() - 0.000001 + , searchRect.yMinimum() - 0.000001 + , searchRect.xMaximum() + 0.000001 + , searchRect.yMaximum() + 0.000001 ); + QgsCoordinateReferenceSystem layerCrs = layer->crs(); + + if ( maxFeatures == 0 ) + maxFeat += layer->featureCount(); + + provider->select( attrIndexes, searchRect, mWithGeom, true ); + + if ( i == 0 ) + startGetFeature( request, format, layerCrs, &searchRect ); + + long featCounter = 0; + QDomNodeList filterNodes = queryElem.elementsByTagName( "Filter" ); + if (filterNodes.size() > 0 ) + { + QDomElement filterElem = filterNodes.at( 0 ).toElement(); + QDomNodeList fidNodes = filterElem.elementsByTagName( "FeatureId" ); + if ( fidNodes.size() > 0 ) + { + QDomElement fidElem; + for ( int f = 0; f < fidNodes.size(); f++ ) + { + fidElem = fidNodes.at( f ).toElement(); + provider->featureAtId( fidElem.attribute( "fid" ).toInt(), feature, mWithGeom, attrIndexes ); + sendGetFeature( request, format, &feature, featCounter, layerCrs, fields, layerExcludedAttributes ); + ++featCounter; + ++featureCounter; + } + } + else + { + QgsFilter* mFilter = QgsFilter::createFilterFromXml( filterElem.firstChild().toElement(), layer ); + if ( mFilter ) + { + while ( provider->nextFeature( feature ) && featureCounter < maxFeat ) + { + if ( mFilter->evaluate( feature ) ) + { + sendGetFeature( request, format, &feature, featCounter, layerCrs, fields, layerExcludedAttributes ); + ++featCounter; + ++featureCounter; + } + } + } + } + } + else + { + while ( provider->nextFeature( feature ) && featureCounter < maxFeat ) + { + sendGetFeature( request, format, &feature, featCounter, layerCrs, fields, layerExcludedAttributes ); + ++featCounter; + ++featureCounter; + } + } + } + + } + + endGetFeature( request, format ); + return 0; + + } + //read TYPENAME QMap::const_iterator type_name_it = mParameterMap.find( "TYPENAME" ); if ( type_name_it != mParameterMap.end() ) @@ -303,11 +483,6 @@ int QgsWFSServer::getFeature( QgsRequestHandler& request, const QString& format return 1; } - QStringList wfsLayersId = mConfigParser->wfsLayers(); - - QList layerList; - QgsMapLayer* currentLayer = 0; - layerList = mConfigParser->mapLayerFromStyle( mTypeName, "" ); currentLayer = layerList.at( 0 ); @@ -1069,12 +1244,17 @@ QgsFeatureIds QgsWFSServer::getFeatureIdsFromFilter( QDomElement filter, QgsVect QgsFeatureIds fids; QgsVectorDataProvider* provider = layer->dataProvider(); - QDomElement filterFirstElem = filter.firstChild().toElement(); + QDomNodeList fidNodes = filter.elementsByTagName( "FeatureId" ); - if ( filterFirstElem.localName() == "FeatureId" ) + if ( fidNodes.size() != 0 ) { + QDomElement fidElem; bool conversionSuccess; - fids.insert( filterFirstElem.attribute( "fid" ).toInt( &conversionSuccess ) ); + for ( int i = 0; i < fidNodes.size(); ++i ) + { + fidElem = fidNodes.at( i ).toElement(); + fids.insert( fidElem.attribute( "fid" ).toInt( &conversionSuccess ) ); + } } else {