From 5d2495e65a56a79ac8409e0a5ec3ad68d5fae74e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 17 Aug 2020 13:00:47 +1000 Subject: [PATCH] [api] Add helper api to directly retrieve a point/linestring/polygon from a QgsMultiPoint/QgsMultiLineString/QgsMultiPolygon Avoids the need to have to manually do an annoying cast when we already know the geometry type --- .../geometry/qgsmulticurve.sip.in | 24 ++++++++ .../geometry/qgsmultilinestring.sip.in | 24 ++++++++ .../geometry/qgsmultipoint.sip.in | 23 +++++++ .../geometry/qgsmultipolygon.sip.in | 25 ++++++++ .../geometry/qgsmultisurface.sip.in | 26 ++++++++ src/3d/symbols/qgsline3dsymbol_p.cpp | 8 +-- src/3d/symbols/qgspolygon3dsymbol_p.cpp | 4 +- .../interpolation/qgstininterpolator.cpp | 2 +- .../qgsalgorithmprojectpointcartesian.cpp | 2 +- .../processing/qgsalgorithmtransect.cpp | 2 +- .../processing/qgsalgorithmwedgebuffers.cpp | 2 +- src/app/qgsmaptoolreverseline.cpp | 2 +- src/app/vertextool/qgsvertextool.cpp | 4 +- src/core/geometry/qgsgeometry.cpp | 6 +- src/core/geometry/qgsmulticurve.cpp | 10 ++++ src/core/geometry/qgsmulticurve.h | 45 ++++++++++++++ src/core/geometry/qgsmultilinestring.cpp | 10 ++++ src/core/geometry/qgsmultilinestring.h | 46 ++++++++++++++ src/core/geometry/qgsmultipoint.cpp | 10 ++++ src/core/geometry/qgsmultipoint.h | 44 ++++++++++++++ src/core/geometry/qgsmultipolygon.cpp | 10 ++++ src/core/geometry/qgsmultipolygon.h | 47 +++++++++++++++ src/core/geometry/qgsmultisurface.cpp | 10 ++++ src/core/geometry/qgsmultisurface.h | 48 +++++++++++++++ .../vectortile/qgsvectortilemvtencoder.cpp | 6 +- tests/src/core/testqgsgeometry.cpp | 18 +++--- tests/src/python/test_qgsgeometry.py | 60 ++++++++++++++++++- 27 files changed, 485 insertions(+), 33 deletions(-) diff --git a/python/core/auto_generated/geometry/qgsmulticurve.sip.in b/python/core/auto_generated/geometry/qgsmulticurve.sip.in index aa3d1a2b08a..040e0c48d1a 100644 --- a/python/core/auto_generated/geometry/qgsmulticurve.sip.in +++ b/python/core/auto_generated/geometry/qgsmulticurve.sip.in @@ -21,6 +21,30 @@ Multi curve geometry collection. %End public: QgsMultiCurve(); + + + + SIP_PYOBJECT curveN( int index ) /TypeHint="QgsCurve"/; +%Docstring +Returns the curve with the specified ``index``. + +An IndexError will be raised if no curve with the specified index exists. + +.. versionadded:: 3.16 +%End +%MethodCode + if ( a0 < 0 || a0 >= sipCpp->numGeometries() ) + { + PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) ); + sipIsErr = 1; + } + else + { + return sipConvertFromType( sipCpp->curveN( a0 ), sipType_QgsCurve, NULL ); + } +%End + + virtual QString geometryType() const; virtual QgsMultiCurve *clone() const /Factory/; diff --git a/python/core/auto_generated/geometry/qgsmultilinestring.sip.in b/python/core/auto_generated/geometry/qgsmultilinestring.sip.in index 0a04df84a76..61e611e3737 100644 --- a/python/core/auto_generated/geometry/qgsmultilinestring.sip.in +++ b/python/core/auto_generated/geometry/qgsmultilinestring.sip.in @@ -8,6 +8,7 @@ + class QgsMultiLineString: QgsMultiCurve { %Docstring @@ -22,6 +23,29 @@ Multi line string geometry collection. public: QgsMultiLineString(); + + + SIP_PYOBJECT lineStringN( int index ) /TypeHint="QgsLineString"/; +%Docstring +Returns the line string with the specified ``index``. + +An IndexError will be raised if no line string with the specified index exists. + +.. versionadded:: 3.16 +%End +%MethodCode + if ( a0 < 0 || a0 >= sipCpp->numGeometries() ) + { + PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) ); + sipIsErr = 1; + } + else + { + return sipConvertFromType( sipCpp->lineStringN( a0 ), sipType_QgsLineString, NULL ); + } +%End + + virtual QString geometryType() const; virtual QgsMultiLineString *clone() const /Factory/; diff --git a/python/core/auto_generated/geometry/qgsmultipoint.sip.in b/python/core/auto_generated/geometry/qgsmultipoint.sip.in index 5e55390cecf..c792c23ee38 100644 --- a/python/core/auto_generated/geometry/qgsmultipoint.sip.in +++ b/python/core/auto_generated/geometry/qgsmultipoint.sip.in @@ -22,6 +22,29 @@ Multi point geometry collection. public: QgsMultiPoint(); + + + SIP_PYOBJECT pointN( int index ) /TypeHint="QgsPoint"/; +%Docstring +Returns the point with the specified ``index``. + +An IndexError will be raised if no point with the specified index exists. + +.. versionadded:: 3.16 +%End +%MethodCode + if ( a0 < 0 || a0 >= sipCpp->numGeometries() ) + { + PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) ); + sipIsErr = 1; + } + else + { + return sipConvertFromType( sipCpp->pointN( a0 ), sipType_QgsPoint, NULL ); + } +%End + + virtual QString geometryType() const; virtual QgsMultiPoint *clone() const /Factory/; diff --git a/python/core/auto_generated/geometry/qgsmultipolygon.sip.in b/python/core/auto_generated/geometry/qgsmultipolygon.sip.in index ae893d63eee..9949643ed16 100644 --- a/python/core/auto_generated/geometry/qgsmultipolygon.sip.in +++ b/python/core/auto_generated/geometry/qgsmultipolygon.sip.in @@ -8,6 +8,7 @@ + class QgsMultiPolygon: QgsMultiSurface { %Docstring @@ -21,6 +22,30 @@ Multi polygon geometry collection. %End public: QgsMultiPolygon(); + + + + SIP_PYOBJECT polygonN( int index ) /TypeHint="QgsPolygon"/; +%Docstring +Returns the polygon with the specified ``index``. + +An IndexError will be raised if no polygon with the specified index exists. + +.. versionadded:: 3.16 +%End +%MethodCode + if ( a0 < 0 || a0 >= sipCpp->numGeometries() ) + { + PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) ); + sipIsErr = 1; + } + else + { + return sipConvertFromType( sipCpp->polygonN( a0 ), sipType_QgsPolygon, NULL ); + } +%End + + virtual QString geometryType() const; virtual void clear(); diff --git a/python/core/auto_generated/geometry/qgsmultisurface.sip.in b/python/core/auto_generated/geometry/qgsmultisurface.sip.in index c5d64ef1f77..bc4fd0d1a6d 100644 --- a/python/core/auto_generated/geometry/qgsmultisurface.sip.in +++ b/python/core/auto_generated/geometry/qgsmultisurface.sip.in @@ -8,6 +8,8 @@ + + class QgsMultiSurface: QgsGeometryCollection { %Docstring @@ -21,6 +23,30 @@ Multi surface geometry collection. %End public: QgsMultiSurface(); + + + + SIP_PYOBJECT surfaceN( int index ) /TypeHint="QgsSurface"/; +%Docstring +Returns the surface with the specified ``index``. + +An IndexError will be raised if no surface with the specified index exists. + +.. versionadded:: 3.16 +%End +%MethodCode + if ( a0 < 0 || a0 >= sipCpp->numGeometries() ) + { + PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) ); + sipIsErr = 1; + } + else + { + return sipConvertFromType( sipCpp->surfaceN( a0 ), sipType_QgsSurface, NULL ); + } +%End + + virtual QString geometryType() const; virtual void clear(); diff --git a/src/3d/symbols/qgsline3dsymbol_p.cpp b/src/3d/symbols/qgsline3dsymbol_p.cpp index d2c1c962ed4..465562d057a 100644 --- a/src/3d/symbols/qgsline3dsymbol_p.cpp +++ b/src/3d/symbols/qgsline3dsymbol_p.cpp @@ -130,9 +130,7 @@ void QgsBufferedLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DR QgsMultiPolygon *mpolyBuffered = static_cast( buffered ); for ( int i = 0; i < mpolyBuffered->numGeometries(); ++i ) { - QgsAbstractGeometry *partBuffered = mpolyBuffered->geometryN( i ); - Q_ASSERT( QgsWkbTypes::flatType( partBuffered->wkbType() ) == QgsWkbTypes::Polygon ); - QgsPolygon *polyBuffered = static_cast( partBuffered )->clone(); // need to clone individual geometry parts + QgsPolygon *polyBuffered = static_cast( mpolyBuffered->polygonN( i ) )->clone(); // need to clone individual geometry parts processPolygon( polyBuffered, f.id(), mSymbol.height(), mSymbol.extrusionHeight(), context, out ); } delete buffered; @@ -259,7 +257,7 @@ void QgsSimpleLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRen { for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom ) { - const QgsLineString *ls = qgsgeometry_cast( mls->geometryN( nGeom ) ); + const QgsLineString *ls = mls->lineStringN( nGeom ); out.addLineString( *ls ); } } @@ -372,7 +370,7 @@ void QgsThickLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRend { for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom ) { - const QgsLineString *ls = qgsgeometry_cast( mls->geometryN( nGeom ) ); + const QgsLineString *ls = mls->lineStringN( nGeom ); out.addLineString( *ls ); } } diff --git a/src/3d/symbols/qgspolygon3dsymbol_p.cpp b/src/3d/symbols/qgspolygon3dsymbol_p.cpp index 7057cefd545..0dbc975bd76 100644 --- a/src/3d/symbols/qgspolygon3dsymbol_p.cpp +++ b/src/3d/symbols/qgspolygon3dsymbol_p.cpp @@ -175,9 +175,7 @@ void QgsPolygon3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRender { for ( int i = 0; i < mpoly->numGeometries(); ++i ) { - const QgsAbstractGeometry *g2 = mpoly->geometryN( i ); - Q_ASSERT( QgsWkbTypes::flatType( g2->wkbType() ) == QgsWkbTypes::Polygon ); - QgsPolygon *polyClone = static_cast< const QgsPolygon *>( g2 )->clone(); + QgsPolygon *polyClone = static_cast< const QgsPolygon *>( mpoly->polygonN( i ) )->clone(); processPolygon( polyClone, f.id(), height, extrusionHeight, context, out ); } } diff --git a/src/analysis/interpolation/qgstininterpolator.cpp b/src/analysis/interpolation/qgstininterpolator.cpp index 1d43cb860cb..5fa52dc18d0 100644 --- a/src/analysis/interpolation/qgstininterpolator.cpp +++ b/src/analysis/interpolation/qgstininterpolator.cpp @@ -273,7 +273,7 @@ int QgsTinInterpolator::insertData( const QgsFeature &f, QgsInterpolator::ValueS const QgsMultiCurve *mc = qgsgeometry_cast< const QgsMultiCurve * >( g.constGet() ); for ( int i = 0; i < mc->numGeometries(); ++i ) { - curves.emplace_back( qgsgeometry_cast< const QgsCurve * >( mc->geometryN( i ) ) ); + curves.emplace_back( mc->curveN( i ) ); } } else diff --git a/src/analysis/processing/qgsalgorithmprojectpointcartesian.cpp b/src/analysis/processing/qgsalgorithmprojectpointcartesian.cpp index e729b9de1b2..d5f9b0a725d 100644 --- a/src/analysis/processing/qgsalgorithmprojectpointcartesian.cpp +++ b/src/analysis/processing/qgsalgorithmprojectpointcartesian.cpp @@ -126,7 +126,7 @@ QgsFeatureList QgsProjectPointCartesianAlgorithm::processFeature( const QgsFeatu result->reserve( mp->numGeometries() ); for ( int i = 0; i < mp->numGeometries(); ++i ) { - const QgsPoint *p = static_cast< const QgsPoint * >( mp->geometryN( i ) ); + const QgsPoint *p = mp->pointN( i ); result->addGeometry( p->project( distance, bearing ).clone() ); } f.setGeometry( QgsGeometry( std::move( result ) ) ); diff --git a/src/analysis/processing/qgsalgorithmtransect.cpp b/src/analysis/processing/qgsalgorithmtransect.cpp index 3b130176365..779f8686f46 100644 --- a/src/analysis/processing/qgsalgorithmtransect.cpp +++ b/src/analysis/processing/qgsalgorithmtransect.cpp @@ -173,7 +173,7 @@ QVariantMap QgsTransectAlgorithm::processAlgorithm( const QVariantMap ¶meter const QgsMultiLineString *multiLine = static_cast< const QgsMultiLineString * >( inputGeometry.constGet() ); for ( int id = 0; id < multiLine->numGeometries(); ++id ) { - const QgsLineString *line = static_cast< const QgsLineString * >( multiLine->geometryN( id ) ); + const QgsLineString *line = multiLine->lineStringN( id ); QgsAbstractGeometry::vertex_iterator it = line->vertices_begin(); while ( it != line->vertices_end() ) { diff --git a/src/analysis/processing/qgsalgorithmwedgebuffers.cpp b/src/analysis/processing/qgsalgorithmwedgebuffers.cpp index 40792ceff53..6ca8a0da264 100644 --- a/src/analysis/processing/qgsalgorithmwedgebuffers.cpp +++ b/src/analysis/processing/qgsalgorithmwedgebuffers.cpp @@ -176,7 +176,7 @@ QgsFeatureList QgsWedgeBuffersAlgorithm::processFeature( const QgsFeature &featu result->reserve( mp->numGeometries() ); for ( int i = 0; i < mp->numGeometries(); ++i ) { - const QgsPoint *p = static_cast< const QgsPoint * >( mp->geometryN( i ) ); + const QgsPoint *p = mp->pointN( i ); result->addGeometry( QgsGeometry::createWedgeBuffer( *p, azimuth, width, outerRadius, innerRadius ).constGet()->clone() ); } f.setGeometry( QgsGeometry( std::move( result ) ) ); diff --git a/src/app/qgsmaptoolreverseline.cpp b/src/app/qgsmaptoolreverseline.cpp index e57149aa8f7..9a55581e2f8 100644 --- a/src/app/qgsmaptoolreverseline.cpp +++ b/src/app/qgsmaptoolreverseline.cpp @@ -100,7 +100,7 @@ void QgsMapToolReverseLine::canvasReleaseEvent( QgsMapMouseEvent *e ) if ( f.geometry().isMultipart() ) { std::unique_ptr line_reversed( static_cast( f.geometry().constGet()->clone() ) ); - std::unique_ptr line_part( static_cast( line_reversed->geometryN( mPressedPartNum )->clone() ) ); + std::unique_ptr line_part( line_reversed->curveN( mPressedPartNum )->clone() ); std::unique_ptr line_part_reversed( line_part->reversed() ); line_reversed->removeGeometry( mPressedPartNum ); line_reversed->insertGeometry( line_part_reversed.release(), mPressedPartNum ); diff --git a/src/app/vertextool/qgsvertextool.cpp b/src/app/vertextool/qgsvertextool.cpp index a089fc56208..e93b8d663c1 100644 --- a/src/app/vertextool/qgsvertextool.cpp +++ b/src/app/vertextool/qgsvertextool.cpp @@ -67,7 +67,7 @@ static bool isEndpointAtVertexIndex( const QgsGeometry &geom, int vertexIndex ) { for ( int i = 0; i < multiCurve->numGeometries(); ++i ) { - QgsCurve *part = qgsgeometry_cast( multiCurve->geometryN( i ) ); + const QgsCurve *part = multiCurve->curveN( i ); Q_ASSERT( part ); if ( vertexIndex < part->numPoints() ) return vertexIndex == 0 || vertexIndex == part->numPoints() - 1; @@ -97,7 +97,7 @@ int adjacentVertexIndexToEndpoint( const QgsGeometry &geom, int vertexIndex ) int offset = 0; for ( int i = 0; i < multiCurve->numGeometries(); ++i ) { - const QgsCurve *part = qgsgeometry_cast( multiCurve->geometryN( i ) ); + const QgsCurve *part = multiCurve->curveN( i ); Q_ASSERT( part ); if ( vertexIndex < part->numPoints() ) return vertexIndex == 0 ? offset + 1 : offset + part->numPoints() - 2; diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp index 2c5960fc77e..1855e2e678e 100644 --- a/src/core/geometry/qgsgeometry.cpp +++ b/src/core/geometry/qgsgeometry.cpp @@ -1642,7 +1642,7 @@ QgsMultiPointXY QgsGeometry::asMultiPoint() const QgsMultiPointXY multiPoint( nPoints ); for ( int i = 0; i < nPoints; ++i ) { - const QgsPoint *pt = static_cast( mp->geometryN( i ) ); + const QgsPoint *pt = mp->pointN( i ); multiPoint[i].setX( pt->x() ); multiPoint[i].setY( pt->y() ); } @@ -3158,7 +3158,7 @@ QgsGeometry QgsGeometry::smooth( const unsigned int iterations, const double off resultMultiline->reserve( multiLine->numGeometries() ); for ( int i = 0; i < multiLine->numGeometries(); ++i ) { - resultMultiline->addGeometry( smoothLine( *( qgsgeometry_cast< const QgsLineString * >( multiLine->geometryN( i ) ) ), iterations, offset, minimumDistance, maxAngle ).release() ); + resultMultiline->addGeometry( smoothLine( *( multiLine->lineStringN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() ); } return QgsGeometry( std::move( resultMultiline ) ); } @@ -3177,7 +3177,7 @@ QgsGeometry QgsGeometry::smooth( const unsigned int iterations, const double off resultMultiPoly->reserve( multiPoly->numGeometries() ); for ( int i = 0; i < multiPoly->numGeometries(); ++i ) { - resultMultiPoly->addGeometry( smoothPolygon( *( qgsgeometry_cast< const QgsPolygon * >( multiPoly->geometryN( i ) ) ), iterations, offset, minimumDistance, maxAngle ).release() ); + resultMultiPoly->addGeometry( smoothPolygon( *( multiPoly->polygonN( i ) ), iterations, offset, minimumDistance, maxAngle ).release() ); } return QgsGeometry( std::move( resultMultiPoly ) ); } diff --git a/src/core/geometry/qgsmulticurve.cpp b/src/core/geometry/qgsmulticurve.cpp index f736df3b258..f0516844dbd 100644 --- a/src/core/geometry/qgsmulticurve.cpp +++ b/src/core/geometry/qgsmulticurve.cpp @@ -31,6 +31,16 @@ QgsMultiCurve::QgsMultiCurve() mWkbType = QgsWkbTypes::MultiCurve; } +QgsCurve *QgsMultiCurve::curveN( int index ) +{ + return qgsgeometry_cast< QgsCurve * >( geometryN( index ) ); +} + +const QgsCurve *QgsMultiCurve::curveN( int index ) const +{ + return qgsgeometry_cast< const QgsCurve * >( geometryN( index ) ); +} + QString QgsMultiCurve::geometryType() const { return QStringLiteral( "MultiCurve" ); diff --git a/src/core/geometry/qgsmulticurve.h b/src/core/geometry/qgsmulticurve.h index 3effe685141..13613970fb7 100644 --- a/src/core/geometry/qgsmulticurve.h +++ b/src/core/geometry/qgsmulticurve.h @@ -30,6 +30,51 @@ class CORE_EXPORT QgsMultiCurve: public QgsGeometryCollection { public: QgsMultiCurve(); + + +#ifndef SIP_RUN + + /** + * Returns the curve with the specified \a index. + * + * \since QGIS 3.16 + */ + QgsCurve *curveN( int index ); +#else + + /** + * Returns the curve with the specified \a index. + * + * An IndexError will be raised if no curve with the specified index exists. + * + * \since QGIS 3.16 + */ + SIP_PYOBJECT curveN( int index ) SIP_TYPEHINT( QgsCurve ); + % MethodCode + if ( a0 < 0 || a0 >= sipCpp->numGeometries() ) + { + PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) ); + sipIsErr = 1; + } + else + { + return sipConvertFromType( sipCpp->curveN( a0 ), sipType_QgsCurve, NULL ); + } + % End +#endif + +#ifndef SIP_RUN + + /** + * Returns the curve with the specified \a index. + * + * \note Not available in Python bindings + * + * \since QGIS 3.16 + */ + const QgsCurve *curveN( int index ) const; +#endif + QString geometryType() const override; QgsMultiCurve *clone() const override SIP_FACTORY; void clear() override; diff --git a/src/core/geometry/qgsmultilinestring.cpp b/src/core/geometry/qgsmultilinestring.cpp index 2473658dd29..1ce05c7832f 100644 --- a/src/core/geometry/qgsmultilinestring.cpp +++ b/src/core/geometry/qgsmultilinestring.cpp @@ -30,6 +30,16 @@ QgsMultiLineString::QgsMultiLineString() mWkbType = QgsWkbTypes::MultiLineString; } +QgsLineString *QgsMultiLineString::lineStringN( int index ) +{ + return qgsgeometry_cast< QgsLineString * >( geometryN( index ) ); +} + +const QgsLineString *QgsMultiLineString::lineStringN( int index ) const +{ + return qgsgeometry_cast< const QgsLineString * >( geometryN( index ) ); +} + QString QgsMultiLineString::geometryType() const { return QStringLiteral( "MultiLineString" ); diff --git a/src/core/geometry/qgsmultilinestring.h b/src/core/geometry/qgsmultilinestring.h index e084f1a4689..1420167b7f6 100644 --- a/src/core/geometry/qgsmultilinestring.h +++ b/src/core/geometry/qgsmultilinestring.h @@ -20,6 +20,8 @@ email : marco.hugentobler at sourcepole dot com #include "qgis_sip.h" #include "qgsmulticurve.h" +class QgsLineString; + /** * \ingroup core * \class QgsMultiLineString @@ -31,6 +33,50 @@ class CORE_EXPORT QgsMultiLineString: public QgsMultiCurve public: QgsMultiLineString(); + +#ifndef SIP_RUN + + /** + * Returns the line string with the specified \a index. + * + * \since QGIS 3.16 + */ + QgsLineString *lineStringN( int index ); +#else + + /** + * Returns the line string with the specified \a index. + * + * An IndexError will be raised if no line string with the specified index exists. + * + * \since QGIS 3.16 + */ + SIP_PYOBJECT lineStringN( int index ) SIP_TYPEHINT( QgsLineString ); + % MethodCode + if ( a0 < 0 || a0 >= sipCpp->numGeometries() ) + { + PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) ); + sipIsErr = 1; + } + else + { + return sipConvertFromType( sipCpp->lineStringN( a0 ), sipType_QgsLineString, NULL ); + } + % End +#endif + +#ifndef SIP_RUN + + /** + * Returns the line string with the specified \a index. + * + * \note Not available in Python bindings + * + * \since QGIS 3.16 + */ + const QgsLineString *lineStringN( int index ) const; +#endif + QString geometryType() const override; QgsMultiLineString *clone() const override SIP_FACTORY; void clear() override; diff --git a/src/core/geometry/qgsmultipoint.cpp b/src/core/geometry/qgsmultipoint.cpp index 244fe3cb63d..c907e19038c 100644 --- a/src/core/geometry/qgsmultipoint.cpp +++ b/src/core/geometry/qgsmultipoint.cpp @@ -28,6 +28,16 @@ QgsMultiPoint::QgsMultiPoint() mWkbType = QgsWkbTypes::MultiPoint; } +QgsPoint *QgsMultiPoint::pointN( int index ) +{ + return qgsgeometry_cast< QgsPoint * >( geometryN( index ) ); +} + +const QgsPoint *QgsMultiPoint::pointN( int index ) const +{ + return qgsgeometry_cast< const QgsPoint * >( geometryN( index ) ); +} + QString QgsMultiPoint::geometryType() const { return QStringLiteral( "MultiPoint" ); diff --git a/src/core/geometry/qgsmultipoint.h b/src/core/geometry/qgsmultipoint.h index b172746a1e6..fac51091582 100644 --- a/src/core/geometry/qgsmultipoint.h +++ b/src/core/geometry/qgsmultipoint.h @@ -31,6 +31,50 @@ class CORE_EXPORT QgsMultiPoint: public QgsGeometryCollection public: QgsMultiPoint(); + +#ifndef SIP_RUN + + /** + * Returns the point with the specified \a index. + * + * \since QGIS 3.16 + */ + QgsPoint *pointN( int index ); +#else + + /** + * Returns the point with the specified \a index. + * + * An IndexError will be raised if no point with the specified index exists. + * + * \since QGIS 3.16 + */ + SIP_PYOBJECT pointN( int index ) SIP_TYPEHINT( QgsPoint ); + % MethodCode + if ( a0 < 0 || a0 >= sipCpp->numGeometries() ) + { + PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) ); + sipIsErr = 1; + } + else + { + return sipConvertFromType( sipCpp->pointN( a0 ), sipType_QgsPoint, NULL ); + } + % End +#endif + +#ifndef SIP_RUN + + /** + * Returns the point with the specified \a index. + * + * \note Not available in Python bindings + * + * \since QGIS 3.16 + */ + const QgsPoint *pointN( int index ) const; +#endif + QString geometryType() const override; QgsMultiPoint *clone() const override SIP_FACTORY; QgsMultiPoint *toCurveType() const override SIP_FACTORY; diff --git a/src/core/geometry/qgsmultipolygon.cpp b/src/core/geometry/qgsmultipolygon.cpp index 1b3daa3ebe1..5c67cc82141 100644 --- a/src/core/geometry/qgsmultipolygon.cpp +++ b/src/core/geometry/qgsmultipolygon.cpp @@ -30,6 +30,16 @@ QgsMultiPolygon::QgsMultiPolygon() mWkbType = QgsWkbTypes::MultiPolygon; } +QgsPolygon *QgsMultiPolygon::polygonN( int index ) +{ + return qgsgeometry_cast< QgsPolygon * >( geometryN( index ) ); +} + +const QgsPolygon *QgsMultiPolygon::polygonN( int index ) const +{ + return qgsgeometry_cast< const QgsPolygon * >( geometryN( index ) ); +} + QString QgsMultiPolygon::geometryType() const { return QStringLiteral( "MultiPolygon" ); diff --git a/src/core/geometry/qgsmultipolygon.h b/src/core/geometry/qgsmultipolygon.h index f7da8d7b220..bf78a2eff73 100644 --- a/src/core/geometry/qgsmultipolygon.h +++ b/src/core/geometry/qgsmultipolygon.h @@ -20,6 +20,8 @@ email : marco.hugentobler at sourcepole dot com #include "qgis_sip.h" #include "qgsmultisurface.h" +class QgsPolygon; + /** * \ingroup core * \class QgsMultiPolygon @@ -30,6 +32,51 @@ class CORE_EXPORT QgsMultiPolygon: public QgsMultiSurface { public: QgsMultiPolygon(); + + +#ifndef SIP_RUN + + /** + * Returns the polygon with the specified \a index. + * + * \since QGIS 3.16 + */ + QgsPolygon *polygonN( int index ); +#else + + /** + * Returns the polygon with the specified \a index. + * + * An IndexError will be raised if no polygon with the specified index exists. + * + * \since QGIS 3.16 + */ + SIP_PYOBJECT polygonN( int index ) SIP_TYPEHINT( QgsPolygon ); + % MethodCode + if ( a0 < 0 || a0 >= sipCpp->numGeometries() ) + { + PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) ); + sipIsErr = 1; + } + else + { + return sipConvertFromType( sipCpp->polygonN( a0 ), sipType_QgsPolygon, NULL ); + } + % End +#endif + +#ifndef SIP_RUN + + /** + * Returns the polygon with the specified \a index. + * + * \note Not available in Python bindings + * + * \since QGIS 3.16 + */ + const QgsPolygon *polygonN( int index ) const; +#endif + QString geometryType() const override; void clear() override; QgsMultiPolygon *clone() const override SIP_FACTORY; diff --git a/src/core/geometry/qgsmultisurface.cpp b/src/core/geometry/qgsmultisurface.cpp index 4201dae0144..fe874108152 100644 --- a/src/core/geometry/qgsmultisurface.cpp +++ b/src/core/geometry/qgsmultisurface.cpp @@ -31,6 +31,16 @@ QgsMultiSurface::QgsMultiSurface() mWkbType = QgsWkbTypes::MultiSurface; } +QgsSurface *QgsMultiSurface::surfaceN( int index ) +{ + return qgsgeometry_cast< QgsSurface * >( geometryN( index ) ); +} + +const QgsSurface *QgsMultiSurface::surfaceN( int index ) const +{ + return qgsgeometry_cast< const QgsSurface * >( geometryN( index ) ); +} + QString QgsMultiSurface::geometryType() const { return QStringLiteral( "MultiSurface" ); diff --git a/src/core/geometry/qgsmultisurface.h b/src/core/geometry/qgsmultisurface.h index 4c54f8d386c..f71dcee2514 100644 --- a/src/core/geometry/qgsmultisurface.h +++ b/src/core/geometry/qgsmultisurface.h @@ -20,6 +20,9 @@ email : marco.hugentobler at sourcepole dot com #include "qgis_sip.h" #include "qgsgeometrycollection.h" + +class QgsSurface; + /** * \ingroup core * \class QgsMultiSurface @@ -30,6 +33,51 @@ class CORE_EXPORT QgsMultiSurface: public QgsGeometryCollection { public: QgsMultiSurface(); + + +#ifndef SIP_RUN + + /** + * Returns the surface with the specified \a index. + * + * \since QGIS 3.16 + */ + QgsSurface *surfaceN( int index ); +#else + + /** + * Returns the surface with the specified \a index. + * + * An IndexError will be raised if no surface with the specified index exists. + * + * \since QGIS 3.16 + */ + SIP_PYOBJECT surfaceN( int index ) SIP_TYPEHINT( QgsSurface ); + % MethodCode + if ( a0 < 0 || a0 >= sipCpp->numGeometries() ) + { + PyErr_SetString( PyExc_IndexError, QByteArray::number( a0 ) ); + sipIsErr = 1; + } + else + { + return sipConvertFromType( sipCpp->surfaceN( a0 ), sipType_QgsSurface, NULL ); + } + % End +#endif + +#ifndef SIP_RUN + + /** + * Returns the surface with the specified \a index. + * + * \note Not available in Python bindings + * + * \since QGIS 3.16 + */ + const QgsSurface *surfaceN( int index ) const; +#endif + QString geometryType() const override; void clear() override; QgsMultiSurface *clone() const override SIP_FACTORY; diff --git a/src/core/vectortile/qgsvectortilemvtencoder.cpp b/src/core/vectortile/qgsvectortilemvtencoder.cpp index fec53573e00..807f209d9e0 100644 --- a/src/core/vectortile/qgsvectortilemvtencoder.cpp +++ b/src/core/vectortile/qgsvectortilemvtencoder.cpp @@ -336,7 +336,7 @@ void QgsVectorTileMVTEncoder::addFeature( vector_tile::Tile_Layer *tileLayer, co const QgsMultiPoint *mpt = static_cast( geom ); geomWriter.addMoveTo( mpt->numGeometries() ); for ( int i = 0; i < mpt->numGeometries(); ++i ) - geomWriter.addPoint( *static_cast( mpt->geometryN( i ) ) ); + geomWriter.addPoint( *mpt->pointN( i ) ); } break; @@ -345,7 +345,7 @@ void QgsVectorTileMVTEncoder::addFeature( vector_tile::Tile_Layer *tileLayer, co const QgsMultiLineString *mls = qgsgeometry_cast( geom ); for ( int i = 0; i < mls->numGeometries(); ++i ) { - encodeLineString( qgsgeometry_cast( mls->geometryN( i ) ), true, false, geomWriter ); + encodeLineString( mls->lineStringN( i ), true, false, geomWriter ); } } break; @@ -355,7 +355,7 @@ void QgsVectorTileMVTEncoder::addFeature( vector_tile::Tile_Layer *tileLayer, co const QgsMultiPolygon *mp = qgsgeometry_cast( geom ); for ( int i = 0; i < mp->numGeometries(); ++i ) { - encodePolygon( static_cast( mp->geometryN( i ) ), geomWriter ); + encodePolygon( mp->polygonN( i ), geomWriter ); } } break; diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index 0d2d4bb2c41..d6810504389 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -2614,10 +2614,10 @@ void TestQgsGeometry::circularString() QgsAbstractGeometry *boundary = boundary1.boundary(); QgsMultiPoint *mpBoundary = dynamic_cast< QgsMultiPoint * >( boundary ); QVERIFY( mpBoundary ); - QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->x(), 0.0 ); - QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->y(), 0.0 ); - QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->x(), 1.0 ); - QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->y(), 1.0 ); + QCOMPARE( mpBoundary->pointN( 0 )->x(), 0.0 ); + QCOMPARE( mpBoundary->pointN( 0 )->y(), 0.0 ); + QCOMPARE( mpBoundary->pointN( 1 )->x(), 1.0 ); + QCOMPARE( mpBoundary->pointN( 1 )->y(), 1.0 ); delete boundary; // closed string = no boundary @@ -2629,11 +2629,11 @@ void TestQgsGeometry::circularString() boundary = boundary1.boundary(); mpBoundary = dynamic_cast< QgsMultiPoint * >( boundary ); QVERIFY( mpBoundary ); - QCOMPARE( mpBoundary->geometryN( 0 )->wkbType(), QgsWkbTypes::PointZ ); - QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->x(), 0.0 ); - QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->y(), 0.0 ); - QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->z(), 10.0 ); - QCOMPARE( mpBoundary->geometryN( 1 )->wkbType(), QgsWkbTypes::PointZ ); + QCOMPARE( mpBoundary->pointN( 0 )->wkbType(), QgsWkbTypes::PointZ ); + QCOMPARE( mpBoundary->pointN( 0 )->x(), 0.0 ); + QCOMPARE( mpBoundary->pointN( 0 )->y(), 0.0 ); + QCOMPARE( mpBoundary->pointN( 0 )->z(), 10.0 ); + QCOMPARE( mpBoundary->pointN( 1 )->wkbType(), QgsWkbTypes::PointZ ); QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->x(), 1.0 ); QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->y(), 1.0 ); QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->z(), 20.0 ); diff --git a/tests/src/python/test_qgsgeometry.py b/tests/src/python/test_qgsgeometry.py index cd621c921a0..5a2e8558a57 100644 --- a/tests/src/python/test_qgsgeometry.py +++ b/tests/src/python/test_qgsgeometry.py @@ -444,12 +444,12 @@ class TestQgsGeometry(unittest.TestCase): g = QgsGeometryCollection() self.assertTrue(bool(g)) self.assertEqual(len(g), 0) - g = QgsGeometryCollection() - g.fromWkt('GeometryCollection( Point(1 2), Point(11 12))') + g = QgsMultiPoint() + g.fromWkt('MultiPoint( (1 2), (11 12))') self.assertTrue(bool(g)) self.assertEqual(len(g), 2) - # pointN + # geometryN with self.assertRaises(IndexError): g.geometryN(-1) with self.assertRaises(IndexError): @@ -457,7 +457,16 @@ class TestQgsGeometry(unittest.TestCase): self.assertEqual(g.geometryN(0), QgsPoint(1, 2)) self.assertEqual(g.geometryN(1), QgsPoint(11, 12)) + # pointN + with self.assertRaises(IndexError): + g.pointN(-1) + with self.assertRaises(IndexError): + g.pointN(2) + self.assertEqual(g.pointN(0), QgsPoint(1, 2)) + self.assertEqual(g.pointN(1), QgsPoint(11, 12)) + # removeGeometry + g = QgsGeometryCollection() g.fromWkt('GeometryCollection( Point(1 2), Point(11 12), Point(33 34))') with self.assertRaises(IndexError): g.removeGeometry(-1) @@ -508,6 +517,51 @@ 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)']) + g = QgsGeometryCollection() + g.fromWkt('GeometryCollection( Point(1 2), Point(11 12))') + self.assertTrue(bool(g)) + self.assertEqual(len(g), 2) + + # lineStringN + g = QgsMultiLineString() + g.fromWkt('MultiLineString( (1 2, 3 4), (11 12, 13 14))') + with self.assertRaises(IndexError): + g.lineStringN(-1) + with self.assertRaises(IndexError): + g.lineStringN(2) + self.assertEqual(g.lineStringN(0).asWkt(), 'LineString (1 2, 3 4)') + self.assertEqual(g.lineStringN(1).asWkt(), 'LineString (11 12, 13 14)') + + # curveN + g = QgsMultiCurve() + g.fromWkt('MultiCurve( LineString(1 2, 3 4), LineString(11 12, 13 14))') + with self.assertRaises(IndexError): + g.curveN(-1) + with self.assertRaises(IndexError): + g.curveN(2) + self.assertEqual(g.curveN(0).asWkt(), 'LineString (1 2, 3 4)') + self.assertEqual(g.curveN(1).asWkt(), 'LineString (11 12, 13 14)') + + # polygonN + g = QgsMultiPolygon() + g.fromWkt('MultiPolygon( ((1 2, 3 4, 3 6, 1 2)), ((11 12, 13 14, 13 16, 11 12)))') + with self.assertRaises(IndexError): + g.polygonN(-1) + with self.assertRaises(IndexError): + g.polygonN(2) + self.assertEqual(g.polygonN(0).asWkt(), 'Polygon ((1 2, 3 4, 3 6, 1 2))') + self.assertEqual(g.polygonN(1).asWkt(), 'Polygon ((11 12, 13 14, 13 16, 11 12))') + + # surfaceN + g = QgsMultiSurface() + g.fromWkt('MultiSurface( Polygon((1 2, 3 4, 3 6, 1 2)), Polygon((11 12, 13 14, 13 16, 11 12)))') + with self.assertRaises(IndexError): + g.surfaceN(-1) + with self.assertRaises(IndexError): + g.surfaceN(2) + self.assertEqual(g.surfaceN(0).asWkt(), 'Polygon ((1 2, 3 4, 3 6, 1 2))') + self.assertEqual(g.surfaceN(1).asWkt(), 'Polygon ((11 12, 13 14, 13 16, 11 12))') + def testCurvePolygonPythonAdditions(self): """ Tests Python specific additions to the QgsCurvePolygon API