Skip to content

Commit 7e34bee

Browse files
committedOct 14, 2017
New API for traversal of geometry's vertices using iterator pattern
Introducing: 1. STL-style iterator: QgsAbstractGeometry::vertex_iterator 2. Java-style iterator: QgsVertexIterator (built on top of STL-style) The iterators are modeled after Qt's STL-style and Java-style iterators, the idea is to replace nextVertex() method and later introduce iterators for other bits (e.g. part_iterator, ring_iterator).
1 parent b91b854 commit 7e34bee

20 files changed

+731
-0
lines changed
 

‎python/core/geometry/qgsabstractgeometry.sip

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,56 @@ Returns the centroid of the geometry
463463
:rtype: bool
464464
%End
465465

466+
467+
QgsVertexIterator vertices() const;
468+
%Docstring
469+
Returns Java-style iterator for traversal of vertices of the geometry
470+
.. versionadded:: 3.0
471+
:rtype: QgsVertexIterator
472+
%End
473+
474+
protected:
475+
476+
virtual bool hasChildGeometries() const;
477+
%Docstring
478+
Returns whether the geometry has any child geometries (false for point / curve, true otherwise)
479+
.. note::
480+
481+
used for vertex_iterator implementation
482+
.. versionadded:: 3.0
483+
:rtype: bool
484+
%End
485+
486+
virtual int childCount() const;
487+
%Docstring
488+
Returns number of child geometries (for geometries with child geometries) or child points (for geometries without child geometries - i.e. curve / point)
489+
.. note::
490+
491+
used for vertex_iterator implementation
492+
.. versionadded:: 3.0
493+
:rtype: int
494+
%End
495+
496+
virtual QgsAbstractGeometry *childGeometry( int index ) const;
497+
%Docstring
498+
Returns pointer to child geometry (for geometries with child geometries - i.e. geom. collection / polygon)
499+
.. note::
500+
501+
used for vertex_iterator implementation
502+
.. versionadded:: 3.0
503+
:rtype: QgsAbstractGeometry
504+
%End
505+
506+
virtual QgsPoint childPoint( int index ) const;
507+
%Docstring
508+
Returns point at index (for geometries without child geometries - i.e. curve / point)
509+
.. note::
510+
511+
used for vertex_iterator implementation
512+
.. versionadded:: 3.0
513+
:rtype: QgsPoint
514+
%End
515+
466516
protected:
467517

468518
void setZMTypeFromSubGeometry( const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType );
@@ -531,6 +581,54 @@ struct QgsVertexId
531581

532582

533583

584+
class QgsVertexIterator
585+
{
586+
%Docstring
587+
Java-style iterator for traversal of vertices of a geometry
588+
.. versionadded:: 3.0
589+
%End
590+
591+
%TypeHeaderCode
592+
#include "qgsabstractgeometry.h"
593+
%End
594+
public:
595+
QgsVertexIterator();
596+
597+
QgsVertexIterator( const QgsAbstractGeometry *geometry );
598+
%Docstring
599+
Constructs iterator for the given geometry
600+
%End
601+
602+
bool hasNext() const;
603+
%Docstring
604+
Find out whether there are more vertices
605+
:rtype: bool
606+
%End
607+
608+
QgsPoint next();
609+
%Docstring
610+
Return next vertex of the geometry (undefined behavior if hasNext() returns false before calling next())
611+
:rtype: QgsPoint
612+
%End
613+
614+
QgsVertexIterator *__iter__();
615+
%Docstring
616+
:rtype: QgsVertexIterator
617+
%End
618+
%MethodCode
619+
sipRes = sipCpp;
620+
%End
621+
622+
SIP_PYOBJECT __next__();
623+
%MethodCode
624+
if ( sipCpp->hasNext() )
625+
sipRes = sipConvertFromType( new QgsPoint( sipCpp->next() ), sipType_QgsPoint, Py_None );
626+
else
627+
PyErr_SetString( PyExc_StopIteration, "" );
628+
%End
629+
630+
};
631+
534632
/************************************************************************
535633
* This file has been generated automatically from *
536634
* *

‎python/core/geometry/qgscurve.sip

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ class QgsCurve: QgsAbstractGeometry
177177
virtual void clearCache() const;
178178

179179

180+
virtual int childCount() const;
181+
virtual QgsPoint childPoint( int index ) const;
182+
180183
};
181184

182185
/************************************************************************

‎python/core/geometry/qgscurvepolygon.sip

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ Adds an interior ring to the geometry (takes ownership)
192192

193193
virtual QgsCurvePolygon *toCurveType() const /Factory/;
194194

195+
protected:
196+
virtual int childCount() const;
197+
virtual QgsAbstractGeometry *childGeometry( int index ) const;
198+
195199
protected:
196200

197201

‎python/core/geometry/qgsgeometry.sip

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,14 @@ Returns true if WKB of the geometry is of WKBMulti* type
241241
:rtype: float
242242
%End
243243

244+
245+
QgsVertexIterator vertices() const;
246+
%Docstring
247+
Returns Java-style iterator for traversal of vertices of the geometry
248+
.. versionadded:: 3.0
249+
:rtype: QgsVertexIterator
250+
%End
251+
244252
double hausdorffDistance( const QgsGeometry &geom ) const;
245253
%Docstring
246254
Returns the Hausdorff distance between this geometry and ``geom``. This is basically a measure of how similar or dissimilar 2 geometries are.

‎python/core/geometry/qgsgeometrycollection.sip

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ Adds a geometry and takes ownership. Returns true in case of success.
168168

169169

170170

171+
protected:
172+
virtual int childCount() const;
173+
virtual QgsAbstractGeometry *childGeometry( int index ) const;
174+
171175
protected:
172176

173177
virtual bool wktOmitChildType() const;

‎python/core/geometry/qgspoint.sip

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,11 @@ class QgsPoint: QgsAbstractGeometry
410410
virtual bool convertTo( QgsWkbTypes::Type type );
411411

412412

413+
414+
protected:
415+
virtual int childCount() const;
416+
virtual QgsPoint childPoint( int index ) const;
417+
413418
};
414419

415420

‎src/core/geometry/qgsabstractgeometry.cpp

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,22 @@ bool QgsAbstractGeometry::convertTo( QgsWkbTypes::Type type )
246246
return true;
247247
}
248248

249+
QgsVertexIterator QgsAbstractGeometry::vertices() const
250+
{
251+
return QgsVertexIterator( this );
252+
}
253+
254+
bool QgsAbstractGeometry::hasChildGeometries() const
255+
{
256+
return QgsWkbTypes::isMultiType( wkbType() ) || dimension() == 2;
257+
}
258+
259+
QgsPoint QgsAbstractGeometry::childPoint( int index ) const
260+
{
261+
Q_UNUSED( index );
262+
return QgsPoint();
263+
}
264+
249265
bool QgsAbstractGeometry::isEmpty() const
250266
{
251267
QgsVertexId vId;
@@ -265,3 +281,112 @@ QgsAbstractGeometry *QgsAbstractGeometry::segmentize( double tolerance, Segmenta
265281
return clone();
266282
}
267283

284+
285+
QgsAbstractGeometry::vertex_iterator::vertex_iterator( const QgsAbstractGeometry *g, int index )
286+
: depth( 0 )
287+
{
288+
::memset( levels, 0, sizeof( Level ) * 3 ); // make sure we clean up also the padding areas (for memcmp test in operator==)
289+
levels[0].g = g;
290+
levels[0].index = index;
291+
292+
digDown(); // go to the leaf level of the first vertex
293+
}
294+
295+
QgsAbstractGeometry::vertex_iterator &QgsAbstractGeometry::vertex_iterator::operator++()
296+
{
297+
if ( depth == 0 && levels[0].index >= levels[0].g->childCount() )
298+
return *this; // end of geometry - nowhere else to go
299+
300+
Q_ASSERT( !levels[depth].g->hasChildGeometries() ); // we should be at a leaf level
301+
302+
++levels[depth].index;
303+
304+
// traverse up if we are at the end in the current level
305+
while ( depth > 0 && levels[depth].index >= levels[depth].g->childCount() )
306+
{
307+
--depth;
308+
++levels[depth].index;
309+
}
310+
311+
digDown(); // go to the leaf level again
312+
313+
return *this;
314+
}
315+
316+
QgsAbstractGeometry::vertex_iterator QgsAbstractGeometry::vertex_iterator::operator++( int )
317+
{
318+
vertex_iterator it( *this );
319+
++*this;
320+
return it;
321+
}
322+
323+
QgsPoint QgsAbstractGeometry::vertex_iterator::operator*() const
324+
{
325+
Q_ASSERT( !levels[depth].g->hasChildGeometries() );
326+
return levels[depth].g->childPoint( levels[depth].index );
327+
}
328+
329+
QgsVertexId QgsAbstractGeometry::vertex_iterator::vertexId() const
330+
{
331+
int part = 0, ring = 0, vertex = levels[depth].index;
332+
if ( depth == 0 )
333+
{
334+
// nothing else to do
335+
}
336+
else if ( depth == 1 )
337+
{
338+
if ( QgsWkbTypes::isMultiType( levels[0].g->wkbType() ) )
339+
part = levels[0].index;
340+
else
341+
ring = levels[0].index;
342+
}
343+
else if ( depth == 2 )
344+
{
345+
part = levels[0].index;
346+
ring = levels[1].index;
347+
}
348+
else
349+
{
350+
Q_ASSERT( false );
351+
return QgsVertexId();
352+
}
353+
354+
// get the vertex type: find out from the leaf geometry
355+
QgsVertexId::VertexType vertexType = QgsVertexId::SegmentVertex;
356+
if ( const QgsCurve *curve = dynamic_cast<const QgsCurve *>( levels[depth].g ) )
357+
{
358+
QgsPoint p;
359+
curve->pointAt( vertex, p, vertexType );
360+
}
361+
362+
return QgsVertexId( part, ring, vertex, vertexType );
363+
}
364+
365+
bool QgsAbstractGeometry::vertex_iterator::operator==( const QgsAbstractGeometry::vertex_iterator &other ) const
366+
{
367+
if ( depth != other.depth )
368+
return false;
369+
int res = ::memcmp( levels, other.levels, sizeof( Level ) * ( depth + 1 ) );
370+
return res == 0;
371+
}
372+
373+
void QgsAbstractGeometry::vertex_iterator::digDown()
374+
{
375+
if ( levels[depth].g->hasChildGeometries() && levels[depth].index >= levels[depth].g->childCount() )
376+
return; // first check we are not already at the end
377+
378+
// while not "final" depth for the geom: go one level down.
379+
while ( levels[depth].g->hasChildGeometries() )
380+
{
381+
++depth;
382+
Q_ASSERT( depth < 3 ); // that's capacity of the levels array
383+
levels[depth].index = 0;
384+
levels[depth].g = levels[depth - 1].g->childGeometry( levels[depth - 1].index );
385+
}
386+
}
387+
388+
QgsPoint QgsVertexIterator::next()
389+
{
390+
n = i++;
391+
return *n;
392+
}

0 commit comments

Comments
 (0)
Please sign in to comment.