Skip to content
Permalink
Browse files
Add QgsAbstractGeometry::compareTo( QgsAbstractGeometry*)
Allows for comparison of geometry objects, e.g. to allow for stable
sorting of them.

Ported from the GEOS equivalent method, but with addition of support
for M values and curved geometry types
  • Loading branch information
nyalldawson committed Apr 27, 2021
1 parent 36e52f8 commit 9c53fb8635aa068250006c228e1ead3e838c03c7
@@ -102,6 +102,13 @@ Constructor for QgsAbstractGeometry.
virtual QgsAbstractGeometry *clone() const = 0 /Factory/;
%Docstring
Clones the geometry by performing a deep copy
%End

virtual int compareTo( const QgsAbstractGeometry *other ) const;
%Docstring
Comparator for sorting of geometry.

.. versionadded:: 3.20
%End

virtual void clear() = 0;
@@ -809,6 +816,22 @@ To create it, the geometry is default constructed and then the WKB is changed.

protected:

int sortIndex() const;
%Docstring
Returns the sort index for the geometry, used in the :py:func:`~QgsAbstractGeometry.compareTo` method to compare
geometries of different types.

.. versionadded:: 3.20
%End

virtual int compareToSameClass( const QgsAbstractGeometry *other ) const = 0;
%Docstring
Compares to an ``other`` geometry of the same class, and returns a integer
for sorting of the two geometries.

.. versionadded:: 3.20
%End

virtual bool hasChildGeometries() const;
%Docstring
Returns whether the geometry has any child geometries (``False`` for point / curve, ``True`` otherwise)
@@ -219,6 +219,7 @@ Appends the contents of another circular ``string`` to the end of this circular

protected:

int compareToSameClass( const QgsAbstractGeometry *other ) const final;
virtual QgsRectangle calculateBoundingBox() const;


@@ -195,6 +195,7 @@ Appends first point if not already closed.

protected:

int compareToSameClass( const QgsAbstractGeometry *other ) const final;
virtual QgsRectangle calculateBoundingBox() const;


@@ -296,6 +296,7 @@ Returns approximate rotation angle for a vertex. Usually average angle between a

virtual QgsAbstractGeometry *childGeometry( int index ) const;

int compareToSameClass( const QgsAbstractGeometry *other ) const final;

protected:

@@ -317,6 +317,7 @@ Iterates through all geometries in the collection.

virtual QgsAbstractGeometry *childGeometry( int index ) const;

int compareToSameClass( const QgsAbstractGeometry *other ) const final;

protected:

@@ -408,7 +408,6 @@ segment in the line.
%End



virtual QString geometryType() const /HoldGIL/;

virtual int dimension() const /HoldGIL/;
@@ -650,6 +649,7 @@ corresponds to the last point in the line.

protected:

int compareToSameClass( const QgsAbstractGeometry *other ) const final;
virtual QgsRectangle calculateBoundingBox() const;


@@ -467,6 +467,7 @@ Angle undefined. Always returns 0.0

protected:

int compareToSameClass( const QgsAbstractGeometry *other ) const final;
virtual int childCount() const;

virtual QgsPoint childPoint( int index ) const;
@@ -40,6 +40,39 @@ QgsAbstractGeometry &QgsAbstractGeometry::operator=( const QgsAbstractGeometry &
return *this;
}

int QgsAbstractGeometry::compareTo( const QgsAbstractGeometry *other ) const
{
// compare to self
if ( this == other )
{
return 0;
}

if ( sortIndex() != other->sortIndex() )
{
//different geometry types
int diff = sortIndex() - other->sortIndex();
return ( diff > 0 ) - ( diff < 0 );
}

// same types
if ( isEmpty() && other->isEmpty() )
{
return 0;
}

if ( isEmpty() )
{
return -1;
}
if ( other->isEmpty() )
{
return 1;
}

return compareToSameClass( other );
}

void QgsAbstractGeometry::setZMTypeFromSubGeometry( const QgsAbstractGeometry *subgeom, QgsWkbTypes::Type baseGeomType )
{
if ( !subgeom )
@@ -284,6 +317,44 @@ QgsVertexIterator QgsAbstractGeometry::vertices() const
return QgsVertexIterator( this );
}

int QgsAbstractGeometry::sortIndex() const
{
switch ( QgsWkbTypes::flatType( mWkbType ) )
{
case QgsWkbTypes::Point:
return 0;
case QgsWkbTypes::MultiPoint:
return 1;
case QgsWkbTypes::LineString:
return 2;
case QgsWkbTypes::CircularString:
return 3;
case QgsWkbTypes::CompoundCurve:
return 4;
case QgsWkbTypes::MultiLineString:
return 5;
case QgsWkbTypes::MultiCurve:
return 6;
case QgsWkbTypes::Polygon:
case QgsWkbTypes::Triangle:
return 7;
case QgsWkbTypes::CurvePolygon:
return 8;
case QgsWkbTypes::MultiPolygon:
return 9;
case QgsWkbTypes::MultiSurface:
return 10;
case QgsWkbTypes::GeometryCollection:
return 11;
case QgsWkbTypes::Unknown:
return 12;
case QgsWkbTypes::NoGeometry:
default:
break;
}
return 13;
}

bool QgsAbstractGeometry::hasChildGeometries() const
{
return QgsWkbTypes::isMultiType( wkbType() ) || dimension() == 2;
@@ -163,6 +163,13 @@ class CORE_EXPORT QgsAbstractGeometry
*/
virtual QgsAbstractGeometry *clone() const = 0 SIP_FACTORY;

/**
* Comparator for sorting of geometry.
*
* \since QGIS 3.20
*/
virtual int compareTo( const QgsAbstractGeometry *other ) const;

/**
* Clears the geometry, ie reset it to a null geometry
*/
@@ -1026,6 +1033,22 @@ class CORE_EXPORT QgsAbstractGeometry

protected:

/**
* Returns the sort index for the geometry, used in the compareTo() method to compare
* geometries of different types.
*
* \since QGIS 3.20
*/
int sortIndex() const;

/**
* Compares to an \a other geometry of the same class, and returns a integer
* for sorting of the two geometries.
*
* \since QGIS 3.20
*/
virtual int compareToSameClass( const QgsAbstractGeometry *other ) const = 0;

/**
* Returns whether the geometry has any child geometries (FALSE for point / curve, TRUE otherwise)
* \note used for vertex_iterator implementation
@@ -158,6 +158,92 @@ QgsCircularString *QgsCircularString::createEmptyWithSameType() const
return result.release();
}

int QgsCircularString::compareToSameClass( const QgsAbstractGeometry *other ) const
{
const QgsCircularString *otherLine = qgsgeometry_cast<const QgsCircularString *>( other );
if ( !otherLine )
return -1;

const int size = mX.size();
const int otherSize = otherLine->mX.size();
if ( size > otherSize )
{
return 1;
}
else if ( size < otherSize )
{
return -1;
}

if ( is3D() && !otherLine->is3D() )
return 1;
else if ( !is3D() && otherLine->is3D() )
return -1;
const bool considerZ = is3D();

if ( isMeasure() && !otherLine->isMeasure() )
return 1;
else if ( !isMeasure() && otherLine->isMeasure() )
return -1;
const bool considerM = isMeasure();

for ( int i = 0; i < size; i++ )
{
const double x = mX[i];
const double otherX = otherLine->mX[i];
if ( x < otherX )
{
return -1;
}
else if ( x > otherX )
{
return 1;
}

const double y = mY[i];
const double otherY = otherLine->mY[i];
if ( y < otherY )
{
return -1;
}
else if ( y > otherY )
{
return 1;
}

if ( considerZ )
{
const double z = mZ[i];
const double otherZ = otherLine->mZ[i];

if ( z < otherZ )
{
return -1;
}
else if ( z > otherZ )
{
return 1;
}
}

if ( considerM )
{
const double m = mM[i];
const double otherM = otherLine->mM[i];

if ( m < otherM )
{
return -1;
}
else if ( m > otherM )
{
return 1;
}
}
}
return 0;
}

QString QgsCircularString::geometryType() const
{
return QStringLiteral( "CircularString" );
@@ -199,6 +199,7 @@ class CORE_EXPORT QgsCircularString: public QgsCurve

protected:

int compareToSameClass( const QgsAbstractGeometry *other ) const final;
QgsRectangle calculateBoundingBox() const override;

private:
@@ -67,6 +67,37 @@ QgsCompoundCurve *QgsCompoundCurve::createEmptyWithSameType() const
return result.release();
}

int QgsCompoundCurve::compareToSameClass( const QgsAbstractGeometry *other ) const
{
const QgsCompoundCurve *otherCurve = qgsgeometry_cast<const QgsCompoundCurve *>( other );
if ( !otherCurve )
return -1;

int i = 0;
int j = 0;
while ( i < mCurves.size() && j < otherCurve->mCurves.size() )
{
const QgsAbstractGeometry *aGeom = mCurves[i];
const QgsAbstractGeometry *bGeom = otherCurve->mCurves[j];
int comparison = aGeom->compareTo( bGeom );
if ( comparison != 0 )
{
return comparison;
}
i++;
j++;
}
if ( i < mCurves.size() )
{
return 1;
}
if ( j < otherCurve->mCurves.size() )
{
return -1;
}
return 0;
}

QString QgsCompoundCurve::geometryType() const
{
return QStringLiteral( "CompoundCurve" );
@@ -176,6 +176,7 @@ class CORE_EXPORT QgsCompoundCurve: public QgsCurve

protected:

int compareToSameClass( const QgsAbstractGeometry *other ) const final;
QgsRectangle calculateBoundingBox() const override;

private:

0 comments on commit 9c53fb8

Please sign in to comment.