Skip to content

Commit

Permalink
Add QgsGeometry::isSimple() - useful for self-intersection checks
Browse files Browse the repository at this point in the history
  • Loading branch information
wonder-sk committed May 18, 2017
1 parent 09975ce commit e001d5c
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 0 deletions.
13 changes: 13 additions & 0 deletions python/core/geometry/qgsgeometry.sip
Expand Up @@ -188,6 +188,19 @@ Returns true if WKB of the geometry is of WKBMulti* type
:rtype: bool
%End

bool isSimple() const;
%Docstring
Determines whether the geometry is simple (according to OGC definition),
i.e. it has no anomalous geometric points, such as self-intersection or self-tangency.
Uses GEOS library for the test.
.. note::

This is useful mainly for linestrings and linear rings. Polygons are simple by definition,
for checking anomalies in polygon geometries one can use isGeosValid().
.. versionadded:: 3.0
:rtype: bool
%End

double area() const;
%Docstring
Returns the area of the geometry using GEOS
Expand Down
7 changes: 7 additions & 0 deletions python/core/geometry/qgsgeometryengine.sip
Expand Up @@ -155,6 +155,13 @@ class QgsGeometryEngine
:rtype: bool
%End

virtual bool isSimple( QString *errorMsg = 0 ) const = 0;
%Docstring
Determines whether the geometry is simple (according to OGC definition).
.. versionadded:: 3.0
:rtype: bool
%End

virtual int splitGeometry( const QgsLineString &splitLine,
QList<QgsAbstractGeometry *> &newGeometries,
bool topological,
Expand Down
9 changes: 9 additions & 0 deletions src/core/geometry/qgsgeometry.cpp
Expand Up @@ -1980,6 +1980,15 @@ bool QgsGeometry::isGeosValid() const
return geos.isValid();
}

bool QgsGeometry::isSimple() const
{
if ( !d->geometry )
return false;

QgsGeos geos( d->geometry );
return geos.isSimple();
}

bool QgsGeometry::isGeosEqual( const QgsGeometry &g ) const
{
if ( !d->geometry || !g.d->geometry )
Expand Down
9 changes: 9 additions & 0 deletions src/core/geometry/qgsgeometry.h
Expand Up @@ -213,6 +213,15 @@ class CORE_EXPORT QgsGeometry
*/
bool isGeosValid() const;

/** Determines whether the geometry is simple (according to OGC definition),
* i.e. it has no anomalous geometric points, such as self-intersection or self-tangency.
* Uses GEOS library for the test.
* \note This is useful mainly for linestrings and linear rings. Polygons are simple by definition,
* for checking anomalies in polygon geometries one can use isGeosValid().
* \since QGIS 3.0
*/
bool isSimple() const;

/** Returns the area of the geometry using GEOS
\since QGIS 1.5
*/
Expand Down
5 changes: 5 additions & 0 deletions src/core/geometry/qgsgeometryengine.h
Expand Up @@ -83,6 +83,11 @@ class CORE_EXPORT QgsGeometryEngine
virtual bool isEqual( const QgsAbstractGeometry &geom, QString *errorMsg = nullptr ) const = 0;
virtual bool isEmpty( QString *errorMsg ) const = 0;

/** Determines whether the geometry is simple (according to OGC definition).
* \since QGIS 3.0
*/
virtual bool isSimple( QString *errorMsg = nullptr ) const = 0;

virtual int splitGeometry( const QgsLineString &splitLine,
QList<QgsAbstractGeometry *> &newGeometries,
bool topological,
Expand Down
14 changes: 14 additions & 0 deletions src/core/geometry/qgsgeos.cpp
Expand Up @@ -1494,6 +1494,20 @@ bool QgsGeos::isEmpty( QString *errorMsg ) const
CATCH_GEOS_WITH_ERRMSG( false );
}

bool QgsGeos::isSimple( QString *errorMsg ) const
{
if ( !mGeos )
{
return false;
}

try
{
return GEOSisSimple_r( geosinit.ctxt, mGeos );
}
CATCH_GEOS_WITH_ERRMSG( false );
}

GEOSCoordSequence *QgsGeos::createCoordinateSequence( const QgsCurve *curve, double precision, bool forceClose )
{
bool segmentize = false;
Expand Down
1 change: 1 addition & 0 deletions src/core/geometry/qgsgeos.h
Expand Up @@ -71,6 +71,7 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
bool isValid( QString *errorMsg = nullptr ) const override;
bool isEqual( const QgsAbstractGeometry &geom, QString *errorMsg = nullptr ) const override;
bool isEmpty( QString *errorMsg = nullptr ) const override;
bool isSimple( QString *errorMsg = nullptr ) const override;

/** Splits this geometry according to a given line.
\param splitLine the line that splits the geometry
Expand Down
29 changes: 29 additions & 0 deletions tests/src/core/testqgsgeometry.cpp
Expand Up @@ -124,6 +124,8 @@ class TestQgsGeometry : public QObject

void makeValid();

void isSimple();

private:
//! A helper method to do a render check to see if the geometry op is as expected
bool renderCheck( const QString &testName, const QString &comment = QLatin1String( QLatin1String( "" ) ), int mismatchCount = 0 );
Expand Down Expand Up @@ -5423,5 +5425,32 @@ void TestQgsGeometry::makeValid()
}
}

void TestQgsGeometry::isSimple()
{
typedef QPair<QString, bool> InputWktAndExpectedResult;
QList<InputWktAndExpectedResult> geoms;
geoms << qMakePair( QString( "LINESTRING(0 0, 1 0, 1 1)" ), true );
geoms << qMakePair( QString( "LINESTRING(0 0, 1 0, 1 1, 0 0)" ), true ); // may be closed (linear ring)
geoms << qMakePair( QString( "LINESTRING(0 0, 1 0, 1 1, 0 -1)" ), false ); // self-intersection
geoms << qMakePair( QString( "LINESTRING(0 0, 1 0, 1 1, 0.5 0, 0 1)" ), false ); // self-tangency
geoms << qMakePair( QString( "POINT(1 1)" ), true ); // points are simple
geoms << qMakePair( QString( "POLYGON((0 0, 1 1, 1 1, 0 0))" ), true ); // polygons are always simple, even if they are invalid
geoms << qMakePair( QString( "MULTIPOINT((1 1), (2 2))" ), true );
geoms << qMakePair( QString( "MULTIPOINT((1 1), (1 1))" ), false ); // must not contain the same point twice
geoms << qMakePair( QString( "MULTILINESTRING((0 0, 1 0), (0 1, 1 1))" ), true );
geoms << qMakePair( QString( "MULTILINESTRING((0 0, 1 0), (0 0, 1 0))" ), true ); // may be touching at endpoints
geoms << qMakePair( QString( "MULTILINESTRING((0 0, 1 1), (0 1, 1 0))" ), false ); // must not intersect each other
geoms << qMakePair( QString( "MULTIPOLYGON(((0 0, 1 1, 1 1, 0 0)),((0 0, 1 1, 1 1, 0 0)))" ), true ); // multi-polygons are always simple

Q_FOREACH ( const InputWktAndExpectedResult &pair, geoms )
{
QgsGeometry gInput = QgsGeometry::fromWkt( pair.first );
QVERIFY( !gInput.isNull() );

bool res = gInput.isSimple();
QCOMPARE( res, pair.second );
}
}

QGSTEST_MAIN( TestQgsGeometry )
#include "testqgsgeometry.moc"

0 comments on commit e001d5c

Please sign in to comment.