Skip to content

Commit e6a33e8

Browse files
committed
Allow setting the invalid geometry callback in processing context
1 parent b32b395 commit e6a33e8

File tree

4 files changed

+83
-4
lines changed

4 files changed

+83
-4
lines changed

python/core/processing/qgsprocessingcontext.sip

+23
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,29 @@ class QgsProcessingContext
8585
\see invalidGeometryCheck()
8686
%End
8787

88+
89+
void setInvalidGeometryCallback( SIP_PYCALLABLE / AllowNone / );
90+
%Docstring
91+
Sets a callback function to use when encountering an invalid geometry and
92+
invalidGeometryCheck() is set to GeometryAbortOnInvalid. This function will be
93+
called using the feature with invalid geometry as a parameter.
94+
.. versionadded:: 3.0
95+
\see invalidGeometryCallback()
96+
%End
97+
%MethodCode
98+
Py_BEGIN_ALLOW_THREADS
99+
100+
sipCpp->setInvalidGeometryCallback( [a0]( const QgsFeature &arg )
101+
{
102+
SIP_BLOCK_THREADS
103+
Py_XDECREF( sipCallMethod( NULL, a0, "D", &arg, sipType_QgsFeature, NULL ) );
104+
SIP_UNBLOCK_THREADS
105+
} );
106+
107+
Py_END_ALLOW_THREADS
108+
%End
109+
110+
88111
};
89112
QFlags<QgsProcessingContext::Flag> operator|(QgsProcessingContext::Flag f1, QFlags<QgsProcessingContext::Flag> f2);
90113

src/core/processing/qgsprocessingcontext.h

+36
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,48 @@ class CORE_EXPORT QgsProcessingContext
9393
*/
9494
void setInvalidGeometryCheck( const QgsFeatureRequest::InvalidGeometryCheck &check ) { mInvalidGeometryCheck = check; }
9595

96+
97+
/**
98+
* Sets a callback function to use when encountering an invalid geometry and
99+
* invalidGeometryCheck() is set to GeometryAbortOnInvalid. This function will be
100+
* called using the feature with invalid geometry as a parameter.
101+
* \since QGIS 3.0
102+
* \see invalidGeometryCallback()
103+
*/
104+
#ifndef SIP_RUN
105+
void setInvalidGeometryCallback( std::function< void( const QgsFeature & ) > callback ) { mInvalidGeometryCallback = callback; }
106+
#else
107+
void setInvalidGeometryCallback( SIP_PYCALLABLE / AllowNone / );
108+
% MethodCode
109+
Py_BEGIN_ALLOW_THREADS
110+
111+
sipCpp->setInvalidGeometryCallback( [a0]( const QgsFeature &arg )
112+
{
113+
SIP_BLOCK_THREADS
114+
Py_XDECREF( sipCallMethod( NULL, a0, "D", &arg, sipType_QgsFeature, NULL ) );
115+
SIP_UNBLOCK_THREADS
116+
} );
117+
118+
Py_END_ALLOW_THREADS
119+
% End
120+
#endif
121+
122+
/**
123+
* Returns the callback function to use when encountering an invalid geometry and
124+
* invalidGeometryCheck() is set to GeometryAbortOnInvalid.
125+
* \since QGIS 3.0
126+
* \note not available in Python bindings
127+
* \see setInvalidGeometryCallback()
128+
*/
129+
SIP_SKIP std::function< void( const QgsFeature & ) > invalidGeometryCallback() const { return mInvalidGeometryCallback; }
130+
96131
private:
97132

98133
QgsProcessingContext::Flags mFlags = 0;
99134
QPointer< QgsProject > mProject;
100135
QgsExpressionContext mExpressionContext;
101136
QgsFeatureRequest::InvalidGeometryCheck mInvalidGeometryCheck = QgsFeatureRequest::GeometryNoCheck;
137+
std::function< void( const QgsFeature & ) > mInvalidGeometryCallback;
102138

103139
};
104140
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsProcessingContext::Flags )

src/core/processing/qgsprocessingutils.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ QgsFeatureIterator QgsProcessingUtils::getFeatures( QgsVectorLayer *layer, const
179179

180180
QgsFeatureRequest req( request );
181181
req.setInvalidGeometryCheck( context.invalidGeometryCheck() );
182+
req.setInvalidGeometryCallback( context.invalidGeometryCallback() );
182183
if ( useSelection )
183184
{
184185
return layer->selectedFeaturesIterator( req );

tests/src/core/testqgsprocessing.cpp

+23-4
Original file line numberDiff line numberDiff line change
@@ -501,12 +501,31 @@ void TestQgsProcessing::features()
501501
ids = getIds( QgsProcessingUtils::getFeatures( layer, context, QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ) ) );
502502
QCOMPARE( ids, QgsFeatureIds() << 2 << 4 );
503503

504-
#if 0
505-
// test exception is raised when filtering invalid geoms
506-
context.setInvalidGeometryCheck( QgsFeatureRequest.GeometryAbortOnInvalid )
507-
#endif
504+
// test callback is hit when filtering invalid geoms
505+
bool encountered = false;
506+
std::function< void( const QgsFeature & ) > callback = [ &encountered ]( const QgsFeature & )
507+
{
508+
encountered = true;
509+
};
510+
511+
context.setFlags( QgsProcessingContext::Flags( 0 ) );
512+
context.setInvalidGeometryCheck( QgsFeatureRequest::GeometryAbortOnInvalid );
513+
context.setInvalidGeometryCallback( callback );
514+
QgsVectorLayer *polyLayer = new QgsVectorLayer( "Polygon", "v2", "memory" );
515+
QgsFeature f;
516+
f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((0 0, 1 0, 0 1, 1 1, 0 0))" ) ) );
517+
polyLayer->dataProvider()->addFeatures( QgsFeatureList() << f );
518+
519+
ids = getIds( QgsProcessingUtils::getFeatures( polyLayer, context ) );
520+
QVERIFY( encountered );
521+
522+
encountered = false;
523+
context.setInvalidGeometryCheck( QgsFeatureRequest::GeometryNoCheck );
524+
ids = getIds( QgsProcessingUtils::getFeatures( polyLayer, context ) );
525+
QVERIFY( !encountered );
508526

509527
delete layer;
528+
delete polyLayer;
510529
}
511530

512531
QGSTEST_MAIN( TestQgsProcessing )

0 commit comments

Comments
 (0)