Add QgsGeometry::isSimple() - useful for self-intersection checks

This commit is contained in:
Martin Dobias 2017-05-17 21:48:52 +08:00
parent 09975cec07
commit e001d5c70a
8 changed files with 87 additions and 0 deletions

View File

@ -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

View File

@ -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,

View File

@ -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 )

View File

@ -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
*/

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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 );
@ -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"