Skip to content
Permalink
Browse files

[API] Throw IndexError on some QgsCurvePolygon methods when invalid

interior ring index is requested
  • Loading branch information
nyalldawson committed Dec 10, 2018
1 parent a49bf9f commit 44fbb894506965ec6ebbc6f232399214ff19574c
@@ -70,11 +70,43 @@ Curve polygon geometry type
virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );



int numInteriorRings() const;
%Docstring
Returns the number of interior rings contained with the curve polygon.

.. seealso:: :py:func:`interiorRing`
%End

const QgsCurve *exteriorRing() const;
%Docstring
Returns the curve polygon's exterior ring.

.. seealso:: :py:func:`interiorRing`
%End


SIP_PYOBJECT interiorRing( int i ) /TypeHint="QgsCurve"/;
%Docstring
Retrieves an interior ring from the curve polygon. The first interior ring has index 0.

An IndexError will be raised if no interior ring with the specified index exists.

const QgsCurve *interiorRing( int i ) const;
.. seealso:: :py:func:`numInteriorRings`

.. seealso:: :py:func:`exteriorRing`
%End
%MethodCode
if ( a0 < 0 || a0 >= sipCpp->numInteriorRings() )
{
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
sipIsErr = 1;
}
else
{
return sipConvertFromType( const_cast< QgsCurve * >( sipCpp->interiorRing( a0 ) ), sipType_QgsCurve, NULL );
}
%End

virtual QgsPolygon *toPolygon( double tolerance = M_PI_2 / 90, SegmentationToleranceType toleranceType = MaximumAngle ) const /Factory/;
%Docstring
@@ -107,13 +139,27 @@ Sets all interior rings (takes ownership)
Adds an interior ring to the geometry (takes ownership)
%End

bool removeInteriorRing( int ringIndex );

bool removeInteriorRing( int i );
%Docstring
Removes an interior ring from the polygon. The first interior ring has index 0.
The corresponding ring is removed from the polygon and deleted. If a ring was successfully removed
the function will return true. It is not possible to remove the exterior ring using this method.
The corresponding ring is removed from the polygon and deleted.
It is not possible to remove the exterior ring using this method.

An IndexError will be raised if no interior ring with the specified index exists.

.. seealso:: :py:func:`removeInteriorRings`
%End
%MethodCode
if ( a0 < 0 || a0 >= sipCpp->numInteriorRings() )
{
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
sipIsErr = 1;
}
else
{
return PyBool_FromLong( sipCpp->removeInteriorRing( a0 ) );
}
%End

void removeInteriorRings( double minimumAllowedArea = -1 );
@@ -136,7 +182,6 @@ For example, this removes unclosed rings and rings with less than 4 vertices.
.. versionadded:: 3.0
%End


void forceRHR();
%Docstring
Forces the geometry to respect the Right-Hand-Rule, in which the area that is
@@ -58,11 +58,12 @@ Returns the number of geometries within the collection.




SIP_PYOBJECT geometryN( int n ) /TypeHint="QgsAbstractGeometry"/;
%Docstring
Returns a geometry from within the collection.

:param n: index of geometry to return
:param n: index of geometry to return. An IndexError will be raised if no geometry with the specified index exists.
%End
%MethodCode
if ( a0 < 0 || a0 >= sipCpp->numGeometries() )
@@ -127,7 +128,7 @@ An IndexError will be raised if no geometry with the specified index exists.
}
else
{
sipCpp->removeGeometry( a0 );
return PyBool_FromLong( sipCpp->removeGeometry( a0 ) );
}
%End

@@ -66,16 +66,35 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
bool removeDuplicateNodes( double epsilon = 4 * std::numeric_limits<double>::epsilon(), bool useZValues = false ) override;

//curve polygon interface

/**
* Returns the number of interior rings contained with the curve polygon.
*
* \see interiorRing()
*/
int numInteriorRings() const
{
return mInteriorRings.size();
}

/**
* Returns the curve polygon's exterior ring.
*
* \see interiorRing()
*/
const QgsCurve *exteriorRing() const
{
return mExteriorRing.get();
}

#ifndef SIP_RUN

/**
* Retrieves an interior ring from the curve polygon. The first interior ring has index 0.
*
* \see numInteriorRings()
* \see exteriorRing()
*/
const QgsCurve *interiorRing( int i ) const
{
if ( i < 0 || i >= mInteriorRings.size() )
@@ -84,6 +103,29 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
}
return mInteriorRings.at( i );
}
#else

/**
* Retrieves an interior ring from the curve polygon. The first interior ring has index 0.
*
* An IndexError will be raised if no interior ring with the specified index exists.
*
* \see numInteriorRings()
* \see exteriorRing()
*/
SIP_PYOBJECT interiorRing( int i ) SIP_TYPEHINT( QgsCurve );
% MethodCode
if ( a0 < 0 || a0 >= sipCpp->numInteriorRings() )
{
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
sipIsErr = 1;
}
else
{
return sipConvertFromType( const_cast< QgsCurve * >( sipCpp->interiorRing( a0 ) ), sipType_QgsCurve, NULL );
}
% End
#endif

/**
* Returns a new polygon geometry corresponding to a segmentized approximation
@@ -107,13 +149,39 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
//! Adds an interior ring to the geometry (takes ownership)
virtual void addInteriorRing( QgsCurve *ring SIP_TRANSFER );

#ifndef SIP_RUN

/**
* Removes an interior ring from the polygon. The first interior ring has index 0.
* The corresponding ring is removed from the polygon and deleted. If a ring was successfully removed
* the function will return true. It is not possible to remove the exterior ring using this method.
* \see removeInteriorRings()
*/
bool removeInteriorRing( int ringIndex );
#else

/**
* Removes an interior ring from the polygon. The first interior ring has index 0.
* The corresponding ring is removed from the polygon and deleted.
* It is not possible to remove the exterior ring using this method.
*
* An IndexError will be raised if no interior ring with the specified index exists.
*
* \see removeInteriorRings()
*/
bool removeInteriorRing( int i );
% MethodCode
if ( a0 < 0 || a0 >= sipCpp->numInteriorRings() )
{
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
sipIsErr = 1;
}
else
{
return PyBool_FromLong( sipCpp->removeInteriorRing( a0 ) );
}
% End
#endif

/**
* Removes the interior rings from the polygon. If the minimumAllowedArea
@@ -133,7 +201,6 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
*/
void removeInvalidRings();


/**
* Forces the geometry to respect the Right-Hand-Rule, in which the area that is
* bounded by the polygon is to the right of the boundary. In particular, the exterior
@@ -81,13 +81,19 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
return mGeometries.value( n );
}

#ifndef SIP_RUN

/**
* Returns a geometry from within the collection.
* \param n index of geometry to return
*/
#ifndef SIP_RUN
QgsAbstractGeometry *geometryN( int n );
#else

/**
* Returns a geometry from within the collection.
* \param n index of geometry to return. An IndexError will be raised if no geometry with the specified index exists.
*/
SIP_PYOBJECT geometryN( int n ) SIP_TYPEHINT( QgsAbstractGeometry );
% MethodCode
if ( a0 < 0 || a0 >= sipCpp->numGeometries() )
@@ -151,7 +157,7 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
}
else
{
sipCpp->removeGeometry( a0 );
return PyBool_FromLong( sipCpp->removeGeometry( a0 ) );
}
% End
#endif
@@ -504,6 +504,48 @@ def testGeometryCollectionPythonAdditions(self):
g.fromWkt('GeometryCollection( Point(1 2), Point(11 12), LineString(33 34, 44 45))')
self.assertEqual([p.asWkt() for p in g], ['Point (1 2)', 'Point (11 12)', 'LineString (33 34, 44 45)'])

def testCurvePolygonPythonAdditions(self):
"""
Tests Python specific additions to the QgsCurvePolygon API
"""
# interiorRing
g = QgsPolygon()
with self.assertRaises(IndexError):
g.interiorRing(-1)
with self.assertRaises(IndexError):
g.interiorRing(0)

g.fromWkt('Polygon((0 0, 1 0, 1 1, 0 0),(0.1 0.1, 0.2 0.1, 0.2 0.2, 0.1 0.1),(0.8 0.8, 0.9 0.8, 0.9 0.9, 0.8 0.8))')
with self.assertRaises(IndexError):
g.interiorRing(-1)
with self.assertRaises(IndexError):
g.interiorRing(2)
self.assertEqual(g.interiorRing(0).asWkt(1), 'LineString (0.1 0.1, 0.2 0.1, 0.2 0.2, 0.1 0.1)')
self.assertEqual(g.interiorRing(1).asWkt(1), 'LineString (0.8 0.8, 0.9 0.8, 0.9 0.9, 0.8 0.8)')

# removeInteriorRing
g = QgsPolygon()
with self.assertRaises(IndexError):
g.removeInteriorRing(-1)
with self.assertRaises(IndexError):
g.removeInteriorRing(0)

g.fromWkt(
'Polygon((0 0, 1 0, 1 1, 0 0),(0.1 0.1, 0.2 0.1, 0.2 0.2, 0.1 0.1),(0.8 0.8, 0.9 0.8, 0.9 0.9, 0.8 0.8))')
with self.assertRaises(IndexError):
g.removeInteriorRing(-1)
with self.assertRaises(IndexError):
g.removeInteriorRing(2)

g.removeInteriorRing(1)
self.assertEqual(g.asWkt(1), 'Polygon ((0 0, 1 0, 1 1, 0 0),(0.1 0.1, 0.2 0.1, 0.2 0.2, 0.1 0.1))')
with self.assertRaises(IndexError):
g.removeInteriorRing(1)
g.removeInteriorRing(0)
self.assertEqual(g.asWkt(1), 'Polygon ((0 0, 1 0, 1 1, 0 0))')
with self.assertRaises(IndexError):
g.removeInteriorRing(0)

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 44fbb89

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