Skip to content

Commit

Permalink
Add methods for getting vector iterators to QgsProcessingUtils
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Apr 26, 2017
1 parent 8cf1617 commit b32b395
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 0 deletions.
18 changes: 18 additions & 0 deletions python/core/processing/qgsprocessingutils.sip
Expand Up @@ -97,6 +97,24 @@ class QgsProcessingUtils
:rtype: str
%End

static QgsFeatureIterator getFeatures( QgsVectorLayer *layer, const QgsProcessingContext &context, const QgsFeatureRequest &request = QgsFeatureRequest() );
%Docstring
Returns an iterator for the features in a ``layer``, respecting
the settings from the supplied ``context``.
An optional base ``request`` can be used to optimise the returned
iterator, eg by restricting the returned attributes or geometry.
:rtype: QgsFeatureIterator
%End

static long featureCount( QgsVectorLayer *layer, const QgsProcessingContext &context );
%Docstring
Returns an approximate feature count for a ``layer``, when
the settings from the supplied ``context`` are respected. E.g. if the
context is set to only use selected features, then calling this will
return the count of selected features in the layer.
:rtype: long
%End

};


Expand Down
26 changes: 26 additions & 0 deletions src/core/processing/qgsprocessingutils.cpp
Expand Up @@ -18,6 +18,7 @@
#include "qgsprocessingutils.h"
#include "qgsproject.h"
#include "qgssettings.h"
#include "qgsprocessingcontext.h"

QList<QgsRasterLayer *> QgsProcessingUtils::compatibleRasterLayers( QgsProject *project, bool sort )
{
Expand Down Expand Up @@ -172,3 +173,28 @@ QString QgsProcessingUtils::normalizeLayerSource( const QString &source )
return normalized.trimmed();
}

QgsFeatureIterator QgsProcessingUtils::getFeatures( QgsVectorLayer *layer, const QgsProcessingContext &context, const QgsFeatureRequest &request )
{
bool useSelection = context.flags() & QgsProcessingContext::UseSelection && layer->selectedFeatureCount() > 0;

QgsFeatureRequest req( request );
req.setInvalidGeometryCheck( context.invalidGeometryCheck() );
if ( useSelection )
{
return layer->selectedFeaturesIterator( req );
}
else
{
return layer->getFeatures( req );
}
}

long QgsProcessingUtils::featureCount( QgsVectorLayer *layer, const QgsProcessingContext &context )
{
bool useSelection = context.flags() & QgsProcessingContext::UseSelection && layer->selectedFeatureCount() > 0;
if ( useSelection )
return layer->selectedFeatureCount();
else
return layer->featureCount();
}

17 changes: 17 additions & 0 deletions src/core/processing/qgsprocessingutils.h
Expand Up @@ -24,6 +24,7 @@
#include "qgsvectorlayer.h"

class QgsProject;
class QgsProcessingContext;

#include <QString>

Expand Down Expand Up @@ -105,6 +106,22 @@ class CORE_EXPORT QgsProcessingUtils
*/
static QString normalizeLayerSource( const QString &source );

/**
* Returns an iterator for the features in a \a layer, respecting
* the settings from the supplied \a context.
* An optional base \a request can be used to optimise the returned
* iterator, eg by restricting the returned attributes or geometry.
*/
static QgsFeatureIterator getFeatures( QgsVectorLayer *layer, const QgsProcessingContext &context, const QgsFeatureRequest &request = QgsFeatureRequest() );

/**
* Returns an approximate feature count for a \a layer, when
* the settings from the supplied \a context are respected. E.g. if the
* context is set to only use selected features, then calling this will
* return the count of selected features in the layer.
*/
static long featureCount( QgsVectorLayer *layer, const QgsProcessingContext &context );

private:

static bool canUseLayer( const QgsRasterLayer *layer );
Expand Down
79 changes: 79 additions & 0 deletions tests/src/core/testqgsprocessing.cpp
Expand Up @@ -19,11 +19,14 @@
#include "qgsprocessingprovider.h"
#include "qgsprocessingutils.h"
#include "qgsprocessingalgorithm.h"
#include "qgsprocessingcontext.h"
#include <QObject>
#include <QtTest/QSignalSpy>
#include "qgstest.h"
#include "qgsrasterlayer.h"
#include "qgsproject.h"
#include "qgspointv2.h"
#include "qgsgeometry.h"

class DummyAlgorithm : public QgsProcessingAlgorithm
{
Expand Down Expand Up @@ -102,6 +105,7 @@ class TestQgsProcessing: public QObject
void normalizeLayerSource();
void mapLayerFromString();
void algorithm();
void features();

private:

Expand Down Expand Up @@ -430,5 +434,80 @@ void TestQgsProcessing::algorithm()
QCOMPARE( p3->algorithms().size(), 2 );
}

void TestQgsProcessing::features()
{
QgsVectorLayer *layer = new QgsVectorLayer( "Point", "v1", "memory" );
for ( int i = 1; i < 6; ++i )
{
QgsFeature f( i );
f.setGeometry( QgsGeometry( new QgsPointV2( 1, 2 ) ) );
layer->dataProvider()->addFeatures( QgsFeatureList() << f );
}

QgsProcessingContext context;
// disable check for geometry validity
context.setFlags( QgsProcessingContext::Flags( 0 ) );

std::function< QgsFeatureIds( QgsFeatureIterator it ) > getIds = []( QgsFeatureIterator it )
{
QgsFeature f;
QgsFeatureIds ids;
while ( it.nextFeature( f ) )
{
ids << f.id();
}
return ids;
};

// test with all features
QgsFeatureIds ids = getIds( QgsProcessingUtils::getFeatures( layer, context ) );
QCOMPARE( ids, QgsFeatureIds() << 1 << 2 << 3 << 4 << 5 );
QCOMPARE( QgsProcessingUtils::featureCount( layer, context ), 5L );

// test with selected features
context.setFlags( QgsProcessingContext::UseSelection );
layer->selectByIds( QgsFeatureIds() << 2 << 4 );
ids = getIds( QgsProcessingUtils::getFeatures( layer, context ) );
QCOMPARE( ids, QgsFeatureIds() << 2 << 4 );
QCOMPARE( QgsProcessingUtils::featureCount( layer, context ), 2L );

// selection, but not using selected features
context.setFlags( QgsProcessingContext::Flags( 0 ) );
layer->selectByIds( QgsFeatureIds() << 2 << 4 );
ids = getIds( QgsProcessingUtils::getFeatures( layer, context ) );
QCOMPARE( ids, QgsFeatureIds() << 1 << 2 << 3 << 4 << 5 );
QCOMPARE( QgsProcessingUtils::featureCount( layer, context ), 5L );

// using selected features, but no selection
context.setFlags( QgsProcessingContext::UseSelection );
layer->removeSelection();
ids = getIds( QgsProcessingUtils::getFeatures( layer, context ) );
QCOMPARE( ids, QgsFeatureIds() << 1 << 2 << 3 << 4 << 5 );
QCOMPARE( QgsProcessingUtils::featureCount( layer, context ), 5L );


// test that feature request is honored
context.setFlags( QgsProcessingContext::Flags( 0 ) );
ids = getIds( QgsProcessingUtils::getFeatures( layer, context, QgsFeatureRequest().setFilterFids( QgsFeatureIds() << 1 << 3 << 5 ) ) );
QCOMPARE( ids, QgsFeatureIds() << 1 << 3 << 5 );

// count is only rough - but we expect (for now) to see full layer count
QCOMPARE( QgsProcessingUtils::featureCount( layer, context ), 5L );


//test that feature request is honored when using selections
context.setFlags( QgsProcessingContext::UseSelection );
layer->selectByIds( QgsFeatureIds() << 2 << 4 );
ids = getIds( QgsProcessingUtils::getFeatures( layer, context, QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ) ) );
QCOMPARE( ids, QgsFeatureIds() << 2 << 4 );

#if 0
// test exception is raised when filtering invalid geoms
context.setInvalidGeometryCheck( QgsFeatureRequest.GeometryAbortOnInvalid )
#endif

delete layer;
}

QGSTEST_MAIN( TestQgsProcessing )
#include "testqgsprocessing.moc"

0 comments on commit b32b395

Please sign in to comment.