Skip to content
Permalink
Browse files

Merge pull request #5986 from nyalldawson/geometry_equals

Refine geometry equals checks
  • Loading branch information
nyalldawson committed Jan 5, 2018
2 parents df95536 + 164c439 commit 63cc1246dfbd231687f8319147e9e89169f77449
@@ -1354,6 +1354,8 @@ maintains Z or M dimensions from the input points and is more efficient.
- exportToWkt() was renamed to asWkt()
- exportToGeoJSON() was renamed to asJson()
- closestSegmentWithContext() now returns an extra value, indicating whether the point is to the left of the geometry
- equals() now performs a fast, strict equality check, where geometries are considered equal only if they have the
exact same type, vertices and order. The slower topological test can be performed by calling isGeosEqual instead.


QgsGeometryAnalyzer {#qgis_api_break_3_0_QgsGeometryAnalyzer}
@@ -72,6 +72,9 @@ Constructor for QgsAbstractGeometry.
virtual ~QgsAbstractGeometry();
QgsAbstractGeometry( const QgsAbstractGeometry &geom );

virtual bool operator==( const QgsAbstractGeometry &other ) const = 0;
virtual bool operator!=( const QgsAbstractGeometry &other ) const = 0;

virtual QgsAbstractGeometry *clone() const = 0 /Factory/;
%Docstring
Clones the geometry by performing a deep copy
@@ -25,9 +25,7 @@ class QgsCircularString: QgsCurve
public:
QgsCircularString();

virtual bool operator==( const QgsCurve &other ) const;

virtual bool operator!=( const QgsCurve &other ) const;
virtual bool equals( const QgsCurve &other ) const;


virtual QString geometryType() const;
@@ -25,9 +25,7 @@ class QgsCompoundCurve: QgsCurve
QgsCompoundCurve( const QgsCompoundCurve &curve );
~QgsCompoundCurve();

virtual bool operator==( const QgsCurve &other ) const;

virtual bool operator!=( const QgsCurve &other ) const;
virtual bool equals( const QgsCurve &other ) const;


virtual QString geometryType() const;
@@ -28,8 +28,17 @@ class QgsCurve: QgsAbstractGeometry
Constructor for QgsCurve.
%End

virtual bool operator==( const QgsCurve &other ) const = 0;
virtual bool operator!=( const QgsCurve &other ) const = 0;
virtual bool equals( const QgsCurve &other ) const = 0;
%Docstring
Checks whether this curve exactly equals another curve.

.. versionadded:: 3.0
%End

virtual bool operator==( const QgsAbstractGeometry &other ) const;

virtual bool operator!=( const QgsAbstractGeometry &other ) const;


virtual QgsCurve *clone() const = 0 /Factory/;

@@ -25,8 +25,10 @@ class QgsCurvePolygon: QgsSurface
QgsCurvePolygon();
QgsCurvePolygon( const QgsCurvePolygon &p );

bool operator==( const QgsCurvePolygon &other ) const;
bool operator!=( const QgsCurvePolygon &other ) const;
virtual bool operator==( const QgsAbstractGeometry &other ) const;

virtual bool operator!=( const QgsAbstractGeometry &other ) const;


~QgsCurvePolygon();

@@ -247,13 +247,46 @@ return true for isEmpty().
bool isMultipart() const;
%Docstring
Returns true if WKB of the geometry is of WKBMulti* type
%End

bool equals( const QgsGeometry &geometry ) const;
%Docstring
Test if this geometry is exactly equal to another ``geometry``.

This is a strict equality check, where the underlying geometries must
have exactly the same type, component vertices and vertex order.

Calling this method is dramatically faster than the topological
equality test performed by isGeosEqual().

.. note::

Comparing two null geometries will return false.

.. versionadded:: 1.5

.. seealso:: :py:func:`isGeosEqual()`
%End

bool isGeosEqual( const QgsGeometry & ) const;
%Docstring
Compares the geometry with another geometry using GEOS
Compares the geometry with another geometry using GEOS.

This method performs a slow, topological check, where geometries
are considered equal if all of the their component edges overlap. E.g.
lines with the same vertex locations but opposite direction will be
considered equal by this method.

Consider using the much faster, stricter equality test performed
by equals() instead.

.. note::

Comparing two null geometries will return false.

.. versionadded:: 1.5

.. seealso:: :py:func:`equals()`
%End

bool isGeosValid() const;
@@ -749,13 +782,6 @@ Tests for if geometry is contained in another (uses GEOS)
%Docstring
Tests for if geometry is disjoint of another (uses GEOS)

.. versionadded:: 1.5
%End

bool equals( const QgsGeometry &geometry ) const;
%Docstring
Test for if geometry equals another (uses GEOS)

.. versionadded:: 1.5
%End

@@ -27,6 +27,11 @@ class QgsGeometryCollection: QgsAbstractGeometry
QgsGeometryCollection( const QgsGeometryCollection &c );
~QgsGeometryCollection();

virtual bool operator==( const QgsAbstractGeometry &other ) const;

virtual bool operator!=( const QgsAbstractGeometry &other ) const;


virtual QgsGeometryCollection *clone() const /Factory/;


@@ -57,9 +57,7 @@ or repeatedly calling addVertex()
.. versionadded:: 3.0
%End

virtual bool operator==( const QgsCurve &other ) const;

virtual bool operator!=( const QgsCurve &other ) const;
virtual bool equals( const QgsCurve &other ) const;


QgsPoint pointN( int i ) const;
@@ -85,8 +85,10 @@ Construct a QgsPoint from a QPointF
%End


bool operator==( const QgsPoint &pt ) const;
bool operator!=( const QgsPoint &pt ) const;
virtual bool operator==( const QgsAbstractGeometry &other ) const;

virtual bool operator!=( const QgsAbstractGeometry &other ) const;


double x() const;
%Docstring
@@ -202,7 +202,7 @@ QVariantMap QgsSplitWithLinesAlgorithm::processAlgorithm( const QVariantMap &par
// between geometry and splitLine, only the first one is considered.
if ( result == QgsGeometry::Success ) // split occurred
{
if ( inGeom.equals( before ) )
if ( inGeom.isGeosEqual( before ) )
{
// bug in splitGeometry: sometimes it returns 0 but
// the geometry is unchanged
@@ -110,6 +110,9 @@ class CORE_EXPORT QgsAbstractGeometry
QgsAbstractGeometry( const QgsAbstractGeometry &geom );
QgsAbstractGeometry &operator=( const QgsAbstractGeometry &geom );

virtual bool operator==( const QgsAbstractGeometry &other ) const = 0;
virtual bool operator!=( const QgsAbstractGeometry &other ) const = 0;

/**
* Clones the geometry by performing a deep copy
*/
@@ -33,7 +33,7 @@ QgsCircularString::QgsCircularString()
mWkbType = QgsWkbTypes::CircularString;
}

bool QgsCircularString::operator==( const QgsCurve &other ) const
bool QgsCircularString::equals( const QgsCurve &other ) const
{
const QgsCircularString *otherLine = dynamic_cast< const QgsCircularString * >( &other );
if ( !otherLine )
@@ -61,11 +61,6 @@ bool QgsCircularString::operator==( const QgsCurve &other ) const
return true;
}

bool QgsCircularString::operator!=( const QgsCurve &other ) const
{
return !operator==( other );
}

QgsCircularString *QgsCircularString::createEmptyWithSameType() const
{
auto result = qgis::make_unique< QgsCircularString >();
@@ -36,8 +36,7 @@ class CORE_EXPORT QgsCircularString: public QgsCurve
public:
QgsCircularString();

bool operator==( const QgsCurve &other ) const override;
bool operator!=( const QgsCurve &other ) const override;
bool equals( const QgsCurve &other ) const override;

QString geometryType() const override;
int dimension() const override;
@@ -35,7 +35,7 @@ QgsCompoundCurve::~QgsCompoundCurve()
clear();
}

bool QgsCompoundCurve::operator==( const QgsCurve &other ) const
bool QgsCompoundCurve::equals( const QgsCurve &other ) const
{
const QgsCompoundCurve *otherCurve = qgsgeometry_cast< const QgsCompoundCurve * >( &other );
if ( !otherCurve )
@@ -56,11 +56,6 @@ bool QgsCompoundCurve::operator==( const QgsCurve &other ) const
return true;
}

bool QgsCompoundCurve::operator!=( const QgsCurve &other ) const
{
return !operator==( other );
}

QgsCompoundCurve *QgsCompoundCurve::createEmptyWithSameType() const
{
auto result = qgis::make_unique< QgsCompoundCurve >();
@@ -36,8 +36,7 @@ class CORE_EXPORT QgsCompoundCurve: public QgsCurve
QgsCompoundCurve &operator=( const QgsCompoundCurve &curve );
~QgsCompoundCurve() override;

bool operator==( const QgsCurve &other ) const override;
bool operator!=( const QgsCurve &other ) const override;
bool equals( const QgsCurve &other ) const override;

QString geometryType() const override;
int dimension() const override;
@@ -22,6 +22,20 @@
#include "qgspoint.h"
#include "qgsmultipoint.h"

bool QgsCurve::operator==( const QgsAbstractGeometry &other ) const
{
const QgsCurve *otherCurve = qgsgeometry_cast< const QgsCurve * >( &other );
if ( !otherCurve )
return false;

return equals( *otherCurve );
}

bool QgsCurve::operator!=( const QgsAbstractGeometry &other ) const
{
return !operator==( other );
}

bool QgsCurve::isClosed() const
{
if ( numPoints() == 0 )
@@ -41,8 +41,14 @@ class CORE_EXPORT QgsCurve: public QgsAbstractGeometry
*/
QgsCurve() = default;

virtual bool operator==( const QgsCurve &other ) const = 0;
virtual bool operator!=( const QgsCurve &other ) const = 0;
/**
* Checks whether this curve exactly equals another curve.
* \since QGIS 3.0
*/
virtual bool equals( const QgsCurve &other ) const = 0;

bool operator==( const QgsAbstractGeometry &other ) const override;
bool operator!=( const QgsAbstractGeometry &other ) const override;

QgsCurve *clone() const override = 0 SIP_FACTORY;

@@ -90,40 +90,44 @@ QgsCurvePolygon &QgsCurvePolygon::operator=( const QgsCurvePolygon &p )
return *this;
}

bool QgsCurvePolygon::operator==( const QgsCurvePolygon &other ) const
bool QgsCurvePolygon::operator==( const QgsAbstractGeometry &other ) const
{
const QgsCurvePolygon *otherPolygon = qgsgeometry_cast< const QgsCurvePolygon * >( &other );
if ( !otherPolygon )
return false;

//run cheap checks first
if ( mWkbType != other.mWkbType )
if ( mWkbType != otherPolygon->mWkbType )
return false;

if ( ( !mExteriorRing && other.mExteriorRing ) || ( mExteriorRing && !other.mExteriorRing ) )
if ( ( !mExteriorRing && otherPolygon->mExteriorRing ) || ( mExteriorRing && !otherPolygon->mExteriorRing ) )
return false;

if ( mInteriorRings.count() != other.mInteriorRings.count() )
if ( mInteriorRings.count() != otherPolygon->mInteriorRings.count() )
return false;

// compare rings
if ( mExteriorRing && other.mExteriorRing )
if ( mExteriorRing && otherPolygon->mExteriorRing )
{
if ( *mExteriorRing != *other.mExteriorRing )
if ( *mExteriorRing != *otherPolygon->mExteriorRing )
return false;
}

for ( int i = 0; i < mInteriorRings.count(); ++i )
{
if ( ( !mInteriorRings.at( i ) && other.mInteriorRings.at( i ) ) ||
( mInteriorRings.at( i ) && !other.mInteriorRings.at( i ) ) )
if ( ( !mInteriorRings.at( i ) && otherPolygon->mInteriorRings.at( i ) ) ||
( mInteriorRings.at( i ) && !otherPolygon->mInteriorRings.at( i ) ) )
return false;

if ( mInteriorRings.at( i ) && other.mInteriorRings.at( i ) &&
*mInteriorRings.at( i ) != *other.mInteriorRings.at( i ) )
if ( mInteriorRings.at( i ) && otherPolygon->mInteriorRings.at( i ) &&
*mInteriorRings.at( i ) != *otherPolygon->mInteriorRings.at( i ) )
return false;
}

return true;
}

bool QgsCurvePolygon::operator!=( const QgsCurvePolygon &other ) const
bool QgsCurvePolygon::operator!=( const QgsAbstractGeometry &other ) const
{
return !operator==( other );
}
@@ -38,8 +38,8 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
QgsCurvePolygon( const QgsCurvePolygon &p );
QgsCurvePolygon &operator=( const QgsCurvePolygon &p );

bool operator==( const QgsCurvePolygon &other ) const;
bool operator!=( const QgsCurvePolygon &other ) const;
bool operator==( const QgsAbstractGeometry &other ) const override;
bool operator!=( const QgsAbstractGeometry &other ) const override;

~QgsCurvePolygon() override;

@@ -1147,9 +1147,12 @@ bool QgsGeometry::equals( const QgsGeometry &geometry ) const
return false;
}

QgsGeos geos( d->geometry.get() );
mLastError.clear();
return geos.isEqual( geometry.d->geometry.get(), &mLastError );
// fast check - are they shared copies of the same underlying geometry?
if ( d == geometry.d )
return true;

// slower check - actually test the geometries
return *d->geometry.get() == *geometry.d->geometry.get();
}

bool QgsGeometry::touches( const QgsGeometry &geometry ) const

0 comments on commit 63cc124

Please sign in to comment.
You can’t perform that action at this time.