1
0
mirror of https://github.com/qgis/QGIS.git synced 2025-04-27 00:03:38 -04:00

[API] Throw IndexError on some QgsCurvePolygon methods when invalid

interior ring index is requested
This commit is contained in:
Nyall Dawson 2018-12-10 12:02:02 +10:00
parent a49bf9f68f
commit 44fbb89450
5 changed files with 171 additions and 10 deletions

@ -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.
const QgsCurve *interiorRing( int i ) const;
.. 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.
.. 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,6 +149,8 @@ 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
@ -114,6 +158,30 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
* \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 @@ class TestQgsGeometry(unittest.TestCase):
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.