Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New API for traversal of geometry's vertices using iterator pattern #4513

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
78 changes: 78 additions & 0 deletions python/core/geometry/qgsabstractgeometry.sip
Expand Up @@ -459,6 +459,38 @@ QgsMultiLineString -> QgsMultiCurve, QgsMultiPolygonV2 -> QgsMultiSurface
:rtype: bool
%End


QgsVertexIterator vertices() const;
%Docstring
.. versionadded:: 3.0
:rtype: QgsVertexIterator
%End

protected:
virtual bool hasChildGeometries() const;
%Docstring
.. versionadded:: 3.0
:rtype: bool
%End

virtual int childCount() const;
%Docstring
.. versionadded:: 3.0
:rtype: int
%End

virtual QgsAbstractGeometry *childGeometry( int index ) const;
%Docstring
.. versionadded:: 3.0
:rtype: QgsAbstractGeometry
%End

virtual QgsPointV2 childPoint( int index ) const;
%Docstring
.. versionadded:: 3.0
:rtype: QgsPointV2
%End

protected:

void setZMTypeFromSubGeometry( const QgsAbstractGeometry *subggeom, QgsWkbTypes::Type baseGeomType );
Expand Down Expand Up @@ -525,6 +557,52 @@ struct QgsVertexId
VertexType type;
};


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 );

bool hasNext() const;
%Docstring
Find out whether there are more vertices
:rtype: bool
%End

QgsPointV2 next();
%Docstring
Return next vertex of the geometry (undefined behavior if hasNext() returns false before calling next())
:rtype: QgsPointV2
%End

QgsVertexIterator *__iter__();
%Docstring
:rtype: QgsVertexIterator
%End
%MethodCode
sipRes = sipCpp;
%End

SIP_PYOBJECT __next__();
%MethodCode
if ( sipCpp->hasNext() )
sipRes = sipConvertFromType( new QgsPointV2( sipCpp->next() ), sipType_QgsPointV2, 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
Expand Up @@ -158,6 +158,9 @@ class QgsCurve: QgsAbstractGeometry

protected:

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

virtual void clearCache() const;

};
Expand Down
4 changes: 4 additions & 0 deletions python/core/geometry/qgscurvepolygon.sip
Expand Up @@ -162,6 +162,10 @@ Adds an interior ring to the geometry (takes ownership)
virtual bool dropZValue();
virtual bool dropMValue();

protected:
virtual int childCount() const;
virtual QgsAbstractGeometry *childGeometry( int index ) const;

protected:


Expand Down
7 changes: 7 additions & 0 deletions python/core/geometry/qgsgeometry.sip
Expand Up @@ -224,6 +224,13 @@ Returns true if WKB of the geometry is of WKBMulti* type
:rtype: float
%End


QgsVertexIterator vertices() const;
%Docstring
.. versionadded:: 3.0
:rtype: QgsVertexIterator
%End

QgsPoint closestVertex( const QgsPoint &point, int &atVertex /Out/, int &beforeVertex /Out/, int &afterVertex /Out/, double &sqrDist /Out/ ) const;
%Docstring
:rtype: QgsPoint
Expand Down
4 changes: 4 additions & 0 deletions python/core/geometry/qgsgeometrycollection.sip
Expand Up @@ -141,6 +141,10 @@ Adds a geometry and takes ownership. Returns true in case of success.
virtual bool dropZValue();
virtual bool dropMValue();

protected:
virtual int childCount() const;
virtual QgsAbstractGeometry *childGeometry( int index ) const;

protected:

virtual bool wktOmitChildType() const;
Expand Down
4 changes: 4 additions & 0 deletions python/core/geometry/qgspointv2.sip
Expand Up @@ -364,6 +364,10 @@ class QgsPointV2: QgsAbstractGeometry
virtual bool convertTo( QgsWkbTypes::Type type );


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

};

/************************************************************************
Expand Down
124 changes: 124 additions & 0 deletions src/core/geometry/qgsabstractgeometry.cpp
Expand Up @@ -249,6 +249,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;
}

QgsPointV2 QgsAbstractGeometry::childPoint( int index ) const
{
Q_UNUSED( index );
return QgsPointV2();
}

bool QgsAbstractGeometry::isEmpty() const
{
QgsVertexId vId;
Expand All @@ -274,3 +290,111 @@ QgsAbstractGeometry *QgsAbstractGeometry::toCurveType() const
return 0;
}

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;
}

QgsPointV2 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 ) )
{
QgsPointV2 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 );
}
}

QgsPointV2 QgsVertexIterator::next()
{
n = i++;
return *n;
}