Skip to content

Commit 44fbb89

Browse files
committedDec 10, 2018
[API] Throw IndexError on some QgsCurvePolygon methods when invalid
interior ring index is requested
1 parent a49bf9f commit 44fbb89

File tree

5 files changed

+171
-10
lines changed

5 files changed

+171
-10
lines changed
 

‎python/core/auto_generated/geometry/qgscurvepolygon.sip.in

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,43 @@ Curve polygon geometry type
7070
virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );
7171

7272

73+
7374
int numInteriorRings() const;
75+
%Docstring
76+
Returns the number of interior rings contained with the curve polygon.
77+
78+
.. seealso:: :py:func:`interiorRing`
79+
%End
7480

7581
const QgsCurve *exteriorRing() const;
82+
%Docstring
83+
Returns the curve polygon's exterior ring.
84+
85+
.. seealso:: :py:func:`interiorRing`
86+
%End
87+
88+
89+
SIP_PYOBJECT interiorRing( int i ) /TypeHint="QgsCurve"/;
90+
%Docstring
91+
Retrieves an interior ring from the curve polygon. The first interior ring has index 0.
92+
93+
An IndexError will be raised if no interior ring with the specified index exists.
7694

77-
const QgsCurve *interiorRing( int i ) const;
95+
.. seealso:: :py:func:`numInteriorRings`
96+
97+
.. seealso:: :py:func:`exteriorRing`
98+
%End
99+
%MethodCode
100+
if ( a0 < 0 || a0 >= sipCpp->numInteriorRings() )
101+
{
102+
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
103+
sipIsErr = 1;
104+
}
105+
else
106+
{
107+
return sipConvertFromType( const_cast< QgsCurve * >( sipCpp->interiorRing( a0 ) ), sipType_QgsCurve, NULL );
108+
}
109+
%End
78110

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

110-
bool removeInteriorRing( int ringIndex );
142+
143+
bool removeInteriorRing( int i );
111144
%Docstring
112145
Removes an interior ring from the polygon. The first interior ring has index 0.
113-
The corresponding ring is removed from the polygon and deleted. If a ring was successfully removed
114-
the function will return true. It is not possible to remove the exterior ring using this method.
146+
The corresponding ring is removed from the polygon and deleted.
147+
It is not possible to remove the exterior ring using this method.
148+
149+
An IndexError will be raised if no interior ring with the specified index exists.
115150

116151
.. seealso:: :py:func:`removeInteriorRings`
152+
%End
153+
%MethodCode
154+
if ( a0 < 0 || a0 >= sipCpp->numInteriorRings() )
155+
{
156+
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
157+
sipIsErr = 1;
158+
}
159+
else
160+
{
161+
return PyBool_FromLong( sipCpp->removeInteriorRing( a0 ) );
162+
}
117163
%End
118164

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

139-
140185
void forceRHR();
141186
%Docstring
142187
Forces the geometry to respect the Right-Hand-Rule, in which the area that is

‎python/core/auto_generated/geometry/qgsgeometrycollection.sip.in

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,12 @@ Returns the number of geometries within the collection.
5858

5959

6060

61+
6162
SIP_PYOBJECT geometryN( int n ) /TypeHint="QgsAbstractGeometry"/;
6263
%Docstring
6364
Returns a geometry from within the collection.
6465

65-
:param n: index of geometry to return
66+
:param n: index of geometry to return. An IndexError will be raised if no geometry with the specified index exists.
6667
%End
6768
%MethodCode
6869
if ( a0 < 0 || a0 >= sipCpp->numGeometries() )
@@ -127,7 +128,7 @@ An IndexError will be raised if no geometry with the specified index exists.
127128
}
128129
else
129130
{
130-
sipCpp->removeGeometry( a0 );
131+
return PyBool_FromLong( sipCpp->removeGeometry( a0 ) );
131132
}
132133
%End
133134

‎src/core/geometry/qgscurvepolygon.h

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,35 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
6666
bool removeDuplicateNodes( double epsilon = 4 * std::numeric_limits<double>::epsilon(), bool useZValues = false ) override;
6767

6868
//curve polygon interface
69+
70+
/**
71+
* Returns the number of interior rings contained with the curve polygon.
72+
*
73+
* \see interiorRing()
74+
*/
6975
int numInteriorRings() const
7076
{
7177
return mInteriorRings.size();
7278
}
7379

80+
/**
81+
* Returns the curve polygon's exterior ring.
82+
*
83+
* \see interiorRing()
84+
*/
7485
const QgsCurve *exteriorRing() const
7586
{
7687
return mExteriorRing.get();
7788
}
7889

90+
#ifndef SIP_RUN
91+
92+
/**
93+
* Retrieves an interior ring from the curve polygon. The first interior ring has index 0.
94+
*
95+
* \see numInteriorRings()
96+
* \see exteriorRing()
97+
*/
7998
const QgsCurve *interiorRing( int i ) const
8099
{
81100
if ( i < 0 || i >= mInteriorRings.size() )
@@ -84,6 +103,29 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
84103
}
85104
return mInteriorRings.at( i );
86105
}
106+
#else
107+
108+
/**
109+
* Retrieves an interior ring from the curve polygon. The first interior ring has index 0.
110+
*
111+
* An IndexError will be raised if no interior ring with the specified index exists.
112+
*
113+
* \see numInteriorRings()
114+
* \see exteriorRing()
115+
*/
116+
SIP_PYOBJECT interiorRing( int i ) SIP_TYPEHINT( QgsCurve );
117+
% MethodCode
118+
if ( a0 < 0 || a0 >= sipCpp->numInteriorRings() )
119+
{
120+
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
121+
sipIsErr = 1;
122+
}
123+
else
124+
{
125+
return sipConvertFromType( const_cast< QgsCurve * >( sipCpp->interiorRing( a0 ) ), sipType_QgsCurve, NULL );
126+
}
127+
% End
128+
#endif
87129

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

152+
#ifndef SIP_RUN
153+
110154
/**
111155
* Removes an interior ring from the polygon. The first interior ring has index 0.
112156
* The corresponding ring is removed from the polygon and deleted. If a ring was successfully removed
113157
* the function will return true. It is not possible to remove the exterior ring using this method.
114158
* \see removeInteriorRings()
115159
*/
116160
bool removeInteriorRing( int ringIndex );
161+
#else
162+
163+
/**
164+
* Removes an interior ring from the polygon. The first interior ring has index 0.
165+
* The corresponding ring is removed from the polygon and deleted.
166+
* It is not possible to remove the exterior ring using this method.
167+
*
168+
* An IndexError will be raised if no interior ring with the specified index exists.
169+
*
170+
* \see removeInteriorRings()
171+
*/
172+
bool removeInteriorRing( int i );
173+
% MethodCode
174+
if ( a0 < 0 || a0 >= sipCpp->numInteriorRings() )
175+
{
176+
PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) );
177+
sipIsErr = 1;
178+
}
179+
else
180+
{
181+
return PyBool_FromLong( sipCpp->removeInteriorRing( a0 ) );
182+
}
183+
% End
184+
#endif
117185

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

136-
137204
/**
138205
* Forces the geometry to respect the Right-Hand-Rule, in which the area that is
139206
* bounded by the polygon is to the right of the boundary. In particular, the exterior

‎src/core/geometry/qgsgeometrycollection.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,19 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
8181
return mGeometries.value( n );
8282
}
8383

84+
#ifndef SIP_RUN
85+
8486
/**
8587
* Returns a geometry from within the collection.
8688
* \param n index of geometry to return
8789
*/
88-
#ifndef SIP_RUN
8990
QgsAbstractGeometry *geometryN( int n );
9091
#else
92+
93+
/**
94+
* Returns a geometry from within the collection.
95+
* \param n index of geometry to return. An IndexError will be raised if no geometry with the specified index exists.
96+
*/
9197
SIP_PYOBJECT geometryN( int n ) SIP_TYPEHINT( QgsAbstractGeometry );
9298
% MethodCode
9399
if ( a0 < 0 || a0 >= sipCpp->numGeometries() )
@@ -151,7 +157,7 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
151157
}
152158
else
153159
{
154-
sipCpp->removeGeometry( a0 );
160+
return PyBool_FromLong( sipCpp->removeGeometry( a0 ) );
155161
}
156162
% End
157163
#endif

‎tests/src/python/test_qgsgeometry.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,48 @@ def testGeometryCollectionPythonAdditions(self):
504504
g.fromWkt('GeometryCollection( Point(1 2), Point(11 12), LineString(33 34, 44 45))')
505505
self.assertEqual([p.asWkt() for p in g], ['Point (1 2)', 'Point (11 12)', 'LineString (33 34, 44 45)'])
506506

507+
def testCurvePolygonPythonAdditions(self):
508+
"""
509+
Tests Python specific additions to the QgsCurvePolygon API
510+
"""
511+
# interiorRing
512+
g = QgsPolygon()
513+
with self.assertRaises(IndexError):
514+
g.interiorRing(-1)
515+
with self.assertRaises(IndexError):
516+
g.interiorRing(0)
517+
518+
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))')
519+
with self.assertRaises(IndexError):
520+
g.interiorRing(-1)
521+
with self.assertRaises(IndexError):
522+
g.interiorRing(2)
523+
self.assertEqual(g.interiorRing(0).asWkt(1), 'LineString (0.1 0.1, 0.2 0.1, 0.2 0.2, 0.1 0.1)')
524+
self.assertEqual(g.interiorRing(1).asWkt(1), 'LineString (0.8 0.8, 0.9 0.8, 0.9 0.9, 0.8 0.8)')
525+
526+
# removeInteriorRing
527+
g = QgsPolygon()
528+
with self.assertRaises(IndexError):
529+
g.removeInteriorRing(-1)
530+
with self.assertRaises(IndexError):
531+
g.removeInteriorRing(0)
532+
533+
g.fromWkt(
534+
'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))')
535+
with self.assertRaises(IndexError):
536+
g.removeInteriorRing(-1)
537+
with self.assertRaises(IndexError):
538+
g.removeInteriorRing(2)
539+
540+
g.removeInteriorRing(1)
541+
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))')
542+
with self.assertRaises(IndexError):
543+
g.removeInteriorRing(1)
544+
g.removeInteriorRing(0)
545+
self.assertEqual(g.asWkt(1), 'Polygon ((0 0, 1 0, 1 1, 0 0))')
546+
with self.assertRaises(IndexError):
547+
g.removeInteriorRing(0)
548+
507549
def testReferenceGeometry(self):
508550
""" Test parsing a whole range of valid reference wkt formats and variants, and checking
509551
expected values such as length, area, centroids, bounding boxes, etc of the resultant geometry.

0 commit comments

Comments
 (0)
Please sign in to comment.