Skip to content

Commit

Permalink
Allow setting the invalid geometry callback in processing context
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Apr 26, 2017
1 parent b32b395 commit e6a33e8
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 4 deletions.
23 changes: 23 additions & 0 deletions python/core/processing/qgsprocessingcontext.sip
Expand Up @@ -85,6 +85,29 @@ class QgsProcessingContext
\see invalidGeometryCheck()
%End


void setInvalidGeometryCallback( SIP_PYCALLABLE / AllowNone / );
%Docstring
Sets a callback function to use when encountering an invalid geometry and
invalidGeometryCheck() is set to GeometryAbortOnInvalid. This function will be
called using the feature with invalid geometry as a parameter.
.. versionadded:: 3.0
\see invalidGeometryCallback()
%End
%MethodCode
Py_BEGIN_ALLOW_THREADS

sipCpp->setInvalidGeometryCallback( [a0]( const QgsFeature &arg )
{
SIP_BLOCK_THREADS
Py_XDECREF( sipCallMethod( NULL, a0, "D", &arg, sipType_QgsFeature, NULL ) );
SIP_UNBLOCK_THREADS
} );

Py_END_ALLOW_THREADS
%End


};
QFlags<QgsProcessingContext::Flag> operator|(QgsProcessingContext::Flag f1, QFlags<QgsProcessingContext::Flag> f2);

Expand Down
36 changes: 36 additions & 0 deletions src/core/processing/qgsprocessingcontext.h
Expand Up @@ -93,12 +93,48 @@ class CORE_EXPORT QgsProcessingContext
*/
void setInvalidGeometryCheck( const QgsFeatureRequest::InvalidGeometryCheck &check ) { mInvalidGeometryCheck = check; }


/**
* Sets a callback function to use when encountering an invalid geometry and
* invalidGeometryCheck() is set to GeometryAbortOnInvalid. This function will be
* called using the feature with invalid geometry as a parameter.
* \since QGIS 3.0
* \see invalidGeometryCallback()
*/
#ifndef SIP_RUN
void setInvalidGeometryCallback( std::function< void( const QgsFeature & ) > callback ) { mInvalidGeometryCallback = callback; }
#else
void setInvalidGeometryCallback( SIP_PYCALLABLE / AllowNone / );
% MethodCode
Py_BEGIN_ALLOW_THREADS

sipCpp->setInvalidGeometryCallback( [a0]( const QgsFeature &arg )
{
SIP_BLOCK_THREADS
Py_XDECREF( sipCallMethod( NULL, a0, "D", &arg, sipType_QgsFeature, NULL ) );
SIP_UNBLOCK_THREADS
} );

Py_END_ALLOW_THREADS
% End
#endif

/**
* Returns the callback function to use when encountering an invalid geometry and
* invalidGeometryCheck() is set to GeometryAbortOnInvalid.
* \since QGIS 3.0
* \note not available in Python bindings
* \see setInvalidGeometryCallback()
*/
SIP_SKIP std::function< void( const QgsFeature & ) > invalidGeometryCallback() const { return mInvalidGeometryCallback; }

private:

QgsProcessingContext::Flags mFlags = 0;
QPointer< QgsProject > mProject;
QgsExpressionContext mExpressionContext;
QgsFeatureRequest::InvalidGeometryCheck mInvalidGeometryCheck = QgsFeatureRequest::GeometryNoCheck;
std::function< void( const QgsFeature & ) > mInvalidGeometryCallback;

};
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsProcessingContext::Flags )
Expand Down
1 change: 1 addition & 0 deletions src/core/processing/qgsprocessingutils.cpp
Expand Up @@ -179,6 +179,7 @@ QgsFeatureIterator QgsProcessingUtils::getFeatures( QgsVectorLayer *layer, const

QgsFeatureRequest req( request );
req.setInvalidGeometryCheck( context.invalidGeometryCheck() );
req.setInvalidGeometryCallback( context.invalidGeometryCallback() );
if ( useSelection )
{
return layer->selectedFeaturesIterator( req );
Expand Down
27 changes: 23 additions & 4 deletions tests/src/core/testqgsprocessing.cpp
Expand Up @@ -501,12 +501,31 @@ void TestQgsProcessing::features()
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
// test callback is hit when filtering invalid geoms
bool encountered = false;
std::function< void( const QgsFeature & ) > callback = [ &encountered ]( const QgsFeature & )
{
encountered = true;
};

context.setFlags( QgsProcessingContext::Flags( 0 ) );
context.setInvalidGeometryCheck( QgsFeatureRequest::GeometryAbortOnInvalid );
context.setInvalidGeometryCallback( callback );
QgsVectorLayer *polyLayer = new QgsVectorLayer( "Polygon", "v2", "memory" );
QgsFeature f;
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((0 0, 1 0, 0 1, 1 1, 0 0))" ) ) );
polyLayer->dataProvider()->addFeatures( QgsFeatureList() << f );

ids = getIds( QgsProcessingUtils::getFeatures( polyLayer, context ) );
QVERIFY( encountered );

encountered = false;
context.setInvalidGeometryCheck( QgsFeatureRequest::GeometryNoCheck );
ids = getIds( QgsProcessingUtils::getFeatures( polyLayer, context ) );
QVERIFY( !encountered );

delete layer;
delete polyLayer;
}

QGSTEST_MAIN( TestQgsProcessing )
Expand Down

0 comments on commit e6a33e8

Please sign in to comment.