Skip to content

Commit

Permalink
New API for traversal of geometry's vertices using iterator pattern
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
wonder-sk committed Oct 14, 2017
1 parent b91b854 commit 7e34bee
Show file tree
Hide file tree
Showing 20 changed files with 731 additions and 0 deletions.
98 changes: 98 additions & 0 deletions python/core/geometry/qgsabstractgeometry.sip
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down Expand Up @@ -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 *
* *
Expand Down
3 changes: 3 additions & 0 deletions python/core/geometry/qgscurve.sip
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ class QgsCurve: QgsAbstractGeometry
virtual void clearCache() const;


virtual int childCount() const;
virtual QgsPoint childPoint( int index ) const;

};

/************************************************************************
Expand Down
4 changes: 4 additions & 0 deletions python/core/geometry/qgscurvepolygon.sip
Original file line number Diff line number Diff line change
Expand Up @@ -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:


Expand Down
8 changes: 8 additions & 0 deletions python/core/geometry/qgsgeometry.sip
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions python/core/geometry/qgsgeometrycollection.sip
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions python/core/geometry/qgspoint.sip
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,11 @@ class QgsPoint: QgsAbstractGeometry
virtual bool convertTo( QgsWkbTypes::Type type );



protected:
virtual int childCount() const;
virtual QgsPoint childPoint( int index ) const;

};


Expand Down
125 changes: 125 additions & 0 deletions src/core/geometry/qgsabstractgeometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Loading

0 comments on commit 7e34bee

Please sign in to comment.