Merge pull request #5367 from wonder-sk/vertex-iterator-2

API for traversal of geometry's vertices using iterator pattern
This commit is contained in:
Martin Dobias 2017-10-15 10:48:25 +02:00 committed by GitHub
commit ae4d26a675
20 changed files with 731 additions and 0 deletions

View File

@ -463,6 +463,56 @@ Returns the centroid of the geometry
:rtype: bool
%End
QgsVertexIterator vertices() const;
%Docstring
Returns Java-style iterator for traversal of vertices of the geometry
.. versionadded:: 3.0
:rtype: QgsVertexIterator
%End
protected:
virtual bool hasChildGeometries() const;
%Docstring
Returns whether the geometry has any child geometries (false for point / curve, true otherwise)
.. note::
used for vertex_iterator implementation
.. versionadded:: 3.0
:rtype: bool
%End
virtual int childCount() const;
%Docstring
Returns number of child geometries (for geometries with child geometries) or child points (for geometries without child geometries - i.e. curve / point)
.. note::
used for vertex_iterator implementation
.. versionadded:: 3.0
:rtype: int
%End
virtual QgsAbstractGeometry *childGeometry( int index ) const;
%Docstring
Returns pointer to child geometry (for geometries with child geometries - i.e. geom. collection / polygon)
.. note::
used for vertex_iterator implementation
.. versionadded:: 3.0
:rtype: QgsAbstractGeometry
%End
virtual QgsPoint childPoint( int index ) const;
%Docstring
Returns point at index (for geometries without child geometries - i.e. curve / point)
.. note::
used for vertex_iterator implementation
.. versionadded:: 3.0
:rtype: QgsPoint
%End
protected:
void setZMTypeFromSubGeometry( const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType );
@ -531,6 +581,54 @@ struct QgsVertexId
class QgsVertexIterator
{
%Docstring
Java-style iterator for traversal of vertices of a geometry
.. versionadded:: 3.0
%End
%TypeHeaderCode
#include "qgsabstractgeometry.h"
%End
public:
QgsVertexIterator();
QgsVertexIterator( const QgsAbstractGeometry *geometry );
%Docstring
Constructs iterator for the given geometry
%End
bool hasNext() const;
%Docstring
Find out whether there are more vertices
:rtype: bool
%End
QgsPoint next();
%Docstring
Return next vertex of the geometry (undefined behavior if hasNext() returns false before calling next())
:rtype: QgsPoint
%End
QgsVertexIterator *__iter__();
%Docstring
:rtype: QgsVertexIterator
%End
%MethodCode
sipRes = sipCpp;
%End
SIP_PYOBJECT __next__();
%MethodCode
if ( sipCpp->hasNext() )
sipRes = sipConvertFromType( new QgsPoint( sipCpp->next() ), sipType_QgsPoint, Py_None );
else
PyErr_SetString( PyExc_StopIteration, "" );
%End
};
/************************************************************************
* This file has been generated automatically from *
* *

View File

@ -177,6 +177,9 @@ class QgsCurve: QgsAbstractGeometry
virtual void clearCache() const;
virtual int childCount() const;
virtual QgsPoint childPoint( int index ) const;
};
/************************************************************************

View File

@ -192,6 +192,10 @@ Adds an interior ring to the geometry (takes ownership)
virtual QgsCurvePolygon *toCurveType() const /Factory/;
protected:
virtual int childCount() const;
virtual QgsAbstractGeometry *childGeometry( int index ) const;
protected:

View File

@ -241,6 +241,14 @@ Returns true if WKB of the geometry is of WKBMulti* type
:rtype: float
%End
QgsVertexIterator vertices() const;
%Docstring
Returns Java-style iterator for traversal of vertices of the geometry
.. versionadded:: 3.0
:rtype: QgsVertexIterator
%End
double hausdorffDistance( const QgsGeometry &geom ) const;
%Docstring
Returns the Hausdorff distance between this geometry and ``geom``. This is basically a measure of how similar or dissimilar 2 geometries are.

View File

@ -168,6 +168,10 @@ Adds a geometry and takes ownership. Returns true in case of success.
protected:
virtual int childCount() const;
virtual QgsAbstractGeometry *childGeometry( int index ) const;
protected:
virtual bool wktOmitChildType() const;

View File

@ -410,6 +410,11 @@ class QgsPoint: QgsAbstractGeometry
virtual bool convertTo( QgsWkbTypes::Type type );
protected:
virtual int childCount() const;
virtual QgsPoint childPoint( int index ) const;
};

View File

@ -246,6 +246,22 @@ bool QgsAbstractGeometry::convertTo( QgsWkbTypes::Type type )
return true;
}
QgsVertexIterator QgsAbstractGeometry::vertices() const
{
return QgsVertexIterator( this );
}
bool QgsAbstractGeometry::hasChildGeometries() const
{
return QgsWkbTypes::isMultiType( wkbType() ) || dimension() == 2;
}
QgsPoint QgsAbstractGeometry::childPoint( int index ) const
{
Q_UNUSED( index );
return QgsPoint();
}
bool QgsAbstractGeometry::isEmpty() const
{
QgsVertexId vId;
@ -265,3 +281,112 @@ QgsAbstractGeometry *QgsAbstractGeometry::segmentize( double tolerance, Segmenta
return clone();
}
QgsAbstractGeometry::vertex_iterator::vertex_iterator( const QgsAbstractGeometry *g, int index )
: depth( 0 )
{
::memset( levels, 0, sizeof( Level ) * 3 ); // make sure we clean up also the padding areas (for memcmp test in operator==)
levels[0].g = g;
levels[0].index = index;
digDown(); // go to the leaf level of the first vertex
}
QgsAbstractGeometry::vertex_iterator &QgsAbstractGeometry::vertex_iterator::operator++()
{
if ( depth == 0 && levels[0].index >= levels[0].g->childCount() )
return *this; // end of geometry - nowhere else to go
Q_ASSERT( !levels[depth].g->hasChildGeometries() ); // we should be at a leaf level
++levels[depth].index;
// traverse up if we are at the end in the current level
while ( depth > 0 && levels[depth].index >= levels[depth].g->childCount() )
{
--depth;
++levels[depth].index;
}
digDown(); // go to the leaf level again
return *this;
}
QgsAbstractGeometry::vertex_iterator QgsAbstractGeometry::vertex_iterator::operator++( int )
{
vertex_iterator it( *this );
++*this;
return it;
}
QgsPoint QgsAbstractGeometry::vertex_iterator::operator*() const
{
Q_ASSERT( !levels[depth].g->hasChildGeometries() );
return levels[depth].g->childPoint( levels[depth].index );
}
QgsVertexId QgsAbstractGeometry::vertex_iterator::vertexId() const
{
int part = 0, ring = 0, vertex = levels[depth].index;
if ( depth == 0 )
{
// nothing else to do
}
else if ( depth == 1 )
{
if ( QgsWkbTypes::isMultiType( levels[0].g->wkbType() ) )
part = levels[0].index;
else
ring = levels[0].index;
}
else if ( depth == 2 )
{
part = levels[0].index;
ring = levels[1].index;
}
else
{
Q_ASSERT( false );
return QgsVertexId();
}
// get the vertex type: find out from the leaf geometry
QgsVertexId::VertexType vertexType = QgsVertexId::SegmentVertex;
if ( const QgsCurve *curve = dynamic_cast<const QgsCurve *>( levels[depth].g ) )
{
QgsPoint p;
curve->pointAt( vertex, p, vertexType );
}
return QgsVertexId( part, ring, vertex, vertexType );
}
bool QgsAbstractGeometry::vertex_iterator::operator==( const QgsAbstractGeometry::vertex_iterator &other ) const
{
if ( depth != other.depth )
return false;
int res = ::memcmp( levels, other.levels, sizeof( Level ) * ( depth + 1 ) );
return res == 0;
}
void QgsAbstractGeometry::vertex_iterator::digDown()
{
if ( levels[depth].g->hasChildGeometries() && levels[depth].index >= levels[depth].g->childCount() )
return; // first check we are not already at the end
// while not "final" depth for the geom: go one level down.
while ( levels[depth].g->hasChildGeometries() )
{
++depth;
Q_ASSERT( depth < 3 ); // that's capacity of the levels array
levels[depth].index = 0;
levels[depth].g = levels[depth - 1].g->childGeometry( levels[depth - 1].index );
}
}
QgsPoint QgsVertexIterator::next()
{
n = i++;
return *n;
}

View File

@ -30,6 +30,7 @@ class QgsMultiCurve;
class QgsMultiPointV2;
class QgsPoint;
struct QgsVertexId;
class QgsVertexIterator;
class QPainter;
class QDomDocument;
class QDomElement;
@ -463,6 +464,114 @@ class CORE_EXPORT QgsAbstractGeometry
*/
virtual bool convertTo( QgsWkbTypes::Type type );
#ifndef SIP_RUN
/**
* \ingroup core
* The vertex_iterator class provides STL-style iterator for vertices.
* \since QGIS 3.0
*/
class CORE_EXPORT vertex_iterator
{
private:
/**
* A helper structure to keep track of vertex traversal within one level within a geometry.
* For example, linestring geometry will have just one level, while multi-polygon has three levels
* (part index, ring index, vertex index).
*/
struct Level
{
const QgsAbstractGeometry *g; //!< Current geometry
int index; //!< Ptr in the current geometry
};
Level levels[3]; //!< Stack of levels - three levels should be sufficient (e.g. part index, ring index, vertex index)
int depth; //!< At what depth level are we right now
void digDown(); //!< Prepare the stack of levels so that it points to a leaf child geometry
public:
//! Create invalid iterator
vertex_iterator() : depth( -1 ) {}
//! Create vertex iterator for a geometry
vertex_iterator( const QgsAbstractGeometry *g, int index );
/**
* The prefix ++ operator (++it) advances the iterator to the next vertex and returns an iterator to the new current vertex.
* Calling this function on iterator that is already past the last item leads to undefined results.
*/
vertex_iterator &operator++();
//! The postfix ++ operator (it++) advances the iterator to the next vertex and returns an iterator to the previously current vertex.
vertex_iterator operator++( int );
//! Returns the current item.
QgsPoint operator*() const;
//! Returns vertex ID of the current item.
QgsVertexId vertexId() const;
bool operator==( const vertex_iterator &other ) const;
bool operator!=( const vertex_iterator &other ) const { return !( *this == other ); }
};
/**
* Returns STL-style iterator pointing to the first vertex of the geometry
* \since QGIS 3.0
*/
vertex_iterator vertices_begin() const
{
return vertex_iterator( this, 0 );
}
/**
* Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry
* \since QGIS 3.0
*/
vertex_iterator vertices_end() const
{
return vertex_iterator( this, childCount() );
}
#endif
/**
* Returns Java-style iterator for traversal of vertices of the geometry
* \since QGIS 3.0
*/
QgsVertexIterator vertices() const;
protected:
/**
* Returns whether the geometry has any child geometries (false for point / curve, true otherwise)
* \note used for vertex_iterator implementation
* \since QGIS 3.0
*/
virtual bool hasChildGeometries() const;
/**
* Returns number of child geometries (for geometries with child geometries) or child points (for geometries without child geometries - i.e. curve / point)
* \note used for vertex_iterator implementation
* \since QGIS 3.0
*/
virtual int childCount() const { return 0; }
/**
* Returns pointer to child geometry (for geometries with child geometries - i.e. geom. collection / polygon)
* \note used for vertex_iterator implementation
* \since QGIS 3.0
*/
virtual QgsAbstractGeometry *childGeometry( int index ) const { Q_UNUSED( index ); return nullptr; }
/**
* Returns point at index (for geometries without child geometries - i.e. curve / point)
* \note used for vertex_iterator implementation
* \since QGIS 3.0
*/
virtual QgsPoint childPoint( int index ) const;
protected:
QgsWkbTypes::Type mWkbType = QgsWkbTypes::Unknown;
@ -554,4 +663,51 @@ inline T qgsgeometry_cast( const QgsAbstractGeometry *geom )
// clazy:excludeall=qstring-allocations
/**
* \ingroup core
* \brief Java-style iterator for traversal of vertices of a geometry
* \since QGIS 3.0
*/
class CORE_EXPORT QgsVertexIterator
{
public:
QgsVertexIterator(): g( nullptr ) {}
//! Constructs iterator for the given geometry
QgsVertexIterator( const QgsAbstractGeometry *geometry )
: g( geometry )
, i( g->vertices_begin() )
, n( g->vertices_end() )
{
}
//! Find out whether there are more vertices
bool hasNext() const
{
return g && g->vertices_end() != i;
}
//! Return next vertex of the geometry (undefined behavior if hasNext() returns false before calling next())
QgsPoint next();
#ifdef SIP_RUN
QgsVertexIterator *__iter__();
% MethodCode
sipRes = sipCpp;
% End
SIP_PYOBJECT __next__();
% MethodCode
if ( sipCpp->hasNext() )
sipRes = sipConvertFromType( new QgsPoint( sipCpp->next() ), sipType_QgsPoint, Py_None );
else
PyErr_SetString( PyExc_StopIteration, "" );
% End
#endif
private:
const QgsAbstractGeometry *g;
QgsAbstractGeometry::vertex_iterator i, n;
};
#endif //QGSABSTRACTGEOMETRYV2

View File

@ -156,3 +156,16 @@ void QgsCurve::clearCache() const
QgsAbstractGeometry::clearCache();
}
int QgsCurve::childCount() const
{
return numPoints();
}
QgsPoint QgsCurve::childPoint( int index ) const
{
QgsPoint point;
QgsVertexId::VertexType type;
bool res = pointAt( index, point, type );
Q_ASSERT( res );
return point;
}

View File

@ -184,6 +184,9 @@ class CORE_EXPORT QgsCurve: public QgsAbstractGeometry
void clearCache() const override;
virtual int childCount() const override;
virtual QgsPoint childPoint( int index ) const override;
private:
mutable QgsRectangle mBoundingBox;

View File

@ -989,3 +989,16 @@ QgsCurvePolygon *QgsCurvePolygon::toCurveType() const
{
return clone();
}
int QgsCurvePolygon::childCount() const
{
return 1 + mInteriorRings.count();
}
QgsAbstractGeometry *QgsCurvePolygon::childGeometry( int index ) const
{
if ( index == 0 )
return mExteriorRing.get();
else
return mInteriorRings.at( index - 1 );
}

View File

@ -171,6 +171,10 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
return nullptr;
}
#endif
protected:
virtual int childCount() const override;
virtual QgsAbstractGeometry *childGeometry( int index ) const override;
protected:
std::unique_ptr< QgsCurve > mExteriorRing;

View File

@ -1551,6 +1551,27 @@ double QgsGeometry::hausdorffDistanceDensify( const QgsGeometry &geom, double de
return g.hausdorffDistanceDensify( geom.d->geometry.get(), densifyFraction, &mLastError );
}
QgsAbstractGeometry::vertex_iterator QgsGeometry::vertices_begin() const
{
if ( !d->geometry )
return QgsAbstractGeometry::vertex_iterator();
return d->geometry->vertices_begin();
}
QgsAbstractGeometry::vertex_iterator QgsGeometry::vertices_end() const
{
if ( !d->geometry )
return QgsAbstractGeometry::vertex_iterator();
return d->geometry->vertices_end();
}
QgsVertexIterator QgsGeometry::vertices() const
{
if ( !d->geometry )
return QgsVertexIterator();
return QgsVertexIterator( d->geometry.get() );
}
QgsGeometry QgsGeometry::buffer( double distance, int segments ) const
{
if ( !d->geometry )

View File

@ -281,6 +281,27 @@ class CORE_EXPORT QgsGeometry
*/
double distance( const QgsGeometry &geom ) const;
#ifndef SIP_RUN
/**
* Returns STL-style iterator pointing to the first vertex of the geometry
* \since QGIS 3.0
*/
QgsAbstractGeometry::vertex_iterator vertices_begin() const;
/**
* Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry
* \since QGIS 3.0
*/
QgsAbstractGeometry::vertex_iterator vertices_end() const;
#endif
/**
* Returns Java-style iterator for traversal of vertices of the geometry
* \since QGIS 3.0
*/
QgsVertexIterator vertices() const;
/**
* Returns the Hausdorff distance between this geometry and \a geom. This is basically a measure of how similar or dissimilar 2 geometries are.
*

View File

@ -733,3 +733,13 @@ bool QgsGeometryCollection::wktOmitChildType() const
{
return false;
}
int QgsGeometryCollection::childCount() const
{
return mGeometries.count();
}
QgsAbstractGeometry *QgsGeometryCollection::childGeometry( int index ) const
{
return mGeometries.at( index );
}

View File

@ -157,6 +157,10 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
}
#endif
protected:
virtual int childCount() const override;
virtual QgsAbstractGeometry *childGeometry( int index ) const override;
protected:
QVector< QgsAbstractGeometry * > mGeometries;

View File

@ -644,3 +644,14 @@ int QgsPoint::dimension() const
{
return 0;
}
int QgsPoint::childCount() const
{
return 1;
}
QgsPoint QgsPoint::childPoint( int index ) const
{
Q_ASSERT( index == 0 );
return *this;
}

View File

@ -449,6 +449,11 @@ class CORE_EXPORT QgsPoint: public QgsAbstractGeometry
return nullptr;
}
#endif
protected:
virtual int childCount() const override;
virtual QgsPoint childPoint( int index ) const override;
private:
double mX;
double mY;

View File

@ -70,6 +70,7 @@ class TestQgsGeometry : public QObject
void asVariant(); //test conversion to and from a QVariant
void isEmpty();
void operatorBool();
void vertexIterator();
// geometry types
void point(); //test QgsPointV2
@ -415,6 +416,23 @@ void TestQgsGeometry::operatorBool()
QVERIFY( !geom );
}
void TestQgsGeometry::vertexIterator()
{
QgsGeometry geom;
QgsVertexIterator it = geom.vertices();
QVERIFY( !it.hasNext() );
QgsPolyline polyline;
polyline << QgsPoint( 1, 2 ) << QgsPoint( 3, 4 );
QgsGeometry geom2 = QgsGeometry::fromPolyline( polyline );
QgsVertexIterator it2 = geom2.vertices();
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 1, 2 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 3, 4 ) );
QVERIFY( !it2.hasNext() );
}
void TestQgsGeometry::point()
{
//test QgsPointV2
@ -769,6 +787,20 @@ void TestQgsGeometry::point()
QCOMPARE( p22, p21 );
QCOMPARE( v, QgsVertexId( 1, 0, 0 ) );
// vertex iterator
QgsAbstractGeometry::vertex_iterator it1 = p21.vertices_begin();
QgsAbstractGeometry::vertex_iterator it1end = p21.vertices_end();
QCOMPARE( *it1, p21 );
QCOMPARE( it1.vertexId(), QgsVertexId( 0, 0, 0 ) );
++it1;
QCOMPARE( it1, it1end );
// Java-style iterator
QgsVertexIterator it2( &p21 );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), p21 );
QVERIFY( !it2.hasNext() );
//vertexAt - will always be same as point
QCOMPARE( p21.vertexAt( QgsVertexId() ), p21 );
QCOMPARE( p21.vertexAt( QgsVertexId( 0, 0, 0 ) ), p21 );
@ -1895,6 +1927,7 @@ void TestQgsGeometry::circularString()
QVERIFY( !l32.nextVertex( v, p ) );
v = QgsVertexId( 0, 0, 10 );
QVERIFY( !l32.nextVertex( v, p ) );
//CircularString
l32.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) );
v = QgsVertexId( 0, 0, 2 ); //out of range
@ -3568,6 +3601,15 @@ void TestQgsGeometry::lineString()
QVERIFY( !l32.nextVertex( v, p ) );
v = QgsVertexId( 0, 0, 10 );
QVERIFY( !l32.nextVertex( v, p ) );
// vertex iterator on empty linestring
QgsAbstractGeometry::vertex_iterator it1 = l32.vertices_begin();
QCOMPARE( it1, l32.vertices_end() );
// Java-style iterator on empty linetring
QgsVertexIterator it1x( &l32 );
QVERIFY( !it1x.hasNext() );
//LineString
l32.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) );
v = QgsVertexId( 0, 0, 2 ); //out of range
@ -3591,6 +3633,24 @@ void TestQgsGeometry::lineString()
QCOMPARE( v, QgsVertexId( 1, 0, 1 ) ); //test that part number is maintained
QCOMPARE( p, QgsPoint( 11, 12 ) );
// vertex iterator
QgsAbstractGeometry::vertex_iterator it2 = l32.vertices_begin();
QCOMPARE( *it2, QgsPoint( 1, 2 ) );
QCOMPARE( it2.vertexId(), QgsVertexId( 0, 0, 0 ) );
++it2;
QCOMPARE( *it2, QgsPoint( 11, 12 ) );
QCOMPARE( it2.vertexId(), QgsVertexId( 0, 0, 1 ) );
++it2;
QCOMPARE( it2, l32.vertices_end() );
// Java-style iterator
QgsVertexIterator it2x( &l32 );
QVERIFY( it2x.hasNext() );
QCOMPARE( it2x.next(), QgsPoint( 1, 2 ) );
QVERIFY( it2x.hasNext() );
QCOMPARE( it2x.next(), QgsPoint( 11, 12 ) );
QVERIFY( !it2x.hasNext() );
//LineStringZ
l32.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) );
v = QgsVertexId( 0, 0, -1 );
@ -3601,6 +3661,7 @@ void TestQgsGeometry::lineString()
QCOMPARE( v, QgsVertexId( 0, 0, 1 ) );
QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) );
QVERIFY( !l32.nextVertex( v, p ) );
//LineStringM
l32.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 14 ) );
v = QgsVertexId( 0, 0, -1 );
@ -10336,6 +10397,25 @@ void TestQgsGeometry::multiPoint()
QgsVertexId after;
// return error - points have no segments
QVERIFY( boundaryMP.closestSegment( QgsPoint( 0.5, 0.5 ), closest, after ) < 0 );
// vertex iterator
QgsAbstractGeometry::vertex_iterator it = boundaryMP.vertices_begin();
QgsAbstractGeometry::vertex_iterator itEnd = boundaryMP.vertices_end();
QCOMPARE( *it, QgsPoint( 0, 0 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 0, 0, 0 ) );
++it;
QCOMPARE( *it, QgsPoint( 1, 1 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 0, 0 ) );
++it;
QCOMPARE( it, itEnd );
// Java-style iterator
QgsVertexIterator it2( &boundaryMP );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 0, 0 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 1, 1 ) );
QVERIFY( !it2.hasNext() );
}
void TestQgsGeometry::multiLineString()
@ -10831,6 +10911,45 @@ void TestQgsGeometry::multiLineString()
QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 3 ) )->y(), 11.0 );
delete boundary;
// vertex iterator: 2 linestrings with 3 points each
QgsAbstractGeometry::vertex_iterator it = multiLine1.vertices_begin();
QgsAbstractGeometry::vertex_iterator itEnd = multiLine1.vertices_end();
QCOMPARE( *it, QgsPoint( 0, 0 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 0, 0, 0 ) );
++it;
QCOMPARE( *it, QgsPoint( 1, 0 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 0, 0, 1 ) );
++it;
QCOMPARE( *it, QgsPoint( 1, 1 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 0, 0, 2 ) );
++it;
QCOMPARE( *it, QgsPoint( 10, 10 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 0, 0 ) );
++it;
QCOMPARE( *it, QgsPoint( 11, 10 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 0, 1 ) );
++it;
QCOMPARE( *it, QgsPoint( 11, 11 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 0, 2 ) );
++it;
QCOMPARE( it, itEnd );
// Java-style iterator
QgsVertexIterator it2( &multiLine1 );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 0, 0 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 1, 0 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 1, 1 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 10, 10 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 11, 10 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 11, 11 ) );
QVERIFY( !it2.hasNext() );
// add a closed string = no boundary
QgsLineString boundaryLine3;
boundaryLine3.setPoints( QList<QgsPoint>() << QgsPoint( 20, 20 ) << QgsPoint( 21, 20 ) << QgsPoint( 21, 21 ) << QgsPoint( 20, 20 ) );
@ -12726,6 +12845,102 @@ void TestQgsGeometry::multiPolygon()
QCOMPARE( dynamic_cast< QgsLineString * >( multiLineBoundary->geometryN( 3 ) )->yAt( 1 ), 10.8 );
QCOMPARE( dynamic_cast< QgsLineString * >( multiLineBoundary->geometryN( 3 ) )->yAt( 2 ), 10.9 );
QCOMPARE( dynamic_cast< QgsLineString * >( multiLineBoundary->geometryN( 3 ) )->yAt( 3 ), 10.8 );
// vertex iterator: 2 polygons (one with just exterior ring, other with two interior rings)
QgsAbstractGeometry::vertex_iterator it = multiPolygon1.vertices_begin();
QgsAbstractGeometry::vertex_iterator itEnd = multiPolygon1.vertices_end();
QCOMPARE( *it, QgsPoint( 0, 0 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 0, 0, 0 ) );
++it;
QCOMPARE( *it, QgsPoint( 1, 0 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 0, 0, 1 ) );
++it;
QCOMPARE( *it, QgsPoint( 1, 1 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 0, 0, 2 ) );
++it;
QCOMPARE( *it, QgsPoint( 0, 0 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 0, 0, 3 ) );
++it;
// 2nd polygon - exterior ring
QCOMPARE( *it, QgsPoint( 10, 10 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 0, 0 ) );
++it;
QCOMPARE( *it, QgsPoint( 11, 10 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 0, 1 ) );
++it;
QCOMPARE( *it, QgsPoint( 11, 11 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 0, 2 ) );
++it;
QCOMPARE( *it, QgsPoint( 10, 10 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 0, 3 ) );
++it;
// 2nd polygon - 1st interior ring
QCOMPARE( *it, QgsPoint( 10.1, 10.1 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 1, 0 ) );
++it;
QCOMPARE( *it, QgsPoint( 10.2, 10.1 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 1, 1 ) );
++it;
QCOMPARE( *it, QgsPoint( 10.2, 10.2 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 1, 2 ) );
++it;
QCOMPARE( *it, QgsPoint( 10.1, 10.1 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 1, 3 ) );
++it;
// 2nd polygon - 2nd interior ring
QCOMPARE( *it, QgsPoint( 10.8, 10.8 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 2, 0 ) );
++it;
QCOMPARE( *it, QgsPoint( 10.9, 10.8 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 2, 1 ) );
++it;
QCOMPARE( *it, QgsPoint( 10.9, 10.9 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 2, 2 ) );
++it;
QCOMPARE( *it, QgsPoint( 10.8, 10.8 ) );
QCOMPARE( it.vertexId(), QgsVertexId( 1, 2, 3 ) );
++it;
// done!
QCOMPARE( it, itEnd );
// Java-style iterator
QgsVertexIterator it2( &multiPolygon1 );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 0, 0 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 1, 0 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 1, 1 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 0, 0 ) );
QVERIFY( it2.hasNext() );
// 2nd polygon - exterior ring
QCOMPARE( it2.next(), QgsPoint( 10, 10 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 11, 10 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 11, 11 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 10, 10 ) );
QVERIFY( it2.hasNext() );
// 2nd polygon - 1st interior ring
QCOMPARE( it2.next(), QgsPoint( 10.1, 10.1 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 10.2, 10.1 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 10.2, 10.2 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 10.1, 10.1 ) );
QVERIFY( it2.hasNext() );
// 2nd polygon - 2nd interior ring
QCOMPARE( it2.next(), QgsPoint( 10.8, 10.8 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 10.9, 10.8 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 10.9, 10.9 ) );
QVERIFY( it2.hasNext() );
QCOMPARE( it2.next(), QgsPoint( 10.8, 10.8 ) );
QVERIFY( !it2.hasNext() );
}
void TestQgsGeometry::geometryCollection()

View File

@ -85,6 +85,14 @@ class TestQgsGeometry(unittest.TestCase):
g = QgsGeometry.fromWkt('MultiPoint ()')
self.assertTrue(g.isEmpty())
def testVertexIterator(self):
g = QgsGeometry.fromWkt('Linestring(11 12, 13 14)')
it = g.vertices()
self.assertEqual(next(it), QgsPoint(11, 12))
self.assertEqual(next(it), QgsPoint(13, 14))
with self.assertRaises(StopIteration):
next(it)
def testWktPointLoading(self):
myWKT = 'Point (10 10)'
myGeometry = QgsGeometry.fromWkt(myWKT)