Navigation Menu

Skip to content

Commit

Permalink
[FEATURE][API] Add parts iterators to QgsGeometry
Browse files Browse the repository at this point in the history
This allows easy iteration over all the parts of a geometry,
regardless of the geometry's type. E.g.

geometry = QgsGeometry.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
for part in geometry.parts():
  print(part.asWkt())

geometry = QgsGeometry.fromWkt( 'LineString( 0 0, 10 10 )' )
for part in geometry.parts():
  print(part.asWkt())

There are two iterators available. QgsGeometry.parts() gives
a non-const iterator, allowing the parts to be modified in place:

geometry = QgsGeometry.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
for part in geometry.parts():
   part.transform(ct)

For a const iteration, calling .const_parts() gives a const
iterator, which cannot edit the parts but avoids a potentially expensive
QgsGeometry detach and clone

geometry = QgsGeometry.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
for part in geometry.const_parts():
   print(part.x())

(cherry picked from commit a22422c)
  • Loading branch information
nyalldawson committed Nov 29, 2018
1 parent 282a2f8 commit 5484bb3
Show file tree
Hide file tree
Showing 10 changed files with 989 additions and 8 deletions.
152 changes: 151 additions & 1 deletion python/core/auto_generated/geometry/qgsabstractgeometry.sip.in
Expand Up @@ -610,9 +610,64 @@ Converts the geometry to a specified type.
%End


QgsGeometryPartIterator parts();
%Docstring
Returns Java-style iterator for traversal of parts of the geometry. This iterator
can safely be used to modify parts of the geometry.

* Example:
.. code-block:: python

# print the WKT representation of each part in a multi-point geometry
geometry = QgsMultiPoint.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
for part in geometry.parts():
print(part.asWkt())

# single part geometries only have one part - this loop will iterate once only
geometry = QgsLineString.fromWkt( 'LineString( 0 0, 10 10 )' )
for part in geometry.parts():
print(part.asWkt())

# parts can be modified during the iteration
geometry = QgsMultiPoint.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
for part in geometry.parts():
part.transform(ct)

# part iteration can also be combined with vertex iteration
geometry = QgsMultiPolygon.fromWkt( 'MultiPolygon((( 0 0, 0 10, 10 10, 10 0, 0 0 ),( 5 5, 5 6, 6 6, 6 5, 5 5)),((20 2, 22 2, 22 4, 20 4, 20 2)))' )
for part in geometry.parts():
for v in part.vertices():
print(v.x(), v.y())

.. seealso:: :py:func:`vertices`

.. versionadded:: 3.6
%End


QgsVertexIterator vertices() const;
%Docstring
Returns Java-style iterator for traversal of vertices of the geometry
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry, including all geometry parts and rings.

.. warning::

The iterator returns a copy of individual vertices, and accordingly geometries cannot be
modified using the iterator. See transformVertices() for a safe method to modify vertices "in-place".

* Example:
.. code-block:: python

# print the x and y coordinate for each vertex in a LineString
geometry = QgsLineString.fromWkt( 'LineString( 0 0, 1 1, 2 2)' )
for v in geometry.vertices():
print(v.x(), v.y())

# vertex iteration includes all parts and rings
geometry = QgsMultiPolygon.fromWkt( 'MultiPolygon((( 0 0, 0 10, 10 10, 10 0, 0 0 ),( 5 5, 5 6, 6 6, 6 5, 5 5)),((20 2, 22 2, 22 4, 20 4, 20 2)))' )
for v in geometry.vertices():
print(v.x(), v.y())

.. seealso:: :py:func:`parts`

.. versionadded:: 3.0
%End
Expand Down Expand Up @@ -771,6 +826,101 @@ Returns next vertex of the geometry (undefined behavior if hasNext() returns fal

};

class QgsGeometryPartIterator
{
%Docstring
Java-style iterator for traversal of parts of a geometry

.. versionadded:: 3.6
%End

%TypeHeaderCode
#include "qgsabstractgeometry.h"
%End
public:
QgsGeometryPartIterator();
%Docstring
Constructor for QgsGeometryPartIterator
%End

QgsGeometryPartIterator( QgsAbstractGeometry *geometry );
%Docstring
Constructs iterator for the given geometry
%End

bool hasNext() const;
%Docstring
Find out whether there are more parts
%End

QgsAbstractGeometry *next();
%Docstring
Returns next part of the geometry (undefined behavior if hasNext() returns false before calling next())
%End

QgsGeometryPartIterator *__iter__();
%MethodCode
sipRes = sipCpp;
%End

SIP_PYOBJECT __next__();
%MethodCode
if ( sipCpp->hasNext() )
sipRes = sipConvertFromType( sipCpp->next(), sipType_QgsAbstractGeometry, NULL );
else
PyErr_SetString( PyExc_StopIteration, "" );
%End

};


class QgsGeometryConstPartIterator
{
%Docstring
Java-style iterator for const traversal of parts of a geometry

.. versionadded:: 3.6
%End

%TypeHeaderCode
#include "qgsabstractgeometry.h"
%End
public:
QgsGeometryConstPartIterator();
%Docstring
Constructor for QgsGeometryConstPartIterator
%End

QgsGeometryConstPartIterator( const QgsAbstractGeometry *geometry );
%Docstring
Constructs iterator for the given geometry
%End

bool hasNext() const;
%Docstring
Find out whether there are more parts
%End

const QgsAbstractGeometry *next();
%Docstring
Returns next part of the geometry (undefined behavior if hasNext() returns false before calling next())
%End

QgsGeometryConstPartIterator *__iter__();
%MethodCode
sipRes = sipCpp;
%End

SIP_PYOBJECT __next__();
%MethodCode
if ( sipCpp->hasNext() )
sipRes = sipConvertFromType( const_cast< QgsAbstractGeometry * >( sipCpp->next() ), sipType_QgsAbstractGeometry, NULL );
else
PyErr_SetString( PyExc_StopIteration, "" );
%End

};

/************************************************************************
* This file has been generated automatically from *
* *
Expand Down
96 changes: 95 additions & 1 deletion python/core/auto_generated/geometry/qgsgeometry.sip.in
Expand Up @@ -353,11 +353,105 @@ Will return a negative value if a geometry is missing.

QgsVertexIterator vertices() const;
%Docstring
Returns Java-style iterator for traversal of vertices of the geometry
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry, including all geometry parts and rings.

.. warning::

The iterator returns a copy of individual vertices, and accordingly geometries cannot be
modified using the iterator. See transformVertices() for a safe method to modify vertices "in-place".

* Example:
.. code-block:: python

# print the x and y coordinate for each vertex in a LineString
geometry = QgsGeometry.fromWkt( 'LineString( 0 0, 1 1, 2 2)' )
for v in geometry.vertices():
print(v.x(), v.y())

# vertex iteration includes all parts and rings
geometry = QgsGeometry.fromWkt( 'MultiPolygon((( 0 0, 0 10, 10 10, 10 0, 0 0 ),( 5 5, 5 6, 6 6, 6 5, 5 5)),((20 2, 22 2, 22 4, 20 4, 20 2)))' )
for v in geometry.vertices():
print(v.x(), v.y())

.. seealso:: :py:func:`parts`

.. versionadded:: 3.0
%End


QgsGeometryPartIterator parts();
%Docstring
Returns Java-style iterator for traversal of parts of the geometry. This iterator
can safely be used to modify parts of the geometry.

This method forces a detach. Use constParts() to avoid the detach
if the parts are not going to be modified.

* Example:
.. code-block:: python

# print the WKT representation of each part in a multi-point geometry
geometry = QgsGeometry.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
for part in geometry.parts():
print(part.asWkt())

# single part geometries only have one part - this loop will iterate once only
geometry = QgsGeometry.fromWkt( 'LineString( 0 0, 10 10 )' )
for part in geometry.parts():
print(part.asWkt())

# parts can be modified during the iteration
geometry = QgsGeometry.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
for part in geometry.parts():
part.transform(ct)

# part iteration can also be combined with vertex iteration
geometry = QgsGeometry.fromWkt( 'MultiPolygon((( 0 0, 0 10, 10 10, 10 0, 0 0 ),( 5 5, 5 6, 6 6, 6 5, 5 5)),((20 2, 22 2, 22 4, 20 4, 20 2)))' )
for part in geometry.parts():
for v in part.vertices():
print(v.x(), v.y())

.. seealso:: :py:func:`constParts`

.. seealso:: :py:func:`vertices`

.. versionadded:: 3.6
%End

QgsGeometryConstPartIterator constParts() const;
%Docstring
Returns Java-style iterator for traversal of parts of the geometry. This iterator
returns read-only references to parts and cannot be used to modify the parts.

Unlike parts(), this method does not force a detach and is more efficient if read-only
iteration only is required.

* Example:
.. code-block:: python

# print the WKT representation of each part in a multi-point geometry
geometry = QgsGeometry.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
for part in geometry.parts():
print(part.asWkt())

# single part geometries only have one part - this loop will iterate once only
geometry = QgsGeometry.fromWkt( 'LineString( 0 0, 10 10 )' )
for part in geometry.parts():
print(part.asWkt())

# part iteration can also be combined with vertex iteration
geometry = QgsGeometry.fromWkt( 'MultiPolygon((( 0 0, 0 10, 10 10, 10 0, 0 0 ),( 5 5, 5 6, 6 6, 6 5, 5 5)),((20 2, 22 2, 22 4, 20 4, 20 2)))' )
for part in geometry.parts():
for v in part.vertices():
print(v.x(), v.y())

.. seealso:: :py:func:`parts`

.. seealso:: :py:func:`vertices`

.. versionadded:: 3.6
%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
Expand Up @@ -58,7 +58,7 @@ Returns the number of geometries within the collection.



SIP_PYOBJECT geometryN( int n ) const;
SIP_PYOBJECT geometryN( int n );
%Docstring
Returns a geometry from within the collection.

Expand Down

0 comments on commit 5484bb3

Please sign in to comment.