Skip to content
Permalink
Browse files

[FEATURE][API] Add some nice PyQGIS API for working with geometry col…

…lections

- Calling removeGeometry with an invalid index will now raise an IndexError
- Calling collection[0] will return the first geometry in the collection,
collection[1] the second, etc. And negative indices return from the end
of the collection, so collection[-1] returns the last geometry in the collection.
- Geometries can be deleted by calling `del collection[1]` (deletes the
second geometry from the collection). Also supports negative indices
to count from the end of the collection.
  • Loading branch information
nyalldawson committed Dec 6, 2018
1 parent 31b82de commit 4bba8ae64dc0eaf6962d9d932c43b99700fad464
@@ -109,13 +109,26 @@ Inserts a geometry before a specified index and takes ownership. Returns true in
:param index: position to insert geometry before
%End


virtual bool removeGeometry( int nr );
%Docstring
Removes a geometry from the collection.
Removes a geometry from the collection by index.

:param nr: index of geometry to remove
An IndexError will be raised if no geometry with the specified index exists.

:return: true if removal was successful.
%End
%MethodCode
const int count = sipCpp->numGeometries();
if ( a0 < 0 || a0 >= count )
{
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
sipIsErr = 1;
}
else
{
sipCpp->removeGeometry( a0 );
}
%End

virtual void transform( const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d = QgsCoordinateTransform::ForwardTransform, bool transformZ = false ) throw( QgsCsException );
@@ -207,6 +220,56 @@ Returns a geometry without curves. Caller takes ownership





SIP_PYOBJECT __getitem__( int index );
%Docstring
Returns the geometry at the specified ``index``. An IndexError will be raised if no geometry with the specified ``index`` exists.

Indexes can be less than 0, in which case they correspond to geometries from the end of the collect. E.g. an index of -1
corresponds to the last geometry in the collection.

.. versionadded:: 3.6
%End
%MethodCode
const int count = sipCpp->numGeometries();
if ( a0 < -count || a0 >= count )
{
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
sipIsErr = 1;
}
else if ( a0 >= 0 )
{
return sipConvertFromType( sipCpp->geometryN( a0 ), sipType_QgsAbstractGeometry, NULL );
}
else
{
return sipConvertFromType( sipCpp->geometryN( count + a0 ), sipType_QgsAbstractGeometry, NULL );
}
%End

void __delitem__( int index );
%Docstring
Deletes the geometry at the specified ``index``. A geometry at the ``index`` must already exist or an IndexError will be raised.

Indexes can be less than 0, in which case they correspond to geometries from the end of the collection. E.g. an index of -1
corresponds to the last geometry in the collection.

.. versionadded:: 3.6
%End
%MethodCode
const int count = sipCpp->numGeometries();
if ( a0 >= 0 && a0 < count )
sipCpp->removeGeometry( a0 );
else if ( a0 < 0 && a0 >= -count )
sipCpp->removeGeometry( count + a0 );
else
{
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
sipIsErr = 1;
}
%End

virtual QgsGeometryCollection *createEmptyWithSameType() const /Factory/;


@@ -124,12 +124,37 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
*/
virtual bool insertGeometry( QgsAbstractGeometry *g SIP_TRANSFER, int index );

#ifndef SIP_RUN

/**
* Removes a geometry from the collection.
* \param nr index of geometry to remove
* \returns true if removal was successful.
*/
virtual bool removeGeometry( int nr );
#else

/**
* Removes a geometry from the collection by index.
*
* An IndexError will be raised if no geometry with the specified index exists.
*
* \returns true if removal was successful.
*/
virtual bool removeGeometry( int nr );
% MethodCode
const int count = sipCpp->numGeometries();
if ( a0 < 0 || a0 >= count )
{
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
sipIsErr = 1;
}
else
{
sipCpp->removeGeometry( a0 );
}
% End
#endif

void transform( const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d = QgsCoordinateTransform::ForwardTransform, bool transformZ = false ) override SIP_THROW( QgsCsException );
void transform( const QTransform &t, double zTranslate = 0.0, double zScale = 1.0, double mTranslate = 0.0, double mScale = 1.0 ) override;
@@ -202,6 +227,58 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
}
#endif


#ifdef SIP_RUN

/**
* Returns the geometry at the specified ``index``. An IndexError will be raised if no geometry with the specified ``index`` exists.
*
* Indexes can be less than 0, in which case they correspond to geometries from the end of the collect. E.g. an index of -1
* corresponds to the last geometry in the collection.
*
* \since QGIS 3.6
*/
SIP_PYOBJECT __getitem__( int index );
% MethodCode
const int count = sipCpp->numGeometries();
if ( a0 < -count || a0 >= count )
{
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
sipIsErr = 1;
}
else if ( a0 >= 0 )
{
return sipConvertFromType( sipCpp->geometryN( a0 ), sipType_QgsAbstractGeometry, NULL );
}
else
{
return sipConvertFromType( sipCpp->geometryN( count + a0 ), sipType_QgsAbstractGeometry, NULL );
}
% End

/**
* Deletes the geometry at the specified ``index``. A geometry at the ``index`` must already exist or an IndexError will be raised.
*
* Indexes can be less than 0, in which case they correspond to geometries from the end of the collection. E.g. an index of -1
* corresponds to the last geometry in the collection.
*
* \since QGIS 3.6
*/
void __delitem__( int index );
% MethodCode
const int count = sipCpp->numGeometries();
if ( a0 >= 0 && a0 < count )
sipCpp->removeGeometry( a0 );
else if ( a0 < 0 && a0 >= -count )
sipCpp->removeGeometry( count + a0 );
else
{
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
sipIsErr = 1;
}
% End
#endif

QgsGeometryCollection *createEmptyWithSameType() const override SIP_FACTORY;

protected:
@@ -433,6 +433,71 @@ def testLineStringPythonAdditions(self):
with self.assertRaises(IndexError):
del ls[-3]

def testGeometryCollectionPythonAdditions(self):
"""
Tests Python specific additions to the QgsGeometryCollection API
"""
g = QgsGeometryCollection()
self.assertTrue(bool(g))
self.assertEqual(len(g), 0)
g = QgsGeometryCollection()
g.fromWkt('GeometryCollection( Point(1 2), Point(11 12))')
self.assertTrue(bool(g))
self.assertEqual(len(g), 2)

# pointN
with self.assertRaises(IndexError):
g.geometryN(-1)
with self.assertRaises(IndexError):
g.geometryN(2)
self.assertEqual(g.geometryN(0), QgsPoint(1, 2))
self.assertEqual(g.geometryN(1), QgsPoint(11, 12))

# removeGeometry
g.fromWkt('GeometryCollection( Point(1 2), Point(11 12), Point(33 34))')
with self.assertRaises(IndexError):
g.removeGeometry(-1)
with self.assertRaises(IndexError):
g.removeGeometry(3)
g.removeGeometry(1)
self.assertEqual(len(g), 2)
self.assertEqual(g.geometryN(0), QgsPoint(1, 2))
self.assertEqual(g.geometryN(1), QgsPoint(33, 34))
with self.assertRaises(IndexError):
g.removeGeometry(2)

g.fromWkt('GeometryCollection( Point(25 16 37 58), Point(26 22 47 68))')
# get item
with self.assertRaises(IndexError):
g[-3]
with self.assertRaises(IndexError):
g[2]
self.assertEqual(g[0], QgsPoint(25, 16, 37, 58))
self.assertEqual(g[1], QgsPoint(26, 22, 47, 68))
self.assertEqual(g[-2], QgsPoint(25, 16, 37, 58))
self.assertEqual(g[-1], QgsPoint(26, 22, 47, 68))

# del item
g.fromWkt('GeometryCollection( Point(1 2), Point(11 12), Point(33 34))')
with self.assertRaises(IndexError):
del g[-4]
with self.assertRaises(IndexError):
del g[3]
del g[1]
self.assertEqual(len(g), 2)
self.assertEqual(g[0], QgsPoint(1, 2))
self.assertEqual(g[1], QgsPoint(33, 34))
with self.assertRaises(IndexError):
del g[2]

g.fromWkt('GeometryCollection( Point(1 2), Point(11 12), Point(33 34))')
del g[-3]
self.assertEqual(len(g), 2)
self.assertEqual(g[0], QgsPoint(11, 12))
self.assertEqual(g[1], QgsPoint(33, 34))
with self.assertRaises(IndexError):
del g[-3]

def testReferenceGeometry(self):
""" Test parsing a whole range of valid reference wkt formats and variants, and checking
expected values such as length, area, centroids, bounding boxes, etc of the resultant geometry.

0 comments on commit 4bba8ae

Please sign in to comment.
You can’t perform that action at this time.