Skip to content

Commit

Permalink
Move extraction of linestrings out of QgsExpression file + add unit test
Browse files Browse the repository at this point in the history
  • Loading branch information
wonder-sk committed Jan 27, 2016
1 parent 5210c12 commit 9fd6b24
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 46 deletions.
42 changes: 42 additions & 0 deletions src/core/geometry/qgsgeometryutils.cpp
Expand Up @@ -14,10 +14,52 @@ email : marco.hugentobler at sourcepole dot com
***************************************************************************/

#include "qgsgeometryutils.h"

#include "qgscurvev2.h"
#include "qgscurvepolygonv2.h"
#include "qgsgeometrycollectionv2.h"
#include "qgslinestringv2.h"
#include "qgswkbptr.h"

#include <QStringList>
#include <QVector>

QList<QgsLineStringV2*> QgsGeometryUtils::extractLineStrings(const QgsAbstractGeometryV2* geom)
{
QList< QgsLineStringV2* > linestrings;
if ( !geom )
return linestrings;

QList< const QgsAbstractGeometryV2 * > geometries;
geometries << geom;
while ( ! geometries.isEmpty() )
{
const QgsAbstractGeometryV2* g = geometries.takeFirst();
if ( const QgsCurveV2* curve = dynamic_cast< const QgsCurveV2* >( g ) )
{
linestrings << static_cast< QgsLineStringV2* >( curve->segmentize() );
}
else if ( const QgsGeometryCollectionV2* collection = dynamic_cast< const QgsGeometryCollectionV2* >( g ) )
{
for ( int i = 0; i < collection->numGeometries(); ++i )
{
geometries.append( collection->geometryN( i ) );
}
}
else if ( const QgsCurvePolygonV2* curvePolygon = dynamic_cast< const QgsCurvePolygonV2* >( g ) )
{
if ( curvePolygon->exteriorRing() )
linestrings << static_cast< QgsLineStringV2* >( curvePolygon->exteriorRing()->segmentize() );

for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
{
linestrings << static_cast< QgsLineStringV2* >( curvePolygon->interiorRing( i )->segmentize() );
}
}
}
return linestrings;
}

QgsPointV2 QgsGeometryUtils::closestVertex( const QgsAbstractGeometryV2& geom, const QgsPointV2& pt, QgsVertexId& id )
{
double minDist = std::numeric_limits<double>::max();
Expand Down
7 changes: 7 additions & 0 deletions src/core/geometry/qgsgeometryutils.h
Expand Up @@ -19,6 +19,8 @@ email : marco.hugentobler at sourcepole dot com
#include "qgspointv2.h"
#include <limits>

class QgsLineStringV2;

/** \ingroup core
* \class QgsGeometryUtils
* \brief Contains various geometry utility functions.
Expand All @@ -30,6 +32,11 @@ class CORE_EXPORT QgsGeometryUtils
{
public:

/** Returns list of linestrings extracted from the passed geometry. The returned objects
* have to be deleted by the caller.
*/
static QList<QgsLineStringV2*> extractLineStrings( const QgsAbstractGeometryV2* geom );

/** Returns the closest vertex to a geometry for a specified point
*/
static QgsPointV2 closestVertex( const QgsAbstractGeometryV2& geom, const QgsPointV2& pt, QgsVertexId& id );
Expand Down
48 changes: 2 additions & 46 deletions src/core/qgsexpression.cpp
Expand Up @@ -29,6 +29,7 @@
#include "qgsfeature.h"
#include "qgsgeometry.h"
#include "qgsgeometryengine.h"
#include "qgsgeometryutils.h"
#include "qgslogger.h"
#include "qgsmaplayerregistry.h"
#include "qgsogcutils.h"
Expand Down Expand Up @@ -1432,52 +1433,7 @@ static QVariant fcnSegmentsToLines( const QVariantList& values, const QgsExpress
if ( geom.isEmpty() )
return QVariant();

QList< QgsAbstractGeometryV2 * > geometries;

QgsGeometryCollectionV2* collection = dynamic_cast< QgsGeometryCollectionV2* >( geom.geometry() );
if ( collection )
{
for ( int i = 0; i < collection->numGeometries(); ++i )
{
geometries.append( collection->geometryN( i ) );
}
}
else
{
geometries.append( geom.geometry() );
}

QList< QgsLineStringV2* > linesToProcess;
while ( ! geometries.isEmpty() )
{
QgsAbstractGeometryV2* g = geometries.takeFirst();
QgsCurveV2* curve = dynamic_cast< QgsCurveV2* >( g );
if ( curve )
{
linesToProcess << static_cast< QgsLineStringV2* >( curve->segmentize() );
continue;
}
QgsGeometryCollectionV2* collection = dynamic_cast< QgsGeometryCollectionV2* >( g );
if ( collection )
{
for ( int i = 0; i < collection->numGeometries(); ++i )
{
geometries.append( collection->geometryN( i ) );
}
}
QgsCurvePolygonV2* curvePolygon = dynamic_cast< QgsCurvePolygonV2* >( g );
if ( curvePolygon )
{
if ( curvePolygon->exteriorRing() )
linesToProcess << static_cast< QgsLineStringV2* >( curvePolygon->exteriorRing()->segmentize() );

for ( int i = 0; i < curvePolygon->numInteriorRings(); ++i )
{
linesToProcess << static_cast< QgsLineStringV2* >( curvePolygon->interiorRing( i )->segmentize() );
}
continue;
}
}
QList< QgsLineStringV2* > linesToProcess = QgsGeometryUtils::extractLineStrings( geom.geometry() );

//ok, now we have a complete list of segmentized lines from the geometry
QgsMultiLineStringV2* ml = new QgsMultiLineStringV2();
Expand Down
30 changes: 30 additions & 0 deletions tests/src/core/testqgsgeometryutils.cpp
Expand Up @@ -16,11 +16,15 @@
#include <QtTest/QtTest>
#include <QObject>
#include "qgsgeometryutils.h"
#include "qgslinestringv2.h"
#include "qgspolygonv2.h"
#include "qgsmultipolygonv2.h"

class TestQgsGeometryUtils: public QObject
{
Q_OBJECT
private slots:
void testExtractLinestrings();
void testCircleClockwise_data();
void testCircleClockwise();
void testAngleOnCircle_data();
Expand All @@ -41,6 +45,32 @@ class TestQgsGeometryUtils: public QObject
void testAverageAngle();
};


void TestQgsGeometryUtils::testExtractLinestrings()
{
QgsLineStringV2* outerRing1 = new QgsLineStringV2();
outerRing1->setPoints( QList<QgsPointV2>() << QgsPointV2( 1, 1 ) << QgsPointV2( 1, 2 ) << QgsPointV2( 2, 2 ) << QgsPointV2( 2, 1 ) << QgsPointV2( 1, 1 ) );
QgsPolygonV2* polygon1 = new QgsPolygonV2();
polygon1->setExteriorRing( outerRing1 );

QgsLineStringV2* outerRing2 = new QgsLineStringV2();
outerRing2->setPoints( QList<QgsPointV2>() << QgsPointV2( 10, 10 ) << QgsPointV2( 10, 20 ) << QgsPointV2( 20, 20 ) << QgsPointV2( 20, 10 ) << QgsPointV2( 10, 10 ) );
QgsPolygonV2* polygon2 = new QgsPolygonV2();
polygon2->setExteriorRing( outerRing2 );

QgsLineStringV2* innerRing2 = new QgsLineStringV2();
innerRing2->setPoints( QList<QgsPointV2>() << QgsPointV2( 14, 14 ) << QgsPointV2( 14, 16 ) << QgsPointV2( 16, 16 ) << QgsPointV2( 16, 14 ) << QgsPointV2( 14, 14 ) );
polygon2->setInteriorRings( QList<QgsCurveV2*>() << innerRing2 );

QgsMultiPolygonV2 mpg;
mpg.addGeometry( polygon1 );
mpg.addGeometry( polygon2 );

QList<QgsLineStringV2*> linestrings = QgsGeometryUtils::extractLineStrings( &mpg );
QCOMPARE( linestrings.count(), 3 );
qDeleteAll( linestrings );
}

void TestQgsGeometryUtils::testLeftOfLine_data()
{
QTest::addColumn<double>( "x" );
Expand Down

2 comments on commit 9fd6b24

@nyalldawson
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is QgsGeometryUtils the correct place for this? Or should it be in QgsInternalGeometryEngine and exposed via QgsGeometry?

@wonder-sk
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not really have a preference where should it go - in theory QgsGeometryUtils and QgsInternalGeometryEngine could be just one class - with assorted geometry functionality.

I would prefer not to expose it via QgsGeometry - that interface is already way too long - but I do not have a strong opinion.

Please sign in to comment.