From f681cd459e0ae2c5644d4d6d99cf1dbd9433730c Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Fri, 31 May 2024 11:38:05 +0200 Subject: [PATCH] [api] Add compare methods to QgsFeatureRequest This will allow to reduce unnecessary expensive calls when the request has not really changed. --- .../auto_generated/qgsfeaturerequest.sip.in | 15 ++++ .../auto_generated/qgsfeaturerequest.sip.in | 15 ++++ src/core/qgsfeaturerequest.cpp | 47 +++++++++++ src/core/qgsfeaturerequest.h | 20 +++++ tests/src/python/test_qgsfeaturerequest.py | 80 +++++++++++++++++++ 5 files changed, 177 insertions(+) diff --git a/python/PyQt6/core/auto_generated/qgsfeaturerequest.sip.in b/python/PyQt6/core/auto_generated/qgsfeaturerequest.sip.in index 09dd1033485..098aa0b683a 100644 --- a/python/PyQt6/core/auto_generated/qgsfeaturerequest.sip.in +++ b/python/PyQt6/core/auto_generated/qgsfeaturerequest.sip.in @@ -204,6 +204,10 @@ Create a new empty order by Create a new order by from a list of clauses %End + bool operator==( const OrderBy &v ) const; + + bool operator!=( const OrderBy &v ) const; + QList list() const; %Docstring Gets a copy as a list of OrderByClauses @@ -275,6 +279,17 @@ construct a request with a filter expression QgsFeatureRequest( const QgsFeatureRequest &rh ); %Docstring copy constructor +%End + + bool compare( const QgsFeatureRequest &other ) const; +%Docstring +Compare two requests for equality, ignoring Expression Context, Transform Error Callback, Feedback and Invalid Geometry Callback + +:param other: the other request + +:return: true if the requests are equal in all respects but without checking for Expression Context, Transform Error, Feedback and Invalid Geometry Callback + +.. versionadded:: 3.38 %End ~QgsFeatureRequest(); diff --git a/python/core/auto_generated/qgsfeaturerequest.sip.in b/python/core/auto_generated/qgsfeaturerequest.sip.in index 09dd1033485..098aa0b683a 100644 --- a/python/core/auto_generated/qgsfeaturerequest.sip.in +++ b/python/core/auto_generated/qgsfeaturerequest.sip.in @@ -204,6 +204,10 @@ Create a new empty order by Create a new order by from a list of clauses %End + bool operator==( const OrderBy &v ) const; + + bool operator!=( const OrderBy &v ) const; + QList list() const; %Docstring Gets a copy as a list of OrderByClauses @@ -275,6 +279,17 @@ construct a request with a filter expression QgsFeatureRequest( const QgsFeatureRequest &rh ); %Docstring copy constructor +%End + + bool compare( const QgsFeatureRequest &other ) const; +%Docstring +Compare two requests for equality, ignoring Expression Context, Transform Error Callback, Feedback and Invalid Geometry Callback + +:param other: the other request + +:return: true if the requests are equal in all respects but without checking for Expression Context, Transform Error, Feedback and Invalid Geometry Callback + +.. versionadded:: 3.38 %End ~QgsFeatureRequest(); diff --git a/src/core/qgsfeaturerequest.cpp b/src/core/qgsfeaturerequest.cpp index b73f65a87bb..38b8f4bddfe 100644 --- a/src/core/qgsfeaturerequest.cpp +++ b/src/core/qgsfeaturerequest.cpp @@ -98,6 +98,34 @@ QgsFeatureRequest &QgsFeatureRequest::operator=( const QgsFeatureRequest &rh ) return *this; } +// Relaxed Equality operator +bool QgsFeatureRequest::compare( const QgsFeatureRequest &rh ) const +{ + if ( &rh == this ) + return true; + + return mFlags == rh.mFlags && + mFilter == rh.mFilter && + mSpatialFilter == rh.mSpatialFilter && + mFilterRect == rh.mFilterRect && + ( ( mReferenceGeometry.isNull() && rh.mReferenceGeometry.isNull() ) || mReferenceGeometry.equals( rh.mReferenceGeometry ) ) && + mDistanceWithin == rh.mDistanceWithin && + mFilterFid == rh.mFilterFid && + mFilterFids == rh.mFilterFids && + ( mFilterExpression ? rh.mFilterExpression && *mFilterExpression == *rh.mFilterExpression : !rh.mFilterExpression ) && + mInvalidGeometryFilter == rh.mInvalidGeometryFilter && + mAttrs == rh.mAttrs && + mSimplifyMethod == rh.mSimplifyMethod && + mLimit == rh.mLimit && + mOrderBy == rh.mOrderBy && + mCrs == rh.mCrs && + mTransformContext == rh.mTransformContext && + mTimeout == rh.mTimeout && + mRequestMayBeNested == rh.mRequestMayBeNested; + +} + + QgsFeatureRequest &QgsFeatureRequest::setFilterRect( const QgsRectangle &rect ) { mFilterRect = rect; @@ -515,6 +543,25 @@ QgsFeatureRequest::OrderBy::OrderBy( const QList QgsFeatureRequest::OrderBy::list() const { return *this; diff --git a/src/core/qgsfeaturerequest.h b/src/core/qgsfeaturerequest.h index 33bd00b83b9..0152c233894 100644 --- a/src/core/qgsfeaturerequest.h +++ b/src/core/qgsfeaturerequest.h @@ -230,6 +230,18 @@ class CORE_EXPORT QgsFeatureRequest */ CORE_EXPORT OrderBy( const QList &other ); + /** + * Equality operator + * \since QGIS 3.8 + */ + CORE_EXPORT bool operator==( const OrderBy &v ) const; + + /** + * Inequality operator + * \since QGIS 3.8 + */ + CORE_EXPORT bool operator!=( const OrderBy &v ) const; + /** * Gets a copy as a list of OrderByClauses * @@ -294,6 +306,14 @@ class CORE_EXPORT QgsFeatureRequest //! Assignment operator QgsFeatureRequest &operator=( const QgsFeatureRequest &rh ); + /** + * Compare two requests for equality, ignoring Expression Context, Transform Error Callback, Feedback and Invalid Geometry Callback + * \param other the other request + * \return true if the requests are equal in all respects but without checking for Expression Context, Transform Error, Feedback and Invalid Geometry Callback + * \since QGIS 3.38 + */ + bool compare( const QgsFeatureRequest &other ) const; + ~QgsFeatureRequest(); /** diff --git a/tests/src/python/test_qgsfeaturerequest.py b/tests/src/python/test_qgsfeaturerequest.py index 421b0f3f84c..2f1a066b535 100644 --- a/tests/src/python/test_qgsfeaturerequest.py +++ b/tests/src/python/test_qgsfeaturerequest.py @@ -410,6 +410,86 @@ class TestQgsFeatureRequest(QgisTestCase): self.assertEqual(req2.distanceWithin(), 1.2) self.assertEqual(req2.filterRect(), QgsRectangle(-1.2, -1.2, 12.2, 3.2)) + def test_compare(self): + + req1 = QgsFeatureRequest().setFilterFids([8, 9]).setFilterRect(QgsRectangle(1, 2, 3, 4)).setInvalidGeometryCheck(QgsFeatureRequest.InvalidGeometryCheck.GeometrySkipInvalid).setLimit(6).setFlags(QgsFeatureRequest.Flag.ExactIntersect).setSubsetOfAttributes([1, 4]).setTimeout(6).setRequestMayBeNested(True) + req2 = QgsFeatureRequest(req1) + self.assertTrue(req1.compare(req1)) + self.assertTrue(req1.compare(req2)) + + req3 = QgsFeatureRequest(req2) + self.assertTrue(req3.compare(req2)) + self.assertTrue(req3.compare(req1)) + req3.setFilterFids([8, 9, 10]) + self.assertFalse(req3.compare(req1)) + + req3 = QgsFeatureRequest(req2) + req3.setFilterRect(QgsRectangle(1, 2, 3, 5)) + self.assertFalse(req3.compare(req1)) + + req3 = QgsFeatureRequest(req2) + req3.setInvalidGeometryCheck(QgsFeatureRequest.InvalidGeometryCheck.GeometryNoCheck) + self.assertFalse(req3.compare(req1)) + + req3 = QgsFeatureRequest(req2) + req3.setLimit(7) + self.assertFalse(req3.compare(req1)) + + req3 = QgsFeatureRequest(req2) + req3.setFlags(QgsFeatureRequest.Flag.NoGeometry) + self.assertFalse(req3.compare(req1)) + + req3 = QgsFeatureRequest(req2) + req3.setSubsetOfAttributes([1, 4, 5]) + self.assertFalse(req3.compare(req1)) + + req3 = QgsFeatureRequest(req2) + req3.setTimeout(7) + self.assertFalse(req3.compare(req1)) + + req3 = QgsFeatureRequest(req2) + req3.setRequestMayBeNested(False) + self.assertFalse(req3.compare(req1)) + + req3 = QgsFeatureRequest(req2) + orderClause = QgsFeatureRequest.OrderByClause('a', False) + order = QgsFeatureRequest.OrderBy([orderClause]) + req3.setOrderBy(order) + self.assertFalse(req3.compare(req1)) + req4 = QgsFeatureRequest(req2) + orderClause = QgsFeatureRequest.OrderByClause('a', False) + order2 = QgsFeatureRequest.OrderBy([orderClause]) + req4.setOrderBy(order2) + self.assertTrue(req4.compare(req3)) + self.assertTrue(order == order2) + + # Expression Context is not checked + req3 = QgsFeatureRequest(req2) + context = QgsExpressionContext() + scope = QgsExpressionContextScope() + scope.setVariable('a', 6) + context.appendScope(scope) + req3.setExpressionContext(context) + self.assertTrue(req3.compare(req1)) + + def test_order_by_equality(self): + + orderClause1 = QgsFeatureRequest.OrderByClause('a', False) + orderClause2 = QgsFeatureRequest.OrderByClause('a', False) + self.assertTrue(orderClause1 == orderClause2) + orderClause2 = QgsFeatureRequest.OrderByClause('b', False) + self.assertFalse(orderClause1 == orderClause2) + orderClause2 = QgsFeatureRequest.OrderByClause('a', True) + self.assertFalse(orderClause1 == orderClause2) + + order1 = QgsFeatureRequest.OrderBy([orderClause1]) + order2 = QgsFeatureRequest.OrderBy([orderClause1]) + self.assertTrue(order1 == order2) + order2 = QgsFeatureRequest.OrderBy([orderClause2]) + self.assertFalse(order1 == order2) + order2 = QgsFeatureRequest.OrderBy([orderClause1, orderClause2]) + self.assertFalse(order1 == order2) + if __name__ == '__main__': unittest.main()