Skip to content

Commit

Permalink
Add DE-9IM variant which tests against a specified pattern
Browse files Browse the repository at this point in the history
(available in PyQGIS/expression engine)
  • Loading branch information
nyalldawson committed Nov 4, 2015
1 parent 9e84fca commit da94223
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 14 deletions.
10 changes: 10 additions & 0 deletions python/core/geometry/qgsgeometryengine.sip
Expand Up @@ -42,6 +42,16 @@ class QgsGeometryEngine
*/
virtual QString relate( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;

/** Tests whether two geometries are related by a specified Dimensional Extended 9 Intersection Model (DE-9IM)
* pattern.
* @param geom geometry to relate to
* @param pattern DE-9IM pattern for match
* @param errorMsg destination storage for any error message
* @returns true if geometry relationship matches with pattern
* @note added in QGIS 2.14
*/
virtual bool relatePattern( const QgsAbstractGeometryV2& geom, const QString& pattern, QString* errorMsg = 0 ) const = 0;

virtual double area( QString* errorMsg = 0 ) const = 0;
virtual double length( QString* errorMsg = 0 ) const = 0;
virtual bool isValid( QString* errorMsg = 0 ) const = 0;
Expand Down
27 changes: 19 additions & 8 deletions resources/function_help/json/relate
@@ -1,12 +1,23 @@
{
"name": "relate",
"type": "function",
"description": "Returns the Dimensional Extended 9 Intersection Model (DE-9IM) representation of the relationship between two geometries.",
"arguments": [
{"arg":"geometry","description":"a geometry"},
{"arg":"geometry","description":"a geometry"}
],
"examples": [
{ "expression":"relate( geom_from_wkt( 'LINESTRING(40 40,120 120)' ), geom_from_wkt( 'LINESTRING(40 40,60 120)' ) )", "returns":"'FF1F00102'"}
]
"description": "Tests the Dimensional Extended 9 Intersection Model (DE-9IM) representation of the relationship between two geometries.",
"variants": [
{ "variant": "Relationship variant",
"variant_description": "Returns the Dimensional Extended 9 Intersection Model (DE-9IM) representation of the relationship between two geometries.",
"arguments": [
{"arg":"geometry","description":"a geometry"},
{"arg":"geometry","description":"a geometry"}
],
"examples": [ { "expression":"relate( geom_from_wkt( 'LINESTRING(40 40,120 120)' ), geom_from_wkt( 'LINESTRING(40 40,60 120)' ) )", "returns":"'FF1F00102'" } ] },
{
"variant": "Pattern match variant",
"variant_description": "Tests whether the DE-9IM relationship between two geometries matches a specified pattern.",
"arguments": [
{"arg":"geometry","description":"a geometry"},
{"arg":"geometry","description":"a geometry"},
{"arg":"pattern","description":"DE-9IM pattern to match"}
],
"examples": [ { "expression":"relate( geom_from_wkt( 'LINESTRING(40 40,120 120)' ), geom_from_wkt( 'LINESTRING(40 40,60 120)' ), '**1F001**' )", "returns":true}]
}]
}
10 changes: 10 additions & 0 deletions src/core/geometry/qgsgeometryengine.h
Expand Up @@ -69,6 +69,16 @@ class CORE_EXPORT QgsGeometryEngine
*/
virtual QString relate( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const = 0;

/** Tests whether two geometries are related by a specified Dimensional Extended 9 Intersection Model (DE-9IM)
* pattern.
* @param geom geometry to relate to
* @param pattern DE-9IM pattern for match
* @param errorMsg destination storage for any error message
* @returns true if geometry relationship matches with pattern
* @note added in QGIS 2.14
*/
virtual bool relatePattern( const QgsAbstractGeometryV2& geom, const QString& pattern, QString* errorMsg = 0 ) const = 0;

virtual double area( QString* errorMsg = 0 ) const = 0;
virtual double length( QString* errorMsg = 0 ) const = 0;
virtual bool isValid( QString* errorMsg = 0 ) const = 0;
Expand Down
31 changes: 30 additions & 1 deletion src/core/geometry/qgsgeos.cpp
Expand Up @@ -300,6 +300,35 @@ QString QgsGeos::relate( const QgsAbstractGeometryV2& geom, QString* errorMsg )
return result;
}

bool QgsGeos::relatePattern( const QgsAbstractGeometryV2& geom, const QString& pattern, QString* errorMsg ) const
{
if ( !mGeos )
{
return false;
}

GEOSGeomScopedPtr geosGeom( asGeos( &geom, mPrecision ) );
if ( !geosGeom )
{
return false;
}

bool result = false;
try
{
result = ( GEOSRelatePattern_r( geosinit.ctxt, mGeos, geosGeom.get(), pattern.toLocal8Bit().constData() ) == 1 );
}
catch ( GEOSException &e )
{
if ( errorMsg )
{
*errorMsg = e.what();
}
}

return result;
}

double QgsGeos::area( QString* errorMsg ) const
{
double area = -1.0;
Expand Down Expand Up @@ -1527,7 +1556,7 @@ GEOSGeometry* QgsGeos::createGeosPoint( const QgsAbstractGeometryV2* point, int
}
}
#if 0 //disabled until geos supports m-coordinates
if (pt->isMeasure() )
if ( pt->isMeasure() )
{
GEOSCoordSeq_setOrdinate_r( geosinit.ctxt, coordSeq, 0, 3, pt->m() );
}
Expand Down
1 change: 1 addition & 0 deletions src/core/geometry/qgsgeos.h
Expand Up @@ -62,6 +62,7 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
bool contains( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
bool disjoint( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
QString relate( const QgsAbstractGeometryV2& geom, QString* errorMsg = 0 ) const override;
bool relatePattern( const QgsAbstractGeometryV2& geom, const QString& pattern, QString* errorMsg = 0 ) const override;
double area( QString* errorMsg = 0 ) const override;
double length( QString* errorMsg = 0 ) const override;
bool isValid( QString* errorMsg = 0 ) const override;
Expand Down
23 changes: 18 additions & 5 deletions src/core/qgsexpression.cpp
Expand Up @@ -1477,17 +1477,30 @@ static QVariant fcnYMax( const QVariantList& values, const QgsExpressionContext*

static QVariant fcnRelate( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
if ( values.length() < 2 || values.length() > 3 )
return QVariant();

QgsGeometry fGeom = getGeometry( values.at( 0 ), parent );
QgsGeometry sGeom = getGeometry( values.at( 1 ), parent );

if ( fGeom.isEmpty() || sGeom.isEmpty() )
return QVariant();

QgsGeometryEngine* engine = QgsGeometry::createGeometryEngine( fGeom.geometry() );
QString result = engine->relate( *sGeom.geometry() );
delete engine;
QScopedPointer<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( fGeom.geometry() ) );

return QVariant::fromValue( result );
if ( values.length() == 2 )
{
//two geometry arguments, return relation
QString result = engine->relate( *sGeom.geometry() );
return QVariant::fromValue( result );
}
else
{
//three arguments, test pattern
QString pattern = getStringValue( values.at( 2 ), parent );
bool result = engine->relatePattern( *sGeom.geometry(), pattern );
return QVariant::fromValue( result );
}
}

static QVariant fcnBbox( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
Expand Down Expand Up @@ -2309,7 +2322,7 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
<< new StaticFunction( "y_max", 1, fcnYMax, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "ymax" )
<< new StaticFunction( "geom_from_wkt", 1, fcnGeomFromWKT, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "geomFromWKT" )
<< new StaticFunction( "geom_from_gml", 1, fcnGeomFromGML, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "geomFromGML" )
<< new StaticFunction( "relate", 2, fcnRelate, "GeometryGroup" )
<< new StaticFunction( "relate", -1, fcnRelate, "GeometryGroup" )
<< new StaticFunction( "intersects_bbox", 2, fcnBbox, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "bbox" )
<< new StaticFunction( "disjoint", 2, fcnDisjoint, "GeometryGroup" )
<< new StaticFunction( "intersects", 2, fcnIntersects, "GeometryGroup" )
Expand Down
2 changes: 2 additions & 0 deletions tests/src/core/testqgsexpression.cpp
Expand Up @@ -452,6 +452,8 @@ class TestQgsExpression: public QObject
QTest::newRow( "relate valid" ) << "relate(geom_from_wkt('POINT(110 120)'),geom_from_wkt('POLYGON((60 120,60 40,160 40,160 120,60 120))'))" << false << QVariant( "F0FFFF212" );
QTest::newRow( "relate bad 1" ) << "relate(geom_from_wkt(''),geom_from_wkt('POLYGON((60 120,60 40,160 40,160 120,60 120))'))" << false << QVariant();
QTest::newRow( "relate bad 2" ) << "relate(geom_from_wkt('POINT(110 120)'),geom_from_wkt(''))" << false << QVariant();
QTest::newRow( "relate pattern true" ) << "relate( geom_from_wkt( 'LINESTRING(40 40,120 120)' ), geom_from_wkt( 'LINESTRING(40 40,60 120)' ), '**1F001**' )" << false << QVariant( true );
QTest::newRow( "relate pattern false" ) << "relate( geom_from_wkt( 'LINESTRING(40 40,120 120)' ), geom_from_wkt( 'LINESTRING(40 40,60 120)' ), '**1F002**' )" << false << QVariant( false );

// string functions
QTest::newRow( "lower" ) << "lower('HeLLo')" << false << QVariant( "hello" );
Expand Down

0 comments on commit da94223

Please sign in to comment.