Remove QgsFeatureRequest::FilterRect from providers/iterators

This commit is contained in:
Matthias Kuhn 2015-07-14 15:35:08 +02:00
parent 75712643cd
commit 5be0ee734f
21 changed files with 174 additions and 104 deletions

View File

@ -801,7 +801,12 @@ class QgsVectorLayer : QgsMapLayer
/** Return the extent of the layer as a QRect */
QgsRectangle extent();
/** Returns field list in the to-be-committed state */
/**
* Returns the list of fields of this layer.
* This also includes fields which have not yet been saved to the provider.
*
* @return A list of fields
*/
const QgsFields &pendingFields() const;
/** Returns list of attributes */

View File

@ -2220,11 +2220,6 @@ bool QgsVectorLayer::deleteFeature( QgsFeatureId fid )
return res;
}
const QgsFields &QgsVectorLayer::pendingFields() const
{
return mUpdatedFields;
}
QgsAttributeList QgsVectorLayer::pendingAllAttributesList()
{
return mUpdatedFields.allAttributesList();

View File

@ -1300,8 +1300,13 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
/** Return the extent of the layer as a QRect */
QgsRectangle extent() override;
/** Returns field list in the to-be-committed state */
const QgsFields &pendingFields() const;
/**
* Returns the list of fields of this layer.
* This also includes fields which have not yet been saved to the provider.
*
* @return A list of fields
*/
const QgsFields &pendingFields() const { return mUpdatedFields; }
/** Returns list of attributes */
QgsAttributeList pendingAllAttributesList();

View File

@ -176,7 +176,7 @@ bool QgsVectorLayerFeatureIterator::fetchFeature( QgsFeature& f )
return res;
}
if ( mRequest.filterType() == QgsFeatureRequest::FilterRect )
if ( !mRequest.filterRect().isNull() )
{
if ( fetchNextChangedGeomFeature( f ) )
return true;
@ -352,6 +352,10 @@ bool QgsVectorLayerFeatureIterator::fetchNextChangedAttributeFeature( QgsFeature
{
while ( mChangedFeaturesIterator.nextFeature( f ) )
{
if ( mFetchConsidered.contains( f.id() ) )
// skip deleted features and those already handled by the geometry
continue;
mFetchConsidered << f.id();
updateChangedAttributes( f );
@ -359,14 +363,7 @@ bool QgsVectorLayerFeatureIterator::fetchNextChangedAttributeFeature( QgsFeature
if ( mHasVirtualAttributes )
addVirtualAttributes( f );
if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression )
{
if ( mRequest.filterExpression()->evaluate( &f ).toBool() )
{
return true;
}
}
else
if ( mRequest.filterExpression()->evaluate( &f ).toBool() )
{
return true;
}

View File

@ -136,6 +136,7 @@ bool QgsVectorLayerRenderer::render()
QgsRenderOptions opts = mRendererV2->startRender( mContext, mFields );
QgsDebugMsg( opts.whereClause() );
QgsRectangle requestExtent = mContext.extent();
mRendererV2->modifyRequestExtent( requestExtent, mContext );

View File

@ -44,21 +44,8 @@ QgsDelimitedTextFeatureIterator::QgsDelimitedTextFeatureIterator( QgsDelimitedTe
mTestGeometry = false;
mMode = FileScan;
if ( request.filterType() == QgsFeatureRequest::FilterFid )
{
QgsDebugMsg( "Configuring for returning single id" );
mFeatureIds.append( request.filterFid() );
mMode = FeatureIds;
mTestSubset = false;
}
// If have geometry and testing geometry then evaluate options...
// If we don't have geometry then all records pass geometry filter.
// CC: 2013-05-09
// Not sure about intended relationship between filtering on geometry and
// requesting no geometry? Have preserved current logic of ignoring spatial filter
// if not requesting geometry.
else if ( request.filterType() == QgsFeatureRequest::FilterRect && hasGeometry )
if ( !request.filterRect().isNull() && hasGeometry )
{
QgsDebugMsg( "Configuring for rectangle select" );
mTestGeometry = true;
@ -98,13 +85,32 @@ QgsDelimitedTextFeatureIterator::QgsDelimitedTextFeatureIterator( QgsDelimitedTe
}
}
// If we have a subset index then use it..
if ( mMode == FileScan && mSource->mUseSubsetIndex )
if ( request.filterType() == QgsFeatureRequest::FilterFid )
{
QgsDebugMsg( QString( "Layer has subset index - use %1 items from subset index" ).arg( mSource->mSubsetIndex.size() ) );
QgsDebugMsg( "Configuring for returning single id" );
if ( request.filterRect().isNull() || ( !request.filterRect().isNull() && mFeatureIds.contains( request.filterFid() ) ) )
{
mFeatureIds = QList<QgsFeatureId>() << request.filterFid();
}
mMode = FeatureIds;
mTestSubset = false;
mMode = SubsetIndex;
}
// If have geometry and testing geometry then evaluate options...
// If we don't have geometry then all records pass geometry filter.
// CC: 2013-05-09
// Not sure about intended relationship between filtering on geometry and
// requesting no geometry? Have preserved current logic of ignoring spatial filter
// if not requesting geometry.
else
// If we have a subset index then use it..
if ( mMode == FileScan && mSource->mUseSubsetIndex )
{
QgsDebugMsg( QString( "Layer has subset index - use %1 items from subset index" ).arg( mSource->mSubsetIndex.size() ) );
mTestSubset = false;
mMode = SubsetIndex;
}
// Otherwise just have to scan the file
if ( mMode == FileScan )

View File

@ -176,7 +176,7 @@ bool QgsGPXFeatureIterator::readFid( QgsFeature& feature )
bool QgsGPXFeatureIterator::readWaypoint( const QgsWaypoint& wpt, QgsFeature& feature )
{
if ( mRequest.filterType() == QgsFeatureRequest::FilterRect )
if ( !mRequest.filterRect().isNull() )
{
const QgsRectangle& rect = mRequest.filterRect();
if ( ! rect.contains( QgsPoint( wpt.lon, wpt.lat ) ) )
@ -206,7 +206,7 @@ bool QgsGPXFeatureIterator::readRoute( const QgsRoute& rte, QgsFeature& feature
QgsGeometry* theGeometry = readRouteGeometry( rte );
if ( mRequest.filterType() == QgsFeatureRequest::FilterRect )
if ( !mRequest.filterRect().isNull() )
{
const QgsRectangle& rect = mRequest.filterRect();
if (( rte.xMax < rect.xMinimum() ) || ( rte.xMin > rect.xMaximum() ) ||
@ -248,7 +248,7 @@ bool QgsGPXFeatureIterator::readTrack( const QgsTrack& trk, QgsFeature& feature
QgsGeometry* theGeometry = readTrackGeometry( trk );
if ( mRequest.filterType() == QgsFeatureRequest::FilterRect )
if ( !mRequest.filterRect().isNull() )
{
const QgsRectangle& rect = mRequest.filterRect();
if (( trk.xMax < rect.xMinimum() ) || ( trk.xMin > rect.xMaximum() ) ||

View File

@ -80,7 +80,7 @@ QgsGrassFeatureIterator::QgsGrassFeatureIterator( QgsGrassFeatureSource* source,
allocateSelection( mSource->mMap );
resetSelection( 1 );
if ( request.filterType() == QgsFeatureRequest::FilterRect )
if ( !request.filterRect().isNull() )
{
setSelectionRect( request.filterRect(), request.flags() & QgsFeatureRequest::ExactIntersect );
}

View File

@ -33,14 +33,14 @@ QgsMemoryFeatureIterator::QgsMemoryFeatureIterator( QgsMemoryFeatureSource* sour
mSubsetExpression->prepare( mSource->mFields );
}
if ( mRequest.filterType() == QgsFeatureRequest::FilterRect && mRequest.flags() & QgsFeatureRequest::ExactIntersect )
if ( !mRequest.filterRect().isNull() && mRequest.flags() & QgsFeatureRequest::ExactIntersect )
{
mSelectRectGeom = QgsGeometry::fromRect( request.filterRect() );
}
// if there's spatial index, use it!
// (but don't use it when selection rect is not specified)
if ( mRequest.filterType() == QgsFeatureRequest::FilterRect && mSource->mSpatialIndex )
if ( !mRequest.filterRect().isNull() && mSource->mSpatialIndex )
{
mUsingFeatureIdList = true;
mFeatureIdList = mSource->mSpatialIndex->intersects( mRequest.filterRect() );
@ -90,7 +90,7 @@ bool QgsMemoryFeatureIterator::nextFeatureUsingList( QgsFeature& feature )
// option 1: we have a list of features to traverse
while ( mFeatureIdListIterator != mFeatureIdList.constEnd() )
{
if ( mRequest.filterType() == QgsFeatureRequest::FilterRect && mRequest.flags() & QgsFeatureRequest::ExactIntersect )
if ( !mRequest.filterRect().isNull() && mRequest.flags() & QgsFeatureRequest::ExactIntersect )
{
// do exact check in case we're doing intersection
if ( mSource->mFeatures[*mFeatureIdListIterator].geometry() && mSource->mFeatures[*mFeatureIdListIterator].geometry()->intersects( mSelectRectGeom ) )
@ -131,7 +131,7 @@ bool QgsMemoryFeatureIterator::nextFeatureTraverseAll( QgsFeature& feature )
// option 2: traversing the whole layer
while ( mSelectIterator != mSource->mFeatures.constEnd() )
{
if ( mRequest.filterType() != QgsFeatureRequest::FilterRect )
if ( mRequest.filterRect().isNull() )
{
// selection rect empty => using all features
hasFeature = true;

View File

@ -89,7 +89,7 @@ void QgsMssqlFeatureIterator::BuildStatement( const QgsFeatureRequest& request )
bool filterAdded = false;
// set spatial filter
if ( request.filterType() == QgsFeatureRequest::FilterRect && mSource->isSpatial() && !request.filterRect().isEmpty() )
if ( !request.filterRect().isNull() && mSource->isSpatial() && !request.filterRect().isEmpty() )
{
// polygons should be CCW for SqlGeography
QString r;

View File

@ -58,20 +58,20 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool
mSubsetStringSet = true;
}
mFetchGeometry = ( mRequest.filterType() == QgsFeatureRequest::FilterRect ) || !( mRequest.flags() & QgsFeatureRequest::NoGeometry );
mFetchGeometry = ( !mRequest.filterRect().isNull() ) || !( mRequest.flags() & QgsFeatureRequest::NoGeometry );
QgsAttributeList attrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList();
// make sure we fetch just relevant fields
// unless it's a VRT data source filtered by geometry as we don't know which
// attributes make up the geometry and OGR won't fetch them to evaluate the
// filter if we choose to ignore them (fixes #11223)
if (( mSource->mDriverName != "VRT" && mSource->mDriverName != "OGR_VRT" ) || mRequest.filterType() != QgsFeatureRequest::FilterRect )
if (( mSource->mDriverName != "VRT" && mSource->mDriverName != "OGR_VRT" ) || mRequest.filterRect().isNull() )
{
QgsOgrUtils::setRelevantFields( ogrLayer, mSource->mFields.count(), mFetchGeometry, attrs );
}
// spatial query to select features
if ( mRequest.filterType() == QgsFeatureRequest::FilterRect )
if ( !mRequest.filterRect().isNull() )
{
const QgsRectangle& rect = mRequest.filterRect();
@ -179,7 +179,7 @@ bool QgsOgrFeatureIterator::fetchFeature( QgsFeature& feature )
if ( !readFeature( fet, feature ) )
continue;
if ( mRequest.filterType() == QgsFeatureRequest::FilterRect && !feature.constGeometry() )
if ( !mRequest.filterRect().isNull() && !feature.constGeometry() )
continue;
// we have a feature, end this cycle

View File

@ -46,45 +46,49 @@ QgsOracleFeatureIterator::QgsOracleFeatureIterator( QgsOracleFeatureSource* sour
QString whereClause;
if ( !mRequest.filterRect().isNull && !mSource->mGeometryColumn.isNull() && mSource->mHasSpatialIndex )
{
QgsRectangle rect( mRequest.filterRect() );
QString bbox = QString( "mdsys.sdo_geometry(2003,%1,NULL,"
"mdsys.sdo_elem_info_array(1,1003,3),"
"mdsys.sdo_ordinate_array(%2,%3,%4,%5)"
")" )
.arg( mSource->mSrid < 1 ? "NULL" : QString::number( mSource->mSrid ) )
.arg( qgsDoubleToString( rect.xMinimum() ) )
.arg( qgsDoubleToString( rect.yMinimum() ) )
.arg( qgsDoubleToString( rect.xMaximum() ) )
.arg( qgsDoubleToString( rect.yMaximum() ) );
whereClause = QString( "sdo_filter(%1,%2)='TRUE'" ).arg( QgsOracleProvider::quotedIdentifier( mSource->mGeometryColumn ) ).arg( bbox );
if ( mRequest.flags() & QgsFeatureRequest::ExactIntersect && mConnection->hasSpatial() )
{
whereClause += QString( " AND sdo_relate(%1,%2,'mask=ANYINTERACT')='TRUE'" )
.arg( QgsOracleProvider::quotedIdentifier( mSource->mGeometryColumn ) )
.arg( bbox );
}
}
switch ( request.filterType() )
{
case QgsFeatureRequest::FilterFid:
QString fidWhereClause = QgsOracleUtils::whereClause( request.filterFid(), mSource->mFields, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared );
whereClause = QgsPostgresUtils::andWhereClauses( whereClause, fidWhereClause );
break;
case QgsFeatureRequest::FilterFids:
QString fidsWhereClause = QgsOracleUtils::whereClause( request.filterFids(), mSource->mFields, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared );
whereClause = QgsPostgresUtils::andWhereClauses( whereClause, fidsWhereClause );
break;
case QgsFeatureRequest::FilterNone:
break;
case QgsFeatureRequest::FilterExpression:
break;
case QgsFeatureRequest::FilterRect:
if ( !mSource->mGeometryColumn.isNull() && mSource->mHasSpatialIndex )
{
QgsRectangle rect( mRequest.filterRect() );
QString bbox = QString( "mdsys.sdo_geometry(2003,%1,NULL,"
"mdsys.sdo_elem_info_array(1,1003,3),"
"mdsys.sdo_ordinate_array(%2,%3,%4,%5)"
")" )
.arg( mSource->mSrid < 1 ? "NULL" : QString::number( mSource->mSrid ) )
.arg( qgsDoubleToString( rect.xMinimum() ) )
.arg( qgsDoubleToString( rect.yMinimum() ) )
.arg( qgsDoubleToString( rect.xMaximum() ) )
.arg( qgsDoubleToString( rect.yMaximum() ) );
whereClause = QString( "sdo_filter(%1,%2)='TRUE'" ).arg( QgsOracleProvider::quotedIdentifier( mSource->mGeometryColumn ) ).arg( bbox );
if ( mRequest.flags() & QgsFeatureRequest::ExactIntersect && mConnection->hasSpatial() )
{
whereClause += QString( " AND sdo_relate(%1,%2,'mask=ANYINTERACT')='TRUE'" )
.arg( QgsOracleProvider::quotedIdentifier( mSource->mGeometryColumn ) )
.arg( bbox );
}
}
break;
case QgsFeatureRequest::FilterFid:
whereClause = QgsOracleUtils::whereClause( request.filterFid(), mSource->mFields, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared );
break;
case QgsFeatureRequest::FilterFids:
whereClause = QgsOracleUtils::whereClause( request.filterFids(), mSource->mFields, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared );
break;
case QgsFeatureRequest::FilterNone:
// Handled in the if-statement above
break;
}

View File

@ -464,6 +464,15 @@ QString QgsOracleUtils::whereClause( QgsFeatureIds featureIds, const QgsFields &
return whereClauses.isEmpty() ? "" : whereClauses.join( " OR " ).prepend( "(" ).append( ")" );
}
QString QgsOracleUtils::andWhereClauses( const QString& c1, const QString& c2 )
{
if ( c1.isNull() )
return c2;
if ( c2.isNull() )
return c1;
return QString( "(%1) AND (%2)" ).arg( c1 ).arg( c2 );
}
QString QgsOracleProvider::whereClause( QgsFeatureId featureId ) const
{

View File

@ -423,6 +423,8 @@ class QgsOracleUtils
QgsOraclePrimaryKeyType primaryKeyType,
const QList<int>& primaryKeyAttrs,
QSharedPointer<QgsOracleSharedData> sharedData );
static QString andWhereClauses( const QString& c1, const QString& c2 );
};

View File

@ -184,9 +184,32 @@ QgsPostgresExpressionCompiler::Result QgsPostgresExpressionCompiler::compile( co
return Complete;
}
case QgsExpression::ntInOperator:
{
const QgsExpression::NodeInOperator* n = static_cast<const QgsExpression::NodeInOperator*>( node );
QStringList list;
Q_FOREACH ( const QgsExpression::Node* ln, n->list()->list() )
{
QString s;
Result r = compile( ln, s );
if ( r == Complete )
list << s;
else
return r;
}
QString nd;
Result rn = compile( n->node(), nd );
if ( rn != Complete )
return rn;
result = QString( "%1 %2IN(%3)" ).arg( nd ).arg( n->isNotIn() ? "NOT " : "" ).arg( list.join( "," ) );
return Complete;
}
case QgsExpression::ntFunction:
case QgsExpression::ntCondition:
case QgsExpression::ntInOperator:
break;
}

View File

@ -58,17 +58,22 @@ QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource
mCursorName = mConn->uniqueCursorName();
QString whereClause;
if ( request.filterType() == QgsFeatureRequest::FilterRect && !mSource->mGeometryColumn.isNull() )
if ( !request.filterRect().isNull() && !mSource->mGeometryColumn.isNull() )
{
whereClause = whereClauseRect();
}
else if ( request.filterType() == QgsFeatureRequest::FilterFid )
if ( request.filterType() == QgsFeatureRequest::FilterFid )
{
whereClause = QgsPostgresUtils::whereClause( mRequest.filterFid(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared );
QString fidWhereClause = QgsPostgresUtils::whereClause( mRequest.filterFid(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared );
whereClause = QgsPostgresUtils::andWhereClauses( whereClause, fidWhereClause );
}
else if ( request.filterType() == QgsFeatureRequest::FilterFids )
{
whereClause = QgsPostgresUtils::whereClause( mRequest.filterFids(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared );
QString fidsWhereClause = QgsPostgresUtils::whereClause( mRequest.filterFids(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared );
whereClause = QgsPostgresUtils::andWhereClauses( whereClause, fidsWhereClause );
}
else if ( request.filterType() == QgsFeatureRequest::FilterExpression
&& QSettings().value( "/qgis/postgres/compileExpressions", false ).toBool() )
@ -77,7 +82,7 @@ QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource
if ( compiler.compile( request.filterExpression() ) == QgsPostgresExpressionCompiler::Complete )
{
whereClause = compiler.result();
whereClause = QgsPostgresUtils::andWhereClauses( whereClause, compiler.result() );
mExpressionCompiled = true;
}
}

View File

@ -548,6 +548,16 @@ QString QgsPostgresUtils::whereClause( QgsFeatureIds featureIds, const QgsFields
return whereClauses.isEmpty() ? "" : whereClauses.join( " OR " ).prepend( "(" ).append( ")" );
}
QString QgsPostgresUtils::andWhereClauses( const QString& c1, const QString& c2 )
{
if ( c1.isNull() )
return c2;
if ( c2.isNull() )
return c1;
return QString( "(%1) AND (%2)" ).arg( c1 ).arg( c2 );
}
QString QgsPostgresProvider::filterWhereClause() const
{
QString where;

View File

@ -501,6 +501,8 @@ class QgsPostgresUtils
QgsPostgresPrimaryKeyType pkType,
const QList<int>& pkAttrs,
QSharedPointer<QgsPostgresSharedData> sharedData );
static QString andWhereClauses( const QString& c1, const QString& c2 );
};
/** Data shared between provider class and its feature sources. Ideally there should

View File

@ -35,7 +35,7 @@ QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeature
mRowNumber = 0;
QString whereClause;
if ( request.filterType() == QgsFeatureRequest::FilterRect && !mSource->mGeometryColumn.isNull() )
if ( !request.filterRect().isNull() && !mSource->mGeometryColumn.isNull() )
{
// some kind of MBR spatial filtering is required
whereClause += whereClauseRect();
@ -45,8 +45,7 @@ QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeature
{
whereClause += whereClauseFid();
}
if ( request.filterType() == QgsFeatureRequest::FilterFids )
else if ( request.filterType() == QgsFeatureRequest::FilterFids )
{
whereClause += whereClauseFids();
}

View File

@ -21,20 +21,18 @@
QgsWFSFeatureIterator::QgsWFSFeatureIterator( QgsWFSFeatureSource* source, bool ownSource, const QgsFeatureRequest& request )
: QgsAbstractFeatureIteratorFromSource<QgsWFSFeatureSource>( source, ownSource, request )
{
switch ( request.filterType() )
if ( !request.filterRect().isNull() && mSource->mSpatialIndex )
{
case QgsFeatureRequest::FilterRect:
if ( mSource->mSpatialIndex )
{
mSelectedFeatures = mSource->mSpatialIndex->intersects( request.filterRect() );
}
break;
case QgsFeatureRequest::FilterFid:
mSelectedFeatures.push_back( request.filterFid() );
break;
case QgsFeatureRequest::FilterNone:
default:
mSelectedFeatures = mSource->mFeatures.keys();
mSelectedFeatures = mSource->mSpatialIndex->intersects( request.filterRect() );
}
if ( request.filterType() == QgsFeatureRequest::FilterFid )
{
mSelectedFeatures.push_back( request.filterFid() );
}
else
{
mSelectedFeatures = mSource->mFeatures.keys();
}
mFeatureIterator = mSelectedFeatures.constBegin();
@ -164,7 +162,7 @@ QgsWFSFeatureSource::~QgsWFSFeatureSource()
QgsFeatureIterator QgsWFSFeatureSource::getFeatures( const QgsFeatureRequest& request )
{
if ( request.filterType() == QgsFeatureRequest::FilterRect )
if ( !request.filterRect().isNull() )
emit extentRequested( request.filterRect() );
return QgsFeatureIterator( new QgsWFSFeatureIterator( this, false, request ) );
}

View File

@ -72,6 +72,15 @@ class ProviderTestCase(object):
features = [f['pk'] for f in self.provider.getFeatures(QgsFeatureRequest().setFilterRect(extent))]
assert set(features) == set([2L, 4L]), 'Got {} instead'.format(features)
def testRectAndExpression(self):
extent = QgsRectangle(-70, 67, -60, 80)
result = set([f['pk'] for f in self.provider.getFeatures(
QgsFeatureRequest()
.setFilterExpression('"cnt">200')
.setFilterRect(extent))])
expected=[4L]
assert set(expected) == result, 'Expected {} and got {} when testing for combination of filterRect and expression'.format(set(expected), result)
def testMinValue(self):
assert self.provider.minimumValue(1) == -200
assert self.provider.minimumValue(2) == 'Apple'