Skip to content

Commit

Permalink
[api] Raises ValueError and TypeError exceptions when QgsGeometry.asP…
Browse files Browse the repository at this point in the history
…olygon()

is called on non-single-polygon geometries

Previously we would just return an empty list when geometries of invalid
type were used, but this is dangerous and we are safer to explicitly
raise errors preventing use of asPolygon() with incompatible geometry types.

(cherry picked from commit 514c5e2)
  • Loading branch information
nyalldawson committed Dec 20, 2018
1 parent 01f6ced commit 7ef9760
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 5 deletions.
31 changes: 28 additions & 3 deletions python/core/auto_generated/geometry/qgsgeometry.sip.in
Expand Up @@ -1473,10 +1473,35 @@ will be raised.
}
%End

QgsPolygonXY asPolygon() const;

SIP_PYOBJECT asPolygon() const /TypeHint="QgsPolygonXY"/;
%Docstring
Returns contents of the geometry as a polygon
if wkbType is WKBPolygon, otherwise an empty list
Returns the contents of the geometry as a polygon.

Any z or m values present in the geometry will be discarded. If the geometry is a curved polygon type
(such as a CurvePolygon), it will be automatically segmentized.

This method works only with single-polygon (or single-curve polygon) geometry types. If the geometry
is not a single-polygon type, a TypeError will be raised. If the geometry is null, a ValueError
will be raised.
%End
%MethodCode
const QgsWkbTypes::Type type = sipCpp->wkbType();
if ( sipCpp->isNull() )
{
PyErr_SetString( PyExc_ValueError, QStringLiteral( "Null geometry cannot be converted to a polygon." ).toUtf8().constData() );
sipIsErr = 1;
}
else if ( QgsWkbTypes::geometryType( type ) != QgsWkbTypes::PolygonGeometry || QgsWkbTypes::isMultiType( type ) )
{
PyErr_SetString( PyExc_TypeError, QStringLiteral( "%1 geometry cannot be converted to a polygon. Only single polygon or curve polygon types are permitted." ).arg( QgsWkbTypes::displayString( type ) ).toUtf8().constData() );
sipIsErr = 1;
}
else
{
const sipMappedType *qvector_type = sipFindMappedType( "QVector<QVector<QgsPointXY>>" );
sipRes = sipConvertFromNewType( new QgsPolygonXY( sipCpp->asPolygon() ), qvector_type, Py_None );
}
%End

QgsMultiPointXY asMultiPoint() const;
Expand Down
42 changes: 40 additions & 2 deletions src/core/geometry/qgsgeometry.h
Expand Up @@ -1498,11 +1498,49 @@ class CORE_EXPORT QgsGeometry
% End
#endif

#ifndef SIP_RUN

/**
* Returns contents of the geometry as a polygon
* if wkbType is WKBPolygon, otherwise an empty list
* Returns the contents of the geometry as a polygon.
*
* Any z or m values present in the geometry will be discarded. If the geometry is a curved polygon type
* (such as a CurvePolygon), it will be automatically segmentized.
*
* \warning If the geometry is not a single-polygon (or single-curve polygon) type, an empty list will be returned.
*/
QgsPolygonXY asPolygon() const;
#else

/**
* Returns the contents of the geometry as a polygon.
*
* Any z or m values present in the geometry will be discarded. If the geometry is a curved polygon type
* (such as a CurvePolygon), it will be automatically segmentized.
*
* This method works only with single-polygon (or single-curve polygon) geometry types. If the geometry
* is not a single-polygon type, a TypeError will be raised. If the geometry is null, a ValueError
* will be raised.
*/
SIP_PYOBJECT asPolygon() const SIP_TYPEHINT( QgsPolygonXY );
% MethodCode
const QgsWkbTypes::Type type = sipCpp->wkbType();
if ( sipCpp->isNull() )
{
PyErr_SetString( PyExc_ValueError, QStringLiteral( "Null geometry cannot be converted to a polygon." ).toUtf8().constData() );
sipIsErr = 1;
}
else if ( QgsWkbTypes::geometryType( type ) != QgsWkbTypes::PolygonGeometry || QgsWkbTypes::isMultiType( type ) )
{
PyErr_SetString( PyExc_TypeError, QStringLiteral( "%1 geometry cannot be converted to a polygon. Only single polygon or curve polygon types are permitted." ).arg( QgsWkbTypes::displayString( type ) ).toUtf8().constData() );
sipIsErr = 1;
}
else
{
const sipMappedType *qvector_type = sipFindMappedType( "QVector<QVector<QgsPointXY>>" );
sipRes = sipConvertFromNewType( new QgsPolygonXY( sipCpp->asPolygon() ), qvector_type, Py_None );
}
% End
#endif

/**
* Returns contents of the geometry as a multi point
Expand Down
22 changes: 22 additions & 0 deletions tests/src/python/test_qgsgeometry.py
Expand Up @@ -288,6 +288,28 @@ def testPointXY(self):
with self.assertRaises(ValueError):
QgsGeometry().asPolyline()

# as polygon
self.assertEqual(QgsGeometry.fromWkt('Polygon((11 13,14 15, 11 15, 11 13))').asPolygon(),
[[QgsPointXY(11, 13), QgsPointXY(14, 15), QgsPointXY(11, 15), QgsPointXY(11, 13)]])
self.assertEqual(QgsGeometry.fromWkt('PolygonZ((11 13 1,14 15 2, 11 15 3, 11 13 1))').asPolygon(),
[[QgsPointXY(11, 13), QgsPointXY(14, 15), QgsPointXY(11, 15), QgsPointXY(11, 13)]])
self.assertEqual(QgsGeometry.fromWkt('PolygonM((11 13 1,14 15 2, 11 15 3, 11 13 1))').asPolygon(),
[[QgsPointXY(11, 13), QgsPointXY(14, 15), QgsPointXY(11, 15), QgsPointXY(11, 13)]])
self.assertEqual(QgsGeometry.fromWkt('PolygonZM((11 13 1 11,14 15 2 12 , 11 15 3 13 , 11 13 1 11))').asPolygon(),
[[QgsPointXY(11, 13), QgsPointXY(14, 15), QgsPointXY(11, 15), QgsPointXY(11, 13)]])
with self.assertRaises(TypeError):
QgsGeometry.fromWkt('Point(11 13)').asPolygon()
with self.assertRaises(TypeError):
QgsGeometry.fromWkt('MultiPoint(11 13,14 15)').asPolygon()
with self.assertRaises(TypeError):
QgsGeometry.fromWkt('MultiLineString((11 13, 14 15),(1 2, 3 4))').asPolygon()
with self.assertRaises(TypeError):
QgsGeometry.fromWkt('LineString(11 13,14 15)').asPolygon()
with self.assertRaises(TypeError):
QgsGeometry.fromWkt('MultiPolygon(((11 13,14 15, 11 15, 11 13)))').asPolygon()
with self.assertRaises(ValueError):
QgsGeometry().asPolygon()

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.
Expand Down

0 comments on commit 7ef9760

Please sign in to comment.