mirror of
https://github.com/qgis/QGIS.git
synced 2025-06-19 00:02:48 -04:00
Add method to perform invalid geometry checking in QgsFeatureRequest
Allows requests to specify how invalid geometries should be handled. Default is to perform no geometry validity checking.
This commit is contained in:
parent
cd521d6f76
commit
80d07cb4b9
@ -26,6 +26,14 @@ class QgsFeatureRequest
|
||||
FilterFids //!< Filter using feature IDs
|
||||
};
|
||||
|
||||
//! Handling of features with invalid geometries
|
||||
enum InvalidGeometryCheck
|
||||
{
|
||||
GeometryNoCheck,
|
||||
GeometrySkipInvalid,
|
||||
GeometryAbortOnInvalid,
|
||||
};
|
||||
|
||||
/**
|
||||
* The OrderByClause class represents an order by clause for a QgsFeatureRequest.
|
||||
*
|
||||
@ -196,6 +204,22 @@ class QgsFeatureRequest
|
||||
//! Get feature IDs that should be fetched.
|
||||
const QgsFeatureIds& filterFids() const;
|
||||
|
||||
/**
|
||||
* Sets invalid geometry checking behavior.
|
||||
* \note Invalid geometry checking is not performed when retrieving features
|
||||
* directly from a QgsVectorDataProvider.
|
||||
* \see invalidGeometryCheck()
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
QgsFeatureRequest &setInvalidGeometryCheck( InvalidGeometryCheck check );
|
||||
|
||||
/**
|
||||
* Returns the invalid geometry checking behavior.
|
||||
* \see setInvalidGeometryCheck()
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
InvalidGeometryCheck invalidGeometryCheck() const;
|
||||
|
||||
/** Set the filter expression. {@see QgsExpression}
|
||||
* @param expression expression string
|
||||
* @see filterExpression
|
||||
|
@ -76,6 +76,7 @@ QgsFeatureRequest &QgsFeatureRequest::operator=( const QgsFeatureRequest &rh )
|
||||
{
|
||||
mFilterExpression.reset( nullptr );
|
||||
}
|
||||
mInvalidGeometryFilter = rh.mInvalidGeometryFilter;
|
||||
mExpressionContext = rh.mExpressionContext;
|
||||
mAttrs = rh.mAttrs;
|
||||
mSimplifyMethod = rh.mSimplifyMethod;
|
||||
@ -104,6 +105,12 @@ QgsFeatureRequest &QgsFeatureRequest::setFilterFids( const QgsFeatureIds &fids )
|
||||
return *this;
|
||||
}
|
||||
|
||||
QgsFeatureRequest &QgsFeatureRequest::setInvalidGeometryCheck( QgsFeatureRequest::InvalidGeometryCheck check )
|
||||
{
|
||||
mInvalidGeometryFilter = check;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QgsFeatureRequest &QgsFeatureRequest::setFilterExpression( const QString &expression )
|
||||
{
|
||||
mFilter = FilterExpression;
|
||||
|
@ -85,6 +85,14 @@ class CORE_EXPORT QgsFeatureRequest
|
||||
FilterFids //!< Filter using feature IDs
|
||||
};
|
||||
|
||||
//! Handling of features with invalid geometries
|
||||
enum InvalidGeometryCheck
|
||||
{
|
||||
GeometryNoCheck = 0, //!< No invalid geometry checking
|
||||
GeometrySkipInvalid = 1, //!< Skip any features with invalid geometry. This requires a slow geometry validity check for every feature.
|
||||
GeometryAbortOnInvalid = 2, //!< Close iterator on encountering any features with invalid geometry. This requires a slow geometry validity check for every feature.
|
||||
};
|
||||
|
||||
/** \ingroup core
|
||||
* The OrderByClause class represents an order by clause for a QgsFeatureRequest.
|
||||
*
|
||||
@ -270,6 +278,22 @@ class CORE_EXPORT QgsFeatureRequest
|
||||
//! Get feature IDs that should be fetched.
|
||||
const QgsFeatureIds &filterFids() const { return mFilterFids; }
|
||||
|
||||
/**
|
||||
* Sets invalid geometry checking behavior.
|
||||
* \note Invalid geometry checking is not performed when retrieving features
|
||||
* directly from a QgsVectorDataProvider.
|
||||
* \see invalidGeometryCheck()
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
QgsFeatureRequest &setInvalidGeometryCheck( InvalidGeometryCheck check );
|
||||
|
||||
/**
|
||||
* Returns the invalid geometry checking behavior.
|
||||
* \see setInvalidGeometryCheck()
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
InvalidGeometryCheck invalidGeometryCheck() const { return mInvalidGeometryFilter; }
|
||||
|
||||
/** Set the filter expression. {\see QgsExpression}
|
||||
* \param expression expression string
|
||||
* \see filterExpression
|
||||
@ -415,6 +439,7 @@ class CORE_EXPORT QgsFeatureRequest
|
||||
QgsSimplifyMethod mSimplifyMethod;
|
||||
long mLimit = -1;
|
||||
OrderBy mOrderBy;
|
||||
InvalidGeometryCheck mInvalidGeometryFilter = GeometryNoCheck;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsFeatureRequest::Flags )
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "qgsexpressioncontext.h"
|
||||
#include "qgsdistancearea.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgsmessagelog.h"
|
||||
|
||||
QgsVectorLayerFeatureSource::QgsVectorLayerFeatureSource( const QgsVectorLayer *layer )
|
||||
{
|
||||
@ -241,8 +242,15 @@ bool QgsVectorLayerFeatureIterator::fetchFeature( QgsFeature &f )
|
||||
if ( mFetchedFid )
|
||||
return false;
|
||||
bool res = nextFeatureFid( f );
|
||||
mFetchedFid = true;
|
||||
return res;
|
||||
if ( res && checkGeometry( f ) )
|
||||
{
|
||||
mFetchedFid = true;
|
||||
return res;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !mRequest.filterRect().isNull() )
|
||||
@ -305,6 +313,9 @@ bool QgsVectorLayerFeatureIterator::fetchFeature( QgsFeature &f )
|
||||
if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) )
|
||||
updateFeatureGeometry( f );
|
||||
|
||||
if ( !checkGeometry( f ) )
|
||||
continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
// no more provider features
|
||||
@ -366,6 +377,9 @@ bool QgsVectorLayerFeatureIterator::fetchNextAddedFeature( QgsFeature &f )
|
||||
// skip features which are not accepted by the filter
|
||||
continue;
|
||||
|
||||
if ( !checkGeometry( *mFetchAddedFeaturesIt ) )
|
||||
continue;
|
||||
|
||||
useAddedFeature( *mFetchAddedFeaturesIt, f );
|
||||
|
||||
return true;
|
||||
@ -416,9 +430,12 @@ bool QgsVectorLayerFeatureIterator::fetchNextChangedGeomFeature( QgsFeature &f )
|
||||
|
||||
useChangedAttributeFeature( fid, *mFetchChangedGeomIt, f );
|
||||
|
||||
// return complete feature
|
||||
mFetchChangedGeomIt++;
|
||||
return true;
|
||||
if ( checkGeometry( f ) )
|
||||
{
|
||||
// return complete feature
|
||||
mFetchChangedGeomIt++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false; // no more changed geometries
|
||||
@ -440,7 +457,7 @@ bool QgsVectorLayerFeatureIterator::fetchNextChangedAttributeFeature( QgsFeature
|
||||
addVirtualAttributes( f );
|
||||
|
||||
mRequest.expressionContext()->setFeature( f );
|
||||
if ( mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() )
|
||||
if ( mRequest.filterExpression()->evaluate( mRequest.expressionContext() ).toBool() && checkGeometry( f ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -658,6 +675,39 @@ void QgsVectorLayerFeatureIterator::createOrderedJoinList()
|
||||
}
|
||||
}
|
||||
|
||||
bool QgsVectorLayerFeatureIterator::checkGeometry( const QgsFeature &feature )
|
||||
{
|
||||
if ( !feature.hasGeometry() )
|
||||
return true;
|
||||
|
||||
switch ( mRequest.invalidGeometryCheck() )
|
||||
{
|
||||
case QgsFeatureRequest::GeometryNoCheck:
|
||||
return true;
|
||||
|
||||
case QgsFeatureRequest::GeometrySkipInvalid:
|
||||
{
|
||||
if ( !feature.geometry().isGeosValid() )
|
||||
{
|
||||
QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), QgsMessageLog::CRITICAL );
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case QgsFeatureRequest::GeometryAbortOnInvalid:
|
||||
if ( !feature.geometry().isGeosValid() )
|
||||
{
|
||||
QgsMessageLog::logMessage( QObject::tr( "Geometry error: One or more input features have invalid geometry." ), QString(), QgsMessageLog::CRITICAL);
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsVectorLayerFeatureIterator::prepareField( int fieldIdx )
|
||||
{
|
||||
switch ( mSource->mFields.fieldOrigin( fieldIdx ) )
|
||||
|
@ -221,6 +221,11 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera
|
||||
virtual bool providerCanSimplify( QgsSimplifyMethod::MethodType methodType ) const override;
|
||||
|
||||
void createOrderedJoinList();
|
||||
|
||||
/**
|
||||
* Performs any geometry validity checking.
|
||||
*/
|
||||
bool checkGeometry( const QgsFeature &feature );
|
||||
};
|
||||
|
||||
#endif // QGSVECTORLAYERFEATUREITERATOR_H
|
||||
|
@ -16,7 +16,14 @@ import qgis # NOQA
|
||||
|
||||
import os
|
||||
|
||||
from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsFeature, QgsField, NULL, QgsProject, QgsVectorLayerJoinInfo
|
||||
from qgis.core import (QgsVectorLayer,
|
||||
QgsFeatureRequest,
|
||||
QgsFeature,
|
||||
QgsField,
|
||||
NULL,
|
||||
QgsProject,
|
||||
QgsVectorLayerJoinInfo,
|
||||
QgsGeometry)
|
||||
from qgis.testing import start_app, unittest
|
||||
from qgis.PyQt.QtCore import QVariant
|
||||
|
||||
@ -273,6 +280,33 @@ class TestQgsFeatureIterator(unittest.TestCase):
|
||||
|
||||
QgsProject.instance().removeMapLayers([layer.id(), joinLayer.id()])
|
||||
|
||||
def test_invalidGeometryFilter(self):
|
||||
layer = QgsVectorLayer(
|
||||
"Polygon?field=x:string",
|
||||
"joinlayer", "memory")
|
||||
|
||||
# add some features, one has invalid geometry
|
||||
pr = layer.dataProvider()
|
||||
f1 = QgsFeature()
|
||||
f1.setAttributes(["a"])
|
||||
f1.setGeometry(QgsGeometry.fromWkt('Polygon((0 0, 1 0, 1 1, 0 1, 0 0))')) # valid
|
||||
f2 = QgsFeature()
|
||||
f2.setAttributes(["b"])
|
||||
f2.setGeometry(QgsGeometry.fromWkt('Polygon((0 0, 1 0, 0 1, 1 1, 0 0))')) # invalid
|
||||
f3 = QgsFeature()
|
||||
f3.setAttributes(["c"])
|
||||
f3.setGeometry(QgsGeometry.fromWkt('Polygon((0 0, 1 0, 1 1, 0 1, 0 0))')) # valid
|
||||
self.assertTrue(pr.addFeatures([f1, f2, f3]))
|
||||
|
||||
res = [f['x'] for f in
|
||||
layer.getFeatures(QgsFeatureRequest().setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck))]
|
||||
self.assertEqual(res, ['a', 'b', 'c'])
|
||||
res = [f['x'] for f in
|
||||
layer.getFeatures(QgsFeatureRequest().setInvalidGeometryCheck(QgsFeatureRequest.GeometrySkipInvalid))]
|
||||
self.assertEqual(res, ['a', 'c'])
|
||||
res = [f['x'] for f in
|
||||
layer.getFeatures(QgsFeatureRequest().setInvalidGeometryCheck(QgsFeatureRequest.GeometryAbortOnInvalid))]
|
||||
self.assertEqual(res, ['a'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
Loading…
x
Reference in New Issue
Block a user