From 86e8da74cb0bbdef40fc8aae9da55d70304f0429 Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Tue, 23 Aug 2016 13:25:58 +0200 Subject: [PATCH] Return enum instead of int from QgsGeometry operations --- python/core/geometry/qgsgeometry.sip | 114 +++++++------ python/core/geometry/qgsgeometryengine.sip | 23 ++- python/core/qgsvectorlayereditutils.sip | 59 ++++--- src/core/geometry/qgsgeometry.cpp | 122 ++++++++++---- src/core/geometry/qgsgeometry.h | 179 +++++++++++--------- src/core/geometry/qgsgeometryeditutils.cpp | 66 ++++---- src/core/geometry/qgsgeometryeditutils.h | 28 ++-- src/core/geometry/qgsgeometryengine.h | 29 +++- src/core/geometry/qgsgeos.cpp | 99 ++++++----- src/core/geometry/qgsgeos.h | 27 +-- src/core/qgsvectorlayereditutils.cpp | 183 +++++++++++---------- src/core/qgsvectorlayereditutils.h | 97 ++++------- tests/src/python/test_qgsgeometry.py | 38 ++--- 13 files changed, 593 insertions(+), 471 deletions(-) diff --git a/python/core/geometry/qgsgeometry.sip b/python/core/geometry/qgsgeometry.sip index df1acb3c6b4..11159287833 100644 --- a/python/core/geometry/qgsgeometry.sip +++ b/python/core/geometry/qgsgeometry.sip @@ -44,6 +44,23 @@ class QgsGeometry #include "qgsgeometry.h" %End public: + + enum OperationResult + { + Success, + NothingHappened, + InvalidBaseGeometry, + InvalidInput, + GeometryEngineError, + AddPartSelectedGeometryNotFound, + AddPartNotMultiGeometry, + AddRingNotClosed, + AddRingNotValid, + AddRingCrossesExistingRings, + AddRingNotInExistingFeature, + SplitCannotSplitPoint, + }; + QgsGeometry(); %Docstring Constructor @@ -380,63 +397,58 @@ Returns true if WKB of the geometry is of WKBMulti* type :rtype: float %End - int addRing( const QList &ring ); + OperationResult addRing( const QList &ring ); %Docstring Adds a new ring to this geometry. This makes only sense for polygon and multipolygons. -:return: 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed, -3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring* - :rtype: int + \param ring The ring to be added + :return: OperationResult a result code: success or reason of failure + :rtype: OperationResult %End - int addRing( QgsCurve *ring /Transfer/ ); + OperationResult addRing( QgsCurve *ring /Transfer/ ); %Docstring Adds a new ring to this geometry. This makes only sense for polygon and multipolygons. -:return: 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed, -3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring* - :rtype: int + \param ring The ring to be added + :return: OperationResult a result code: success or reason of failure + :rtype: OperationResult %End - int addPart( const QList &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) /PyName=addPoints/; + OperationResult addPart( const QList &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) /PyName=addPoints/; %Docstring Adds a new part to a the geometry. \param points points describing part to add \param geomType default geometry type to create if no existing geometry - :return: 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring - not disjoint with existing polygons of the feature - :rtype: int + :return: OperationResult a result code: success or reason of failure + :rtype: OperationResult %End - int addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) /PyName=addPointsV2/; + OperationResult addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) /PyName=addPointsV2/; %Docstring Adds a new part to a the geometry. \param points points describing part to add \param geomType default geometry type to create if no existing geometry - :return: 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring - not disjoint with existing polygons of the feature - :rtype: int + :return: OperationResult a result code: success or reason of failure + :rtype: OperationResult %End - int addPart( QgsAbstractGeometry *part /Transfer/, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ); + OperationResult addPart( QgsAbstractGeometry *part /Transfer/, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ); %Docstring Adds a new part to this geometry. \param part part to add (ownership is transferred) \param geomType default geometry type to create if no existing geometry - :return: 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring - not disjoint with existing polygons of the feature - :rtype: int + :return: OperationResult a result code: success or reason of failure + :rtype: OperationResult %End - int addPart( const QgsGeometry &newPart ) /PyName=addPartGeometry/; + OperationResult addPart( const QgsGeometry &newPart ) /PyName=addPartGeometry/; %Docstring Adds a new island polygon to a multipolygon feature -:return: 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring -not disjoint with existing polygons of the feature + :return: OperationResult a result code: success or reason of failure .. note:: - available in Python bindings as addPartGeometry -.. versionadded:: 2.2 - :rtype: int + available in python bindings as addPartGeometry + :rtype: OperationResult %End QgsGeometry removeInteriorRings( double minimumAllowedArea = -1 ) const; @@ -448,52 +460,52 @@ not disjoint with existing polygons of the feature :rtype: QgsGeometry %End - int translate( double dx, double dy ); + OperationResult translate( double dx, double dy ); %Docstring Translate this geometry by dx, dy -:return: 0 in case of success* - :rtype: int + :return: OperationResult a result code: success or reason of failure + :rtype: OperationResult %End - int transform( const QgsCoordinateTransform &ct ); + OperationResult transform( const QgsCoordinateTransform &ct ); %Docstring Transform this geometry as described by CoordinateTransform ct -:return: 0 in case of success* - :rtype: int + :return: OperationResult a result code: success or reason of failure + :rtype: OperationResult %End - int transform( const QTransform &ct ); + OperationResult transform( const QTransform &ct ); %Docstring Transform this geometry as described by QTransform ct -.. versionadded:: 2.8 -:return: 0 in case of success* - :rtype: int + :return: OperationResult a result code: success or reason of failure + :rtype: OperationResult %End - int rotate( double rotation, const QgsPointXY ¢er ); + OperationResult rotate( double rotation, const QgsPointXY ¢er ); %Docstring Rotate this geometry around the Z axis -.. versionadded:: 2.8 -\param rotation clockwise rotation in degrees -\param center rotation center -:return: 0 in case of success* - :rtype: int + \param rotation clockwise rotation in degrees + \param center rotation center + :return: OperationResult a result code: success or reason of failure + :rtype: OperationResult %End - int splitGeometry( const QList &splitLine, - QList &newGeometries /Out/, - bool topological, - QList &topologyTestPoints /Out/ ); + OperationResult splitGeometry( const QList &splitLine, QList &newGeometries, bool topological, QList &topologyTestPoints ); %Docstring - :rtype: int + Splits this geometry according to a given line. + \param splitLine the line that splits the geometry + \param[out] newGeometries list of new geometries that have been created with the split + \param topological true if topological editing is enabled + \param[out] topologyTestPoints points that need to be tested for topological completeness in the dataset + :return: OperationResult a result code: success or reason of failure + :rtype: OperationResult %End - int reshapeGeometry( const QgsLineString &reshapeLineString ); + OperationResult reshapeGeometry( const QgsLineString &reshapeLineString ); %Docstring Replaces a part of this geometry with another line - :return: 0 in case of success -.. versionadded:: 1.3 - :rtype: int + :return: OperationResult a result code: success or reason of failure + :rtype: OperationResult %End diff --git a/python/core/geometry/qgsgeometryengine.sip b/python/core/geometry/qgsgeometryengine.sip index ad86e0552b0..11554ba3d31 100644 --- a/python/core/geometry/qgsgeometryengine.sip +++ b/python/core/geometry/qgsgeometryengine.sip @@ -21,6 +21,19 @@ class QgsGeometryEngine #include "qgsgeometryengine.h" %End public: + + enum EngineOperationResult + { + Success, + NothingHappened, + MethodNotImplemented, + EngineError, + NodedGeometryError, + InvalidBaseGeometry, + InvalidInput, + SplitCannotSplitPoint, + }; + virtual ~QgsGeometryEngine(); virtual void geometryChanged() = 0; @@ -230,12 +243,12 @@ class QgsGeometryEngine :rtype: bool %End - virtual int splitGeometry( const QgsLineString &splitLine, - QList &newGeometries, - bool topological, - QgsPointSequence &topologyTestPoints, QString *errorMsg = 0 ) const; + virtual QgsGeometryEngine::EngineOperationResult splitGeometry( const QgsLineString &splitLine, + QList &newGeometries, + bool topological, + QgsPointSequence &topologyTestPoints, QString *errorMsg = 0 ) const; %Docstring - :rtype: int + :rtype: QgsGeometryEngine.EngineOperationResult %End virtual QgsAbstractGeometry *offsetCurve( double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg = 0 ) const = 0 /Factory/; diff --git a/python/core/qgsvectorlayereditutils.sip b/python/core/qgsvectorlayereditutils.sip index 52bd16521cb..b903872eaad 100644 --- a/python/core/qgsvectorlayereditutils.sip +++ b/python/core/qgsvectorlayereditutils.sip @@ -63,29 +63,51 @@ class QgsVectorLayerEditUtils :rtype: QgsVectorLayer.EditResult %End - int addRing( const QList &ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = 0 ); + QgsGeometry::OperationResult addRing( const QList &ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = 0 ); %Docstring - :rtype: int + Adds a ring to polygon/multipolygon features + @param ring ring to add + @param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise + all intersecting features are tested and the ring is added to the first valid feature. + @param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter + @return OperationResult result code: success or reason of failure + :rtype: QgsGeometry.OperationResult %End - int addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = 0 ) /PyName=addCurvedRing/; + QgsGeometry::OperationResult addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = 0 ) /PyName=addCurvedRing/; %Docstring - :rtype: int + Adds a ring to polygon/multipolygon features + @param ring ring to add + @param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise + all intersecting features are tested and the ring is added to the first valid feature. + @param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter + @return OperationResult result code: success or reason of failure +.. note:: + + available in python bindings as addCurvedRing + :rtype: QgsGeometry.OperationResult %End - int addPart( const QList &ring, QgsFeatureId featureId ); + QgsGeometry::OperationResult addPart( const QList &ring, QgsFeatureId featureId ); %Docstring - :rtype: int + Adds a new part polygon to a multipart feature + @returns QgsGeometry.OperationResult a result code: success or reason of failure + :rtype: QgsGeometry.OperationResult %End - int addPart( const QgsPointSequence &ring, QgsFeatureId featureId ); + QgsGeometry::OperationResult addPart( const QgsPointSequence &ring, QgsFeatureId featureId ); %Docstring - :rtype: int + Adds a new part polygon to a multipart feature + @returns QgsGeometry.OperationResult a result code: success or reason of failure +.. note:: + + available in python bindings as addPartV2 + :rtype: QgsGeometry.OperationResult %End - int addPart( QgsCurve *ring, QgsFeatureId featureId ) /PyName=addCurvedPart/; + QgsGeometry::OperationResult addPart( QgsCurve *ring, QgsFeatureId featureId ) /PyName=addCurvedPart/; %Docstring - :rtype: int + :rtype: QgsGeometry.OperationResult %End int translateFeature( QgsFeatureId featureId, double dx, double dy ); @@ -98,14 +120,14 @@ class QgsVectorLayerEditUtils :rtype: int %End - int splitParts( const QList &splitLine, bool topologicalEditing = false ); + QgsGeometry::OperationResult splitParts( const QList &splitLine, bool topologicalEditing = false ); %Docstring - :rtype: int + :rtype: QgsGeometry.OperationResult %End - int splitFeatures( const QList &splitLine, bool topologicalEditing = false ); + QgsGeometry::OperationResult splitFeatures( const QList &splitLine, bool topologicalEditing = false ); %Docstring - :rtype: int + :rtype: QgsGeometry.OperationResult %End int addTopologicalPoints( const QgsGeometry &geom ); @@ -130,15 +152,6 @@ class QgsVectorLayerEditUtils :rtype: int %End - protected: - - int boundingBoxFromPointList( const QList &list, double &xmin, double &ymin, double &xmax, double &ymax ) const; -%Docstring - Little helper function that gives bounding box from a list of points. -:return: 0 in case of success * - :rtype: int -%End - }; /************************************************************************ diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp index d1a710d3660..5c7d61e7ae9 100644 --- a/src/core/geometry/qgsgeometry.cpp +++ b/src/core/geometry/qgsgeometry.cpp @@ -626,7 +626,7 @@ double QgsGeometry::closestSegmentWithContext( return sqrDist; } -int QgsGeometry::addRing( const QList &ring ) +QgsGeometry::OperationResult QgsGeometry::addRing( const QList &ring ) { detach( true ); @@ -634,12 +634,12 @@ int QgsGeometry::addRing( const QList &ring ) return addRing( ringLine ); } -int QgsGeometry::addRing( QgsCurve *ring ) +QgsGeometry::OperationResult QgsGeometry::addRing( QgsCurve *ring ) { if ( !d->geometry ) { delete ring; - return 1; + return InvalidInput; } detach( true ); @@ -647,14 +647,14 @@ int QgsGeometry::addRing( QgsCurve *ring ) return QgsGeometryEditUtils::addRing( d->geometry, ring ); } -int QgsGeometry::addPart( const QList &points, QgsWkbTypes::GeometryType geomType ) +QgsGeometry::OperationResult QgsGeometry::addPart( const QList &points, QgsWkbTypes::GeometryType geomType ) { QgsPointSequence l; convertPointList( points, l ); return addPart( l, geomType ); } -int QgsGeometry::addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType ) +QgsGeometry::OperationResult QgsGeometry::addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType ) { QgsAbstractGeometry *partGeom = nullptr; if ( points.size() == 1 ) @@ -670,7 +670,7 @@ int QgsGeometry::addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryT return addPart( partGeom, geomType ); } -int QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType geomType ) +QgsGeometry::OperationResult QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType geomType ) { if ( !d->geometry ) { @@ -687,7 +687,7 @@ int QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType g d->geometry = new QgsMultiPolygonV2(); break; default: - return 1; + return QgsGeometry::AddPartNotMultiGeometry; } } else @@ -699,11 +699,15 @@ int QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType g return QgsGeometryEditUtils::addPart( d->geometry, part ); } -int QgsGeometry::addPart( const QgsGeometry &newPart ) +QgsGeometry::OperationResult QgsGeometry::addPart( const QgsGeometry &newPart ) { - if ( !d->geometry || !newPart.d || !newPart.d->geometry ) + if ( !d->geometry ) { - return 1; + return QgsGeometry::InvalidBaseGeometry; + } + if ( !newPart.d || !newPart.d->geometry ) + { + return QgsGeometry::AddPartNotMultiGeometry; } return addPart( newPart.d->geometry->clone() ); @@ -744,11 +748,15 @@ QgsGeometry QgsGeometry::removeInteriorRings( double minimumRingArea ) const } } -int QgsGeometry::addPart( GEOSGeometry *newPart ) +QgsGeometry::OperationResult QgsGeometry::addPart( GEOSGeometry *newPart ) { - if ( !d->geometry || !newPart ) + if ( !d->geometry ) { - return 1; + return QgsGeometry::InvalidBaseGeometry; + } + if ( !newPart ) + { + return QgsGeometry::AddPartNotMultiGeometry; } detach( true ); @@ -757,24 +765,24 @@ int QgsGeometry::addPart( GEOSGeometry *newPart ) return QgsGeometryEditUtils::addPart( d->geometry, geom ); } -int QgsGeometry::translate( double dx, double dy ) +QgsGeometry::OperationResult QgsGeometry::translate( double dx, double dy ) { if ( !d->geometry ) { - return 1; + return QgsGeometry::InvalidBaseGeometry; } detach( true ); d->geometry->transform( QTransform::fromTranslate( dx, dy ) ); - return 0; + return QgsGeometry::Success; } -int QgsGeometry::rotate( double rotation, const QgsPointXY ¢er ) +QgsGeometry::OperationResult QgsGeometry::rotate( double rotation, const QgsPointXY ¢er ) { if ( !d->geometry ) { - return 1; + return QgsGeometry::InvalidBaseGeometry; } detach( true ); @@ -783,14 +791,14 @@ int QgsGeometry::rotate( double rotation, const QgsPointXY ¢er ) t.rotate( -rotation ); t.translate( -center.x(), -center.y() ); d->geometry->transform( t ); - return 0; + return QgsGeometry::Success; } -int QgsGeometry::splitGeometry( const QList &splitLine, QList &newGeometries, bool topological, QList &topologyTestPoints ) +QgsGeometry::OperationResult QgsGeometry::splitGeometry( const QList &splitLine, QList &newGeometries, bool topological, QList &topologyTestPoints ) { if ( !d->geometry ) { - return 0; + return InvalidBaseGeometry; } QList newGeoms; @@ -798,9 +806,9 @@ int QgsGeometry::splitGeometry( const QList &splitLine, QListgeometry ); - int result = geos.splitGeometry( splitLineString, newGeoms, topological, tp ); + QgsGeometryEngine::EngineOperationResult result = geos.splitGeometry( splitLineString, newGeoms, topological, tp ); - if ( result == 0 ) + if ( result == QgsGeometryEngine::Success ) { detach( false ); d->geometry = newGeoms.at( 0 ); @@ -813,27 +821,69 @@ int QgsGeometry::splitGeometry( const QList &splitLine, QListgeometry ) { - return 0; + return InvalidBaseGeometry; } QgsGeos geos( d->geometry ); - int errorCode = 0; + QgsGeometryEngine::EngineOperationResult errorCode = QgsGeometryEngine::Success; QgsAbstractGeometry *geom = geos.reshapeGeometry( reshapeLineString, &errorCode ); - if ( errorCode == 0 && geom ) + if ( errorCode == QgsGeometryEngine::Success && geom ) { detach( false ); delete d->geometry; d->geometry = geom; - return 0; + return Success; } - return errorCode; + + switch ( errorCode ) + { + case QgsGeometryEngine::Success: + return Success; + case QgsGeometryEngine::MethodNotImplemented: + case QgsGeometryEngine::EngineError: + case QgsGeometryEngine::NodedGeometryError: + return GeometryEngineError; + case QgsGeometryEngine::InvalidBaseGeometry: + return InvalidBaseGeometry; + case QgsGeometryEngine::InvalidInput: + return InvalidInput; + case QgsGeometryEngine::SplitCannotSplitPoint: // should not happen + return GeometryEngineError; + case QgsGeometryEngine::NothingHappened: + return NothingHappened; + } + + // should not be reached + return GeometryEngineError; } int QgsGeometry::makeDifferenceInPlace( const QgsGeometry &other ) @@ -2091,28 +2141,28 @@ bool QgsGeometry::requiresConversionToStraightSegments() const return d->geometry->hasCurvedSegments(); } -int QgsGeometry::transform( const QgsCoordinateTransform &ct ) +QgsGeometry::OperationResult QgsGeometry::transform( const QgsCoordinateTransform &ct ) { if ( !d->geometry ) { - return 1; + return QgsGeometry::InvalidBaseGeometry; } detach(); d->geometry->transform( ct ); - return 0; + return QgsGeometry::Success; } -int QgsGeometry::transform( const QTransform &ct ) +QgsGeometry::OperationResult QgsGeometry::transform( const QTransform &ct ) { if ( !d->geometry ) { - return 1; + return QgsGeometry::InvalidBaseGeometry; } detach(); d->geometry->transform( ct ); - return 0; + return QgsGeometry::Success; } void QgsGeometry::mapToPixel( const QgsMapToPixel &mtp ) diff --git a/src/core/geometry/qgsgeometry.h b/src/core/geometry/qgsgeometry.h index 6e52137b6c2..70bbb5bd678 100644 --- a/src/core/geometry/qgsgeometry.h +++ b/src/core/geometry/qgsgeometry.h @@ -96,6 +96,30 @@ struct QgsGeometryPrivate; class CORE_EXPORT QgsGeometry { public: + + /** + * Success or failure of a geometry operation. + * This gived details about cause of failure. + */ + enum OperationResult + { + Success = 0, //!< Operation succeeded + NothingHappened, //!< Nothing happened, without any error + InvalidBaseGeometry, //!< The base geometry on which the operation is done is invalid or empty + InvalidInput, //!< The input geometry (ring, part, split line, etc.) has not the correct geometry type + GeometryEngineError, //!< Geometry engine misses a method implemented or an error occured in the geometry engine + /* Add part issues */ + AddPartSelectedGeometryNotFound, //!< The selected geometry cannot be found + AddPartNotMultiGeometry, //!< The source geometry is not multi + /* Add ring issues*/ + AddRingNotClosed, //!< The imput ring is not closed + AddRingNotValid, //!< The input ring is not valid + AddRingCrossesExistingRings, //!< The input ring crosses existing rings (it is not disjoint) + AddRingNotInExistingFeature, //!< The input ring doesn't have any existing ring to fit into + /* Split features */ + SplitCannotSplitPoint, //!< Cannot split points + }; + //! Constructor QgsGeometry(); @@ -390,62 +414,58 @@ class CORE_EXPORT QgsGeometry double closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint SIP_OUT, int &afterVertex SIP_OUT ) const; #endif - /** Adds a new ring to this geometry. This makes only sense for polygon and multipolygons. - \returns 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed, - 3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*/ - int addRing( const QList &ring ); - // TODO QGIS 3.0 returns an enum instead of a magic constant + /** + * Adds a new ring to this geometry. This makes only sense for polygon and multipolygons. + * \param ring The ring to be added + * \returns OperationResult a result code: success or reason of failure + */ + OperationResult addRing( const QList &ring ); - /** Adds a new ring to this geometry. This makes only sense for polygon and multipolygons. - \returns 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed, - 3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*/ - int addRing( QgsCurve *ring SIP_TRANSFER ); - // TODO QGIS 3.0 returns an enum instead of a magic constant + /** + * Adds a new ring to this geometry. This makes only sense for polygon and multipolygons. + * \param ring The ring to be added + * \returns OperationResult a result code: success or reason of failure + */ + OperationResult addRing( QgsCurve *ring SIP_TRANSFER ); - /** Adds a new part to a the geometry. + /** + * Adds a new part to a the geometry. * \param points points describing part to add * \param geomType default geometry type to create if no existing geometry - * \returns 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring - * not disjoint with existing polygons of the feature + * \returns OperationResult a result code: success or reason of failure */ - int addPart( const QList &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) SIP_PYNAME( addPoints ); - // TODO QGIS 3.0 returns an enum instead of a magic constant + OperationResult addPart( const QList &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) SIP_PYNAME( addPoints ); - /** Adds a new part to a the geometry. + /** + * Adds a new part to a the geometry. * \param points points describing part to add * \param geomType default geometry type to create if no existing geometry - * \returns 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring - * not disjoint with existing polygons of the feature + * \returns OperationResult a result code: success or reason of failure */ - int addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) SIP_PYNAME( addPointsV2 ); - // TODO QGIS 3.0 returns an enum instead of a magic constant + OperationResult addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) SIP_PYNAME( addPointsV2 ); - /** Adds a new part to this geometry. + /** + * Adds a new part to this geometry. * \param part part to add (ownership is transferred) * \param geomType default geometry type to create if no existing geometry - * \returns 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring - * not disjoint with existing polygons of the feature + * \returns OperationResult a result code: success or reason of failure */ - int addPart( QgsAbstractGeometry *part SIP_TRANSFER, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ); - // TODO QGIS 3.0 returns an enum instead of a magic constant + OperationResult addPart( QgsAbstractGeometry *part SIP_TRANSFER, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ); - /** Adds a new island polygon to a multipolygon feature + /** + * Adds a new island polygon to a multipolygon feature * \param newPart part to add. Ownership is NOT transferred. - * \returns 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring - * not disjoint with existing polygons of the feature - * \note not available in Python bindings + * \returns OperationResult a result code: success or reason of failure + * \note not available in python bindings */ - int addPart( GEOSGeometry *newPart ) SIP_SKIP; - // TODO QGIS 3.0 returns an enum instead of a magic constant + OperationResult addPart( GEOSGeometry *newPart ) SIP_SKIP; - /** Adds a new island polygon to a multipolygon feature - \returns 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring - not disjoint with existing polygons of the feature - \note available in Python bindings as addPartGeometry - \since QGIS 2.2 + /** + * Adds a new island polygon to a multipolygon feature + * \returns OperationResult a result code: success or reason of failure + * \note available in python bindings as addPartGeometry */ - int addPart( const QgsGeometry &newPart ) SIP_PYNAME( addPartGeometry ); - // TODO QGIS 3.0 returns an enum instead of a magic constant + OperationResult addPart( const QgsGeometry &newPart ) SIP_PYNAME( addPartGeometry ); /** * Removes the interior rings from a (multi)polygon geometry. If the minimumAllowedArea @@ -455,52 +475,57 @@ class CORE_EXPORT QgsGeometry */ QgsGeometry removeInteriorRings( double minimumAllowedArea = -1 ) const; - /** Translate this geometry by dx, dy - \returns 0 in case of success*/ - int translate( double dx, double dy ); - - /** Transform this geometry as described by CoordinateTransform ct - \returns 0 in case of success*/ - int transform( const QgsCoordinateTransform &ct ); - - /** Transform this geometry as described by QTransform ct - \since QGIS 2.8 - \returns 0 in case of success*/ - int transform( const QTransform &ct ); - - /** Rotate this geometry around the Z axis - \since QGIS 2.8 - \param rotation clockwise rotation in degrees - \param center rotation center - \returns 0 in case of success*/ - int rotate( double rotation, const QgsPointXY ¢er ); - - /** Splits this geometry according to a given line. - \param splitLine the line that splits the geometry - \param[out] newGeometries list of new geometries that have been created with the split - \param topological true if topological editing is enabled - \param[out] topologyTestPoints points that need to be tested for topological completeness in the dataset - \returns 0 in case of success, 1 if geometry has not been split, error else*/ - // TODO QGIS 3.0 returns an enum instead of a magic constant - int splitGeometry( const QList &splitLine, - QList &newGeometries SIP_OUT, - bool topological, - QList &topologyTestPoints SIP_OUT ); - - /** Replaces a part of this geometry with another line - * \returns 0 in case of success - * \since QGIS 1.3 + /** + * Translate this geometry by dx, dy + * \returns OperationResult a result code: success or reason of failure */ - int reshapeGeometry( const QgsLineString &reshapeLineString ); + OperationResult translate( double dx, double dy ); - /** Changes this geometry such that it does not intersect the other geometry + /** + * Transform this geometry as described by CoordinateTransform ct + * \returns OperationResult a result code: success or reason of failure + */ + OperationResult transform( const QgsCoordinateTransform &ct ); + + /** + * Transform this geometry as described by QTransform ct + * \returns OperationResult a result code: success or reason of failure + */ + OperationResult transform( const QTransform &ct ); + + /** + * Rotate this geometry around the Z axis + * \param rotation clockwise rotation in degrees + * \param center rotation center + * \returns OperationResult a result code: success or reason of failure + */ + OperationResult rotate( double rotation, const QgsPointXY ¢er ); + + /** + * Splits this geometry according to a given line. + * \param splitLine the line that splits the geometry + * \param[out] newGeometries list of new geometries that have been created with the split + * \param topological true if topological editing is enabled + * \param[out] topologyTestPoints points that need to be tested for topological completeness in the dataset + * \returns OperationResult a result code: success or reason of failure + */ + OperationResult splitGeometry( const QList &splitLine, QList &newGeometries, bool topological, QList &topologyTestPoints ); + + /** + * Replaces a part of this geometry with another line + * \returns OperationResult a result code: success or reason of failure + */ + OperationResult reshapeGeometry( const QgsLineString &reshapeLineString ); + + /** + * Changes this geometry such that it does not intersect the other geometry * \param other geometry that should not be intersect - * \returns 0 in case of success * \note Not available in Python */ int makeDifferenceInPlace( const QgsGeometry &other ) SIP_SKIP; - /** Returns the geometry formed by modifying this geometry such that it does not + /** + * Returns the geometry formed by modifying this geometry such that it does not * intersect the other geometry. * \param other geometry that should not be intersect * \returns difference geometry, or empty geometry if difference could not be calculated diff --git a/src/core/geometry/qgsgeometryeditutils.cpp b/src/core/geometry/qgsgeometryeditutils.cpp index 8f65a4bcd67..f939d03e4f7 100644 --- a/src/core/geometry/qgsgeometryeditutils.cpp +++ b/src/core/geometry/qgsgeometryeditutils.cpp @@ -26,11 +26,12 @@ email : marco.hugentobler at sourcepole dot com #include "qgsvectorlayer.h" #include -int QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *ring ) +QgsGeometry::OperationResult QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *r ) { + std::unique_ptr ring( r ); if ( !ring ) { - return 1; + return QgsGeometry::InvalidInput; } QList< QgsCurvePolygon * > polygonList; @@ -50,23 +51,20 @@ int QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *ring ) } else { - delete ring; - return 1; //not polygon / multipolygon; + return QgsGeometry::InvalidInput; //not polygon / multipolygon; } //ring must be closed if ( !ring->isClosed() ) { - delete ring; - return 2; + return QgsGeometry::AddRingNotClosed; } else if ( !ring->isRing() ) { - delete ring; - return 3; + return QgsGeometry::AddRingNotValid; } - std::unique_ptr ringGeom( QgsGeometry::createGeometryEngine( ring ) ); + std::unique_ptr ringGeom( QgsGeometry::createGeometryEngine( ring.get() ) ); ringGeom->prepareGeometry(); //for each polygon, test if inside outer ring and no intersection with other interior ring @@ -81,8 +79,7 @@ int QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *ring ) { if ( !ringGeom->disjoint( ( *polyIter )->interiorRing( i ) ) ) { - delete ring; - return 4; + return QgsGeometry::AddRingCrossesExistingRings; } } @@ -92,59 +89,62 @@ int QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *ring ) if ( QgsWkbTypes::hasM( geom->wkbType() ) ) ring->addMValue( 0 ); - ( *polyIter )->addInteriorRing( ring ); - return 0; //success + ( *polyIter )->addInteriorRing( ring.release() ); + return QgsGeometry::Success; //success } } - delete ring; - return 5; //not contained in any outer ring + return QgsGeometry::AddRingNotInExistingFeature; //not contained in any outer ring } -int QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, QgsAbstractGeometry *part ) +QgsGeometry::OperationResult QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, QgsAbstractGeometry *p ) { + std::unique_ptr part( p ); if ( !geom ) { - return 1; + return QgsGeometry::InvalidBaseGeometry; } if ( !part ) { - return 2; + return QgsGeometry::InvalidInput; } //multitype? QgsGeometryCollection *geomCollection = qgsgeometry_cast( geom ); if ( !geomCollection ) { - return 1; + return QgsGeometry::AddPartNotMultiGeometry; } bool added = false; if ( QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiSurface || QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiPolygon ) { - QgsCurve *curve = qgsgeometry_cast( part ); + std::unique_ptr curve( qgsgeometry_cast( part.get() ) ); + if ( curve ) + part.release(); + if ( curve && curve->isClosed() && curve->numPoints() >= 4 ) { - QgsCurvePolygon *poly = nullptr; + std::unique_ptr poly; if ( QgsWkbTypes::flatType( curve->wkbType() ) == QgsWkbTypes::LineString ) { - poly = new QgsPolygonV2(); + poly.reset( new QgsPolygonV2() ); } else { - poly = new QgsCurvePolygon(); + poly.reset( new QgsCurvePolygon() ); } - poly->setExteriorRing( curve ); - added = geomCollection->addGeometry( poly ); + poly->setExteriorRing( curve.release() ); + added = geomCollection->addGeometry( poly.release() ); } else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::Polygon ) { - added = geomCollection->addGeometry( part ); + added = geomCollection->addGeometry( part.release() ); } else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiPolygon ) { - QgsGeometryCollection *parts = static_cast( part ); + std::unique_ptr parts( static_cast( part.release() ) ); int i; int n = geomCollection->numGeometries(); @@ -156,23 +156,19 @@ int QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, QgsAbstractGeometr { while ( geomCollection->numGeometries() > n ) geomCollection->removeGeometry( n ); - delete part; - return 2; + return QgsGeometry::InvalidInput; } - - delete part; } else { - delete part; - return 2; + return QgsGeometry::InvalidInput; } } else { - added = geomCollection->addGeometry( part ); + added = geomCollection->addGeometry( part.release() ); } - return added ? 0 : 2; + return added ? QgsGeometry::Success : QgsGeometry::InvalidInput; } bool QgsGeometryEditUtils::deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum ) diff --git a/src/core/geometry/qgsgeometryeditutils.h b/src/core/geometry/qgsgeometryeditutils.h index 8c3905da167..359f6c6e153 100644 --- a/src/core/geometry/qgsgeometryeditutils.h +++ b/src/core/geometry/qgsgeometryeditutils.h @@ -24,6 +24,7 @@ class QgsVectorLayer; #define SIP_NO_FILE #include "qgsfeature.h" +#include "qgsgeometry.h" #include /** \ingroup core @@ -36,19 +37,24 @@ class QgsGeometryEditUtils { public: - /** Adds interior ring (taking ownership). - \returns 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed, - 3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*/ - // TODO QGIS 3.0 returns an enum instead of a magic constant - static int addRing( QgsAbstractGeometry *geom, QgsCurve *ring ); + /** + * Add an interior \a ring to a \a geometry. + * Ownership of the \a ring is transferred. + * \returns 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed, + * 3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring + */ + static QgsGeometry::OperationResult addRing( QgsAbstractGeometry *geometry, QgsCurve *ring SIP_TRANSFER ); - /** Adds part to multi type geometry (taking ownership) - \returns 0 in case of success, 1 if not a multigeometry, 2 if part is not a valid geometry, 3 if new polygon ring - not disjoint with existing polygons of the feature*/ - // TODO QGIS 3.0 returns an enum instead of a magic constant - static int addPart( QgsAbstractGeometry *geom, QgsAbstractGeometry *part ); + /** + * Add a \a part to multi type \a geometry. + * Ownership of the \a part is transferred. + * \returns 0 in case of success, 1 if not a multigeometry, 2 if part is not a valid geometry, 3 if new polygon ring + * not disjoint with existing polygons of the feature + */ + static QgsGeometry::OperationResult addPart( QgsAbstractGeometry *geometry, QgsAbstractGeometry *part SIP_TRANSFER ); - /** Deletes a ring from a geometry. + /** + * Deletes a ring from a geometry. * \returns true if delete was successful */ static bool deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum = 0 ); diff --git a/src/core/geometry/qgsgeometryengine.h b/src/core/geometry/qgsgeometryengine.h index e2f5927bf45..fca801dac02 100644 --- a/src/core/geometry/qgsgeometryengine.h +++ b/src/core/geometry/qgsgeometryengine.h @@ -18,6 +18,7 @@ email : marco.hugentobler at sourcepole dot com #include "qgis_core.h" #include "qgslinestring.h" +#include "qgsgeometry.h" #include @@ -31,6 +32,24 @@ class QgsAbstractGeometry; class CORE_EXPORT QgsGeometryEngine { public: + + /** + * Success or failure of a geometry operation. + * This gived details about cause of failure. + */ + enum EngineOperationResult + { + Success = 0, //!< Operation succeeded + NothingHappened, //!< Nothing happened, without any error + MethodNotImplemented, //!< Method not implemented in geometry engine + EngineError, //!< Error occured in the geometry engine + NodedGeometryError, //!< Error occured while creating a noded geometry + InvalidBaseGeometry, //!< The geometry on which the operation occurs is not valid + InvalidInput, //!< The input is not valid + /* split */ + SplitCannotSplitPoint, //!< Points cannot be split + }; + virtual ~QgsGeometryEngine() = default; virtual void geometryChanged() = 0; @@ -190,17 +209,17 @@ class CORE_EXPORT QgsGeometryEngine */ virtual bool isSimple( QString *errorMsg = nullptr ) const = 0; - virtual int splitGeometry( const QgsLineString &splitLine, - QList &newGeometries, - bool topological, - QgsPointSequence &topologyTestPoints, QString *errorMsg = nullptr ) const + virtual QgsGeometryEngine::EngineOperationResult splitGeometry( const QgsLineString &splitLine, + QList &newGeometries, + bool topological, + QgsPointSequence &topologyTestPoints, QString *errorMsg = nullptr ) const { Q_UNUSED( splitLine ); Q_UNUSED( newGeometries ); Q_UNUSED( topological ); Q_UNUSED( topologyTestPoints ); Q_UNUSED( errorMsg ); - return 2; + return MethodNotImplemented; } virtual QgsAbstractGeometry *offsetCurve( double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg = nullptr ) const = 0 SIP_FACTORY; diff --git a/src/core/geometry/qgsgeos.cpp b/src/core/geometry/qgsgeos.cpp index 19eb50cc6e9..f639422ee82 100644 --- a/src/core/geometry/qgsgeos.cpp +++ b/src/core/geometry/qgsgeos.cpp @@ -25,7 +25,6 @@ email : marco.hugentobler at sourcepole dot com #include "qgsmultipolygon.h" #include "qgslogger.h" #include "qgspolygon.h" -#include "qgsgeometry.h" #include #include #include @@ -519,32 +518,32 @@ double QgsGeos::length( QString *errorMsg ) const return length; } -int QgsGeos::splitGeometry( const QgsLineString &splitLine, - QList &newGeometries, - bool topological, - QgsPointSequence &topologyTestPoints, - QString *errorMsg ) const +QgsGeometryEngine::EngineOperationResult QgsGeos::splitGeometry( const QgsLineString &splitLine, + QList &newGeometries, + bool topological, + QgsPointSequence &topologyTestPoints, + QString *errorMsg ) const { - int returnCode = 0; - if ( !mGeometry || !mGeos ) + EngineOperationResult returnCode = Success; + if ( !mGeos || !mGeometry ) { - return 1; + return InvalidBaseGeometry; } //return if this type is point/multipoint if ( mGeometry->dimension() == 0 ) { - return 1; //cannot split points + return SplitCannotSplitPoint; //cannot split points } if ( !GEOSisValid_r( geosinit.ctxt, mGeos ) ) - return 7; + return InvalidBaseGeometry; //make sure splitLine is valid if ( ( mGeometry->dimension() == 1 && splitLine.numPoints() < 1 ) || ( mGeometry->dimension() == 2 && splitLine.numPoints() < 2 ) ) - return 1; + return InvalidInput; newGeometries.clear(); GEOSGeometry *splitLineGeos = nullptr; @@ -561,20 +560,22 @@ int QgsGeos::splitGeometry( const QgsLineString &splitLine, } else { - return 1; + return InvalidInput; } if ( !GEOSisValid_r( geosinit.ctxt, splitLineGeos ) || !GEOSisSimple_r( geosinit.ctxt, splitLineGeos ) ) { GEOSGeom_destroy_r( geosinit.ctxt, splitLineGeos ); - return 1; + return InvalidInput; } if ( topological ) { //find out candidate points for topological corrections - if ( topologicalTestPointsSplit( splitLineGeos, topologyTestPoints ) != 0 ) - return 1; + if ( !topologicalTestPointsSplit( splitLineGeos, topologyTestPoints ) ) + { + return InvalidInput; // TODO: is it really an invalid input? + } } //call split function depending on geometry type @@ -590,17 +591,17 @@ int QgsGeos::splitGeometry( const QgsLineString &splitLine, } else { - return 1; + return InvalidInput; } } - CATCH_GEOS_WITH_ERRMSG( 2 ) + CATCH_GEOS_WITH_ERRMSG( EngineError ) return returnCode; } -int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPointSequence &testPoints, QString *errorMsg ) const +bool QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPointSequence &testPoints, QString *errorMsg ) const { //Find out the intersection points between splitLineGeos and this geometry. //These points need to be tested for topological correctness by the calling function @@ -608,7 +609,7 @@ int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPoint if ( !mGeos ) { - return 1; + return false; } try @@ -616,7 +617,7 @@ int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPoint testPoints.clear(); GEOSGeometry *intersectionGeom = GEOSIntersection_r( geosinit.ctxt, mGeos, splitLine ); if ( !intersectionGeom ) - return 1; + return false; bool simple = false; int nIntersectGeoms = 1; @@ -656,7 +657,7 @@ int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPoint } CATCH_GEOS_WITH_ERRMSG( 1 ) - return 0; + return true; } GEOSGeometry *QgsGeos::linePointDifference( GEOSGeometry *GEOSsplitPoint ) const @@ -725,22 +726,22 @@ GEOSGeometry *QgsGeos::linePointDifference( GEOSGeometry *GEOSsplitPoint ) const return asGeos( &lines, mPrecision ); } -int QgsGeos::splitLinearGeometry( GEOSGeometry *splitLine, QList &newGeometries ) const +QgsGeometryEngine::EngineOperationResult QgsGeos::splitLinearGeometry( GEOSGeometry *splitLine, QList &newGeometries ) const { if ( !splitLine ) - return 2; + return InvalidInput; if ( !mGeos ) - return 5; + return InvalidBaseGeometry; //first test if linestring intersects geometry. If not, return straight away if ( !GEOSIntersects_r( geosinit.ctxt, splitLine, mGeos ) ) - return 1; + return NothingHappened; //check that split line has no linear intersection int linearIntersect = GEOSRelatePattern_r( geosinit.ctxt, mGeos, splitLine, "1********" ); if ( linearIntersect > 0 ) - return 3; + return InvalidInput; int splitGeomType = GEOSGeomTypeId_r( geosinit.ctxt, splitLine ); @@ -778,25 +779,25 @@ int QgsGeos::splitLinearGeometry( GEOSGeometry *splitLine, QList &newGeometries ) const +QgsGeometryEngine::EngineOperationResult QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QList &newGeometries ) const { if ( !splitLine ) - return 2; + return InvalidInput; if ( !mGeos ) - return 5; + return InvalidBaseGeometry; //first test if linestring intersects geometry. If not, return straight away if ( !GEOSIntersects_r( geosinit.ctxt, splitLine, mGeos ) ) - return 1; + return NothingHappened; //first union all the polygon rings together (to get them noded, see JTS developer guide) GEOSGeometry *nodedGeometry = nodeGeometries( splitLine, mGeos ); if ( !nodedGeometry ) - return 2; //an error occurred during noding + return NodedGeometryError; //an error occurred during noding GEOSGeometry *polygons = GEOSPolygonize_r( geosinit.ctxt, &nodedGeometry, 1 ); if ( !polygons || numberOfGeometries( polygons ) == 0 ) @@ -806,7 +807,7 @@ int QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QListdimension() == 0 ) + if ( !mGeos || mGeometry->dimension() == 0 ) { - if ( errorCode ) { *errorCode = 1; } + if ( errorCode ) { *errorCode = InvalidBaseGeometry; } + return nullptr; + } + + if ( reshapeWithLine.numPoints() < 2 ) + { + if ( errorCode ) { *errorCode = InvalidInput; } return nullptr; } @@ -1906,7 +1913,7 @@ QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithL int numGeoms = GEOSGetNumGeometries_r( geosinit.ctxt, mGeos ); if ( numGeoms == -1 ) { - if ( errorCode ) { *errorCode = 1; } + if ( errorCode ) { *errorCode = InvalidBaseGeometry; } GEOSGeom_destroy_r( geosinit.ctxt, reshapeLineGeos ); return nullptr; } @@ -1930,7 +1937,8 @@ QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithL reshapedGeometry = reshapePolygon( mGeos, reshapeLineGeos, mPrecision ); } - if ( errorCode ) { *errorCode = 0; } + if ( errorCode ) + *errorCode = Success; QgsAbstractGeometry *reshapeResult = fromGeos( reshapedGeometry ); GEOSGeom_destroy_r( geosinit.ctxt, reshapedGeometry ); GEOSGeom_destroy_r( geosinit.ctxt, reshapeLineGeos ); @@ -1978,13 +1986,14 @@ QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithL delete[] newGeoms; if ( !newMultiGeom ) { - if ( errorCode ) { *errorCode = 3; } + if ( errorCode ) { *errorCode = EngineError; } return nullptr; } if ( reshapeTookPlace ) { - if ( errorCode ) { *errorCode = 0; } + if ( errorCode ) + *errorCode = Success; QgsAbstractGeometry *reshapedMultiGeom = fromGeos( newMultiGeom ); GEOSGeom_destroy_r( geosinit.ctxt, newMultiGeom ); return reshapedMultiGeom; @@ -1992,7 +2001,7 @@ QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithL else { GEOSGeom_destroy_r( geosinit.ctxt, newMultiGeom ); - if ( errorCode ) { *errorCode = 1; } + if ( errorCode ) { *errorCode = NothingHappened; } return nullptr; } } diff --git a/src/core/geometry/qgsgeos.h b/src/core/geometry/qgsgeos.h index a7bfb934f65..cf07861c02c 100644 --- a/src/core/geometry/qgsgeos.h +++ b/src/core/geometry/qgsgeos.h @@ -20,6 +20,7 @@ email : marco.hugentobler at sourcepole dot com #include "qgis_core.h" #include "qgsgeometryengine.h" +#include "qgsgeometry.h" #include class QgsLineString; @@ -106,11 +107,11 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine \param[out] topologyTestPoints points that need to be tested for topological completeness in the dataset \param[out] errorMsg error messages emitted, if any \returns 0 in case of success, 1 if geometry has not been split, error else*/ - int splitGeometry( const QgsLineString &splitLine, - QList &newGeometries, - bool topological, - QgsPointSequence &topologyTestPoints, - QString *errorMsg = nullptr ) const override; + EngineOperationResult splitGeometry( const QgsLineString &splitLine, + QList &newGeometries, + bool topological, + QgsPointSequence &topologyTestPoints, + QString *errorMsg = nullptr ) const override; QgsAbstractGeometry *offsetCurve( double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg = nullptr ) const override; @@ -131,8 +132,14 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine int joinStyle, double miterLimit, QString *errorMsg = nullptr ) const; - - QgsAbstractGeometry *reshapeGeometry( const QgsLineString &reshapeWithLine, int *errorCode, QString *errorMsg = nullptr ) const; + /** + * Reshapes the geometry using a line + * @param reshapeWithLine the line used to reshape lines or polygons + * @param errorCode if specified, provides result of operation (success or reason of failure) + * @param errorMsg if specified, provides more details about failure + * @return the reshaped geometry + */ + QgsAbstractGeometry *reshapeGeometry( const QgsLineString &reshapeWithLine, EngineOperationResult *errorCode, QString *errorMsg = nullptr ) const; /** Merges any connected lines in a LineString/MultiLineString geometry and * converts them to single line strings. @@ -261,10 +268,10 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine static GEOSGeometry *createGeosPolygon( const QgsAbstractGeometry *poly, double precision ); //utils for geometry split - int topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPointSequence &testPoints, QString *errorMsg = nullptr ) const; + bool topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPointSequence &testPoints, QString *errorMsg = nullptr ) const; GEOSGeometry *linePointDifference( GEOSGeometry *GEOSsplitPoint ) const; - int splitLinearGeometry( GEOSGeometry *splitLine, QList &newGeometries ) const; - int splitPolygonGeometry( GEOSGeometry *splitLine, QList &newGeometries ) const; + EngineOperationResult splitLinearGeometry( GEOSGeometry *splitLine, QList &newGeometries ) const; + EngineOperationResult splitPolygonGeometry( GEOSGeometry *splitLine, QList &newGeometries ) const; //utils for reshape static GEOSGeometry *reshapeLine( const GEOSGeometry *line, const GEOSGeometry *reshapeLineGeos, double precision ); diff --git a/src/core/qgsvectorlayereditutils.cpp b/src/core/qgsvectorlayereditutils.cpp index 1f7e193043a..45fb6577272 100644 --- a/src/core/qgsvectorlayereditutils.cpp +++ b/src/core/qgsvectorlayereditutils.cpp @@ -29,41 +29,41 @@ QgsVectorLayerEditUtils::QgsVectorLayerEditUtils( QgsVectorLayer *layer ) - : L( layer ) + : mLayer( layer ) { } bool QgsVectorLayerEditUtils::insertVertex( double x, double y, QgsFeatureId atFeatureId, int beforeVertex ) { - if ( !L->isSpatial() ) + if ( !mLayer->isSpatial() ) return false; QgsFeature f; - if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() ) + if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() ) return false; // geometry not found QgsGeometry geometry = f.geometry(); geometry.insertVertex( x, y, beforeVertex ); - L->editBuffer()->changeGeometry( atFeatureId, geometry ); + mLayer->editBuffer()->changeGeometry( atFeatureId, geometry ); return true; } bool QgsVectorLayerEditUtils::insertVertex( const QgsPoint &point, QgsFeatureId atFeatureId, int beforeVertex ) { - if ( !L->isSpatial() ) + if ( !mLayer->isSpatial() ) return false; QgsFeature f; - if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() ) + if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() ) return false; // geometry not found QgsGeometry geometry = f.geometry(); geometry.insertVertex( point, beforeVertex ); - L->editBuffer()->changeGeometry( atFeatureId, geometry ); + mLayer->editBuffer()->changeGeometry( atFeatureId, geometry ); return true; } @@ -75,29 +75,29 @@ bool QgsVectorLayerEditUtils::moveVertex( double x, double y, QgsFeatureId atFea bool QgsVectorLayerEditUtils::moveVertex( const QgsPoint &p, QgsFeatureId atFeatureId, int atVertex ) { - if ( !L->isSpatial() ) + if ( !mLayer->isSpatial() ) return false; QgsFeature f; - if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() ) + if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() ) return false; // geometry not found QgsGeometry geometry = f.geometry(); geometry.moveVertex( p, atVertex ); - L->editBuffer()->changeGeometry( atFeatureId, geometry ); + mLayer->editBuffer()->changeGeometry( atFeatureId, geometry ); return true; } QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId featureId, int vertex ) { - if ( !L->isSpatial() ) + if ( !mLayer->isSpatial() ) return QgsVectorLayer::InvalidLayer; QgsFeature f; - if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() ) + if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() ) return QgsVectorLayer::FetchFeatureFailed; // geometry not found QgsGeometry geometry = f.geometry(); @@ -111,38 +111,38 @@ QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId f geometry.setGeometry( nullptr ); } - L->editBuffer()->changeGeometry( featureId, geometry ); + mLayer->editBuffer()->changeGeometry( featureId, geometry ); return !geometry.isNull() ? QgsVectorLayer::Success : QgsVectorLayer::EmptyGeometry; } -int QgsVectorLayerEditUtils::addRing( const QList &ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId ) +QgsGeometry::OperationResult QgsVectorLayerEditUtils::addRing( const QList &ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId ) { QgsLineString *ringLine = new QgsLineString( ring ); return addRing( ringLine, targetFeatureIds, modifiedFeatureId ); } -int QgsVectorLayerEditUtils::addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId ) +QgsGeometry::OperationResult QgsVectorLayerEditUtils::addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId ) { - if ( !L->isSpatial() ) + if ( !mLayer->isSpatial() ) { delete ring; - return 5; + return QgsGeometry::AddRingNotInExistingFeature; } - int addRingReturnCode = 5; //default: return code for 'ring not inserted' + QgsGeometry::OperationResult addRingReturnCode = QgsGeometry::AddRingNotInExistingFeature; //default: return code for 'ring not inserted' QgsFeature f; QgsFeatureIterator fit; if ( !targetFeatureIds.isEmpty() ) { //check only specified features - fit = L->getFeatures( QgsFeatureRequest().setFilterFids( targetFeatureIds ) ); + fit = mLayer->getFeatures( QgsFeatureRequest().setFilterFids( targetFeatureIds ) ); } else { //check all intersecting features QgsRectangle bBox = ring->boundingBox(); - fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) ); + fit = mLayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) ); } //find first valid feature we can add the ring to @@ -156,21 +156,22 @@ int QgsVectorLayerEditUtils::addRing( QgsCurve *ring, const QgsFeatureIds &targe addRingReturnCode = g.addRing( static_cast< QgsCurve * >( ring->clone() ) ); if ( addRingReturnCode == 0 ) - { - L->editBuffer()->changeGeometry( f.id(), g ); - if ( modifiedFeatureId ) - *modifiedFeatureId = f.id(); + if ( addRingReturnCode == QgsGeometry::Success ) + { + mLayer->editBuffer()->changeGeometry( f.id(), g ); + if ( modifiedFeatureId ) + *modifiedFeatureId = f.id(); - //setModified( true, true ); - break; - } + //setModified( true, true ); + break; + } } delete ring; return addRingReturnCode; } -int QgsVectorLayerEditUtils::addPart( const QList &points, QgsFeatureId featureId ) +QgsGeometry::OperationResult QgsVectorLayerEditUtils::addPart( const QList &points, QgsFeatureId featureId ) { QgsPointSequence l; for ( QList::const_iterator it = points.constBegin(); it != points.constEnd(); ++it ) @@ -180,16 +181,16 @@ int QgsVectorLayerEditUtils::addPart( const QList &points, QgsFeatur return addPart( l, featureId ); } -int QgsVectorLayerEditUtils::addPart( const QgsPointSequence &points, QgsFeatureId featureId ) +QgsGeometry::OperationResult QgsVectorLayerEditUtils::addPart( const QgsPointSequence &points, QgsFeatureId featureId ) { - if ( !L->isSpatial() ) - return 6; + if ( !mLayer->isSpatial() ) + return QgsGeometry::AddPartSelectedGeometryNotFound; QgsGeometry geometry; bool firstPart = false; QgsFeature f; - if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) ) - return 6; //not found + if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) ) + return QgsGeometry::AddPartSelectedGeometryNotFound; //not found if ( !f.hasGeometry() ) { @@ -201,30 +202,30 @@ int QgsVectorLayerEditUtils::addPart( const QgsPointSequence &points, QgsFeature geometry = f.geometry(); } - int errorCode = geometry.addPart( points, L->geometryType() ) ; - if ( errorCode == 0 ) + QgsGeometry::OperationResult errorCode = geometry.addPart( points, mLayer->geometryType() ) ; + if ( errorCode == QgsGeometry::Success ) { - if ( firstPart && QgsWkbTypes::isSingleType( L->wkbType() ) - && L->dataProvider()->doesStrictFeatureTypeCheck() ) + if ( firstPart && QgsWkbTypes::isSingleType( mLayer->wkbType() ) + && mLayer->dataProvider()->doesStrictFeatureTypeCheck() ) { //convert back to single part if required by layer geometry.convertToSingleType(); } - L->editBuffer()->changeGeometry( featureId, geometry ); + mLayer->editBuffer()->changeGeometry( featureId, geometry ); } return errorCode; } -int QgsVectorLayerEditUtils::addPart( QgsCurve *ring, QgsFeatureId featureId ) +QgsGeometry::OperationResult QgsVectorLayerEditUtils::addPart( QgsCurve *ring, QgsFeatureId featureId ) { - if ( !L->isSpatial() ) - return 6; + if ( !mLayer->isSpatial() ) + return QgsGeometry::AddPartSelectedGeometryNotFound; QgsGeometry geometry; bool firstPart = false; QgsFeature f; - if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) ) - return 6; //not found + if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) ) + return QgsGeometry::AddPartSelectedGeometryNotFound; if ( !f.hasGeometry() ) { @@ -236,16 +237,16 @@ int QgsVectorLayerEditUtils::addPart( QgsCurve *ring, QgsFeatureId featureId ) geometry = f.geometry(); } - int errorCode = geometry.addPart( ring, L->geometryType() ) ; - if ( errorCode == 0 ) + QgsGeometry::OperationResult errorCode = geometry.addPart( ring, mLayer->geometryType() ) ; + if ( errorCode == QgsGeometry::Success ) { - if ( firstPart && QgsWkbTypes::isSingleType( L->wkbType() ) - && L->dataProvider()->doesStrictFeatureTypeCheck() ) + if ( firstPart && QgsWkbTypes::isSingleType( mLayer->wkbType() ) + && mLayer->dataProvider()->doesStrictFeatureTypeCheck() ) { //convert back to single part if required by layer geometry.convertToSingleType(); } - L->editBuffer()->changeGeometry( featureId, geometry ); + mLayer->editBuffer()->changeGeometry( featureId, geometry ); } return errorCode; } @@ -253,11 +254,11 @@ int QgsVectorLayerEditUtils::addPart( QgsCurve *ring, QgsFeatureId featureId ) int QgsVectorLayerEditUtils::translateFeature( QgsFeatureId featureId, double dx, double dy ) { - if ( !L->isSpatial() ) + if ( !mLayer->isSpatial() ) return 1; QgsFeature f; - if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() ) + if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() ) return 1; //geometry not found QgsGeometry geometry = f.geometry(); @@ -265,30 +266,30 @@ int QgsVectorLayerEditUtils::translateFeature( QgsFeatureId featureId, double dx int errorCode = geometry.translate( dx, dy ); if ( errorCode == 0 ) { - L->editBuffer()->changeGeometry( featureId, geometry ); + mLayer->editBuffer()->changeGeometry( featureId, geometry ); } return errorCode; } -int QgsVectorLayerEditUtils::splitFeatures( const QList &splitLine, bool topologicalEditing ) +QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitFeatures( const QList &splitLine, bool topologicalEditing ) { - if ( !L->isSpatial() ) - return 4; + if ( !mLayer->isSpatial() ) + return QgsGeometry::InvalidBaseGeometry; QgsFeatureList newFeatures; //store all the newly created features double xMin, yMin, xMax, yMax; QgsRectangle bBox; //bounding box of the split line - int returnCode = 0; - int splitFunctionReturn; //return code of QgsGeometry::splitGeometry + QgsGeometry::OperationResult returnCode = QgsGeometry::Success; + QgsGeometry::OperationResult splitFunctionReturn; //return code of QgsGeometry::splitGeometry int numberOfSplitFeatures = 0; QgsFeatureIterator features; - const QgsFeatureIds selectedIds = L->selectedFeatureIds(); + const QgsFeatureIds selectedIds = mLayer->selectedFeatureIds(); if ( !selectedIds.isEmpty() ) //consider only the selected features if there is a selection { - features = L->getSelectedFeatures(); + features = mLayer->getSelectedFeatures(); } else //else consider all the feature that intersect the bounding box of the split line { @@ -301,7 +302,7 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList &splitLine, } else { - return 1; + return QgsGeometry::InvalidInput; } if ( bBox.isEmpty() ) @@ -321,7 +322,7 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList &splitLine, { //If we have a single point, we still create a non-null box double bufferDistance = 0.000001; - if ( L->crs().isGeographic() ) + if ( mLayer->crs().isGeographic() ) bufferDistance = 0.00000001; bBox.setXMinimum( bBox.xMinimum() - bufferDistance ); bBox.setXMaximum( bBox.xMaximum() + bufferDistance ); @@ -330,7 +331,7 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList &splitLine, } } - features = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) ); + features = mLayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) ); } QgsFeature feat; @@ -344,16 +345,16 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList &splitLine, QList topologyTestPoints; QgsGeometry featureGeom = feat.geometry(); splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints ); - if ( splitFunctionReturn == 0 ) + if ( splitFunctionReturn == QgsGeometry::Success ) { //change this geometry - L->editBuffer()->changeGeometry( feat.id(), featureGeom ); + mLayer->editBuffer()->changeGeometry( feat.id(), featureGeom ); //insert new features for ( int i = 0; i < newGeometries.size(); ++i ) { - QgsFeature f = QgsVectorLayerUtils::createFeature( L, newGeometries.at( i ), feat.attributes().toMap() ); - L->editBuffer()->addFeature( f ); + QgsFeature f = QgsVectorLayerUtils::createFeature( mLayer, newGeometries.at( i ), feat.attributes().toMap() ); + mLayer->editBuffer()->addFeature( f ); } if ( topologicalEditing ) @@ -366,7 +367,7 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList &splitLine, } ++numberOfSplitFeatures; } - else if ( splitFunctionReturn > 1 ) //1 means no split but also no error + else if ( splitFunctionReturn != QgsGeometry::Success && splitFunctionReturn != QgsGeometry::NothingHappened ) // i.e. no split but no error occurred { returnCode = splitFunctionReturn; } @@ -376,28 +377,28 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList &splitLine, { //There is a selection but no feature has been split. //Maybe user forgot that only the selected features are split - returnCode = 4; + returnCode = QgsGeometry::NothingHappened; } return returnCode; } -int QgsVectorLayerEditUtils::splitParts( const QList &splitLine, bool topologicalEditing ) +QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitParts( const QList &splitLine, bool topologicalEditing ) { - if ( !L->isSpatial() ) - return 4; + if ( !mLayer->isSpatial() ) + return QgsGeometry::InvalidBaseGeometry; double xMin, yMin, xMax, yMax; QgsRectangle bBox; //bounding box of the split line - int returnCode = 0; - int splitFunctionReturn; //return code of QgsGeometry::splitGeometry + QgsGeometry::OperationResult returnCode = QgsGeometry::Success; + QgsGeometry::OperationResult splitFunctionReturn; //return code of QgsGeometry::splitGeometry int numberOfSplitParts = 0; QgsFeatureIterator fit; - if ( L->selectedFeatureCount() > 0 ) //consider only the selected features if there is a selection + if ( mLayer->selectedFeatureCount() > 0 ) //consider only the selected features if there is a selection { - fit = L->getSelectedFeatures(); + fit = mLayer->getSelectedFeatures(); } else //else consider all the feature that intersect the bounding box of the split line { @@ -410,7 +411,7 @@ int QgsVectorLayerEditUtils::splitParts( const QList &splitLine, boo } else { - return 1; + return QgsGeometry::InvalidInput; } if ( bBox.isEmpty() ) @@ -430,7 +431,7 @@ int QgsVectorLayerEditUtils::splitParts( const QList &splitLine, boo { //If we have a single point, we still create a non-null box double bufferDistance = 0.000001; - if ( L->crs().isGeographic() ) + if ( mLayer->crs().isGeographic() ) bufferDistance = 0.00000001; bBox.setXMinimum( bBox.xMinimum() - bufferDistance ); bBox.setXMaximum( bBox.xMaximum() + bufferDistance ); @@ -439,7 +440,7 @@ int QgsVectorLayerEditUtils::splitParts( const QList &splitLine, boo } } - fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) ); + fit = mLayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) ); } int addPartRet = 0; @@ -469,7 +470,7 @@ int QgsVectorLayerEditUtils::splitParts( const QList &splitLine, boo if ( !addPartRet ) { - L->editBuffer()->changeGeometry( feat.id(), featureGeom ); + mLayer->editBuffer()->changeGeometry( feat.id(), featureGeom ); } else { @@ -489,7 +490,7 @@ int QgsVectorLayerEditUtils::splitParts( const QList &splitLine, boo break; } } - L->editBuffer()->changeGeometry( feat.id(), featureGeom ); + mLayer->editBuffer()->changeGeometry( feat.id(), featureGeom ); if ( topologicalEditing ) { @@ -507,11 +508,11 @@ int QgsVectorLayerEditUtils::splitParts( const QList &splitLine, boo } } - if ( numberOfSplitParts == 0 && L->selectedFeatureCount() > 0 && returnCode == 0 ) + if ( numberOfSplitParts == 0 && mLayer->selectedFeatureCount() > 0 && returnCode == 0 ) { //There is a selection but no feature has been split. //Maybe user forgot that only the selected features are split - returnCode = 4; + returnCode = QgsGeometry::NothingHappened; } return returnCode; @@ -520,7 +521,7 @@ int QgsVectorLayerEditUtils::splitParts( const QList &splitLine, boo int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsGeometry &geom ) { - if ( !L->isSpatial() ) + if ( !mLayer->isSpatial() ) return 1; if ( geom.isNull() ) @@ -628,18 +629,18 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsGeometry &geom ) int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p ) { - if ( !L->isSpatial() ) + if ( !mLayer->isSpatial() ) return 1; - double segmentSearchEpsilon = L->crs().isGeographic() ? 1e-12 : 1e-8; + double segmentSearchEpsilon = mLayer->crs().isGeographic() ? 1e-12 : 1e-8; //work with a tolerance because coordinate projection may introduce some rounding double threshold = 0.0000001; - if ( L->crs().mapUnits() == QgsUnitTypes::DistanceMeters ) + if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceMeters ) { threshold = 0.001; } - else if ( L->crs().mapUnits() == QgsUnitTypes::DistanceFeet ) + else if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceFeet ) { threshold = 0.0001; } @@ -649,7 +650,7 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p ) double sqrSnappingTolerance = threshold * threshold; QgsFeature f; - QgsFeatureIterator fit = L->getFeatures( QgsFeatureRequest() + QgsFeatureIterator fit = mLayer->getFeatures( QgsFeatureRequest() .setFilterRect( searchRect ) .setFlags( QgsFeatureRequest::ExactIntersect ) .setSubsetOfAttributes( QgsAttributeList() ) ); @@ -685,7 +686,7 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p ) if ( sqrDistVertexSnap < sqrSnappingTolerance ) continue; // the vertex already exists - do not insert it - if ( !L->insertVertex( p.x(), p.y(), fid, segmentAfterVertex ) ) + if ( !mLayer->insertVertex( p.x(), p.y(), fid, segmentAfterVertex ) ) { QgsDebugMsg( "failed to insert topo point" ); } @@ -695,11 +696,11 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p ) } -int QgsVectorLayerEditUtils::boundingBoxFromPointList( const QList &list, double &xmin, double &ymin, double &xmax, double &ymax ) const +bool QgsVectorLayerEditUtils::boundingBoxFromPointList( const QList &list, double &xmin, double &ymin, double &xmax, double &ymax ) const { if ( list.size() < 1 ) { - return 1; + return false; } xmin = std::numeric_limits::max(); @@ -727,5 +728,5 @@ int QgsVectorLayerEditUtils::boundingBoxFromPointList( const QList & } } - return 0; + return true; } diff --git a/src/core/qgsvectorlayereditutils.h b/src/core/qgsvectorlayereditutils.h index cd89c76969b..e9ee658d4f7 100644 --- a/src/core/qgsvectorlayereditutils.h +++ b/src/core/qgsvectorlayereditutils.h @@ -19,9 +19,8 @@ #include "qgis_core.h" #include "qgis.h" #include "qgsfeature.h" - -#include "qgsvectorlayer.h" #include "qgsgeometry.h" +#include "qgsvectorlayer.h" class QgsCurve; @@ -66,68 +65,40 @@ class CORE_EXPORT QgsVectorLayerEditUtils QgsVectorLayer::EditResult deleteVertex( QgsFeatureId featureId, int vertex ); /** Adds a ring to polygon/multipolygon features - * \param ring ring to add - * \param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise + * @param ring ring to add + * @param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise * all intersecting features are tested and the ring is added to the first valid feature. - * \param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter - * \returns - * 0 in case of success, - * 1 problem with feature type, - * 2 ring not closed, - * 3 ring not valid, - * 4 ring crosses existing rings, - * 5 no feature found where ring can be inserted + * @param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter + * @return OperationResult result code: success or reason of failure */ - // TODO QGIS 3.0 returns an enum instead of a magic constant - int addRing( const QList &ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr ); + QgsGeometry::OperationResult addRing( const QList &ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr ); - /** Adds a ring to polygon/multipolygon features - * \param ring ring to add - * \param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise + /** + * Adds a ring to polygon/multipolygon features + * @param ring ring to add + * @param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise * all intersecting features are tested and the ring is added to the first valid feature. - * \param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter - * \returns - * 0 in case of success, - * 1 problem with feature type, - * 2 ring not closed, - * 3 ring not valid, - * 4 ring crosses existing rings, - * 5 no feature found where ring can be inserted - * \note available in Python bindings as addCurvedRing + * @param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter + * @return OperationResult result code: success or reason of failure + * @note available in python bindings as addCurvedRing */ - // TODO QGIS 3.0 returns an enum instead of a magic constant - int addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr ) SIP_PYNAME( addCurvedRing ); + QgsGeometry::OperationResult addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr ) SIP_PYNAME( addCurvedRing ); - /** Adds a new part polygon to a multipart feature - * \returns - * 0 in case of success, - * 1 if selected feature is not multipart, - * 2 if ring is not a valid geometry, - * 3 if new polygon ring not disjoint with existing rings, - * 4 if no feature was selected, - * 5 if several features are selected, - * 6 if selected geometry not found + /** + * Adds a new part polygon to a multipart feature + * @returns QgsGeometry::OperationResult a result code: success or reason of failure */ - // TODO QGIS 3.0 returns an enum instead of a magic constant - int addPart( const QList &ring, QgsFeatureId featureId ); + QgsGeometry::OperationResult addPart( const QList &ring, QgsFeatureId featureId ); - /** Adds a new part polygon to a multipart feature - * \returns - * 0 in case of success, - * 1 if selected feature is not multipart, - * 2 if ring is not a valid geometry, - * 3 if new polygon ring not disjoint with existing rings, - * 4 if no feature was selected, - * 5 if several features are selected, - * 6 if selected geometry not found - * \note available in Python bindings as addPartV2 + /** + * Adds a new part polygon to a multipart feature + * @returns QgsGeometry::OperationResult a result code: success or reason of failure + * @note available in python bindings as addPartV2 */ - // TODO QGIS 3.0 returns an enum instead of a magic constant - int addPart( const QgsPointSequence &ring, QgsFeatureId featureId ); + QgsGeometry::OperationResult addPart( const QgsPointSequence &ring, QgsFeatureId featureId ); - //! \note available in Python bindings as addCurvedPart - // TODO QGIS 3.0 returns an enum instead of a magic constant - int addPart( QgsCurve *ring, QgsFeatureId featureId ) SIP_PYNAME( addCurvedPart ); + // @note available in python bindings as addCurvedPart + QgsGeometry::OperationResult addPart( QgsCurve *ring, QgsFeatureId featureId ) SIP_PYNAME( addCurvedPart ); /** Translates feature by dx, dy * \param featureId id of the feature to translate @@ -145,7 +116,7 @@ class CORE_EXPORT QgsVectorLayerEditUtils * 4 if there is a selection but no feature split */ // TODO QGIS 3.0 returns an enum instead of a magic constant - int splitParts( const QList &splitLine, bool topologicalEditing = false ); + QgsGeometry::OperationResult splitParts( const QList &splitLine, bool topologicalEditing = false ); /** Splits features cut by the given line * \param splitLine line that splits the layer features @@ -155,7 +126,7 @@ class CORE_EXPORT QgsVectorLayerEditUtils * 4 if there is a selection but no feature split */ // TODO QGIS 3.0 returns an enum instead of a magic constant - int splitFeatures( const QList &splitLine, bool topologicalEditing = false ); + QgsGeometry::OperationResult splitFeatures( const QList &splitLine, bool topologicalEditing = false ); /** Adds topological points for every vertex of the geometry. * \param geom the geometry where each vertex is added to segments of other features @@ -173,15 +144,15 @@ class CORE_EXPORT QgsVectorLayerEditUtils */ int addTopologicalPoints( const QgsPointXY &p ); - protected: - - /** Little helper function that gives bounding box from a list of points. - \returns 0 in case of success */ - int boundingBoxFromPointList( const QList &list, double &xmin, double &ymin, double &xmax, double &ymax ) const; - private: - QgsVectorLayer *L = nullptr; + /** + * Little helper function that gives bounding box from a list of points. + * \returns True in case of success + */ + bool boundingBoxFromPointList( const QList &list, double &xmin, double &ymin, double &xmax, double &ymax ) const; + + QgsVectorLayer *mLayer = nullptr; }; #endif // QGSVECTORLAYEREDITUTILS_H diff --git a/tests/src/python/test_qgsgeometry.py b/tests/src/python/test_qgsgeometry.py index 6065c1f7c0a..8fb43724b46 100644 --- a/tests/src/python/test_qgsgeometry.py +++ b/tests/src/python/test_qgsgeometry.py @@ -1423,14 +1423,14 @@ class TestQgsGeometry(unittest.TestCase): ] polyline = QgsGeometry.fromPolyline(points[0]) - self.assertEqual(polyline.addPoints(points[1][0:1]), 2, "addPoints with one point line unexpectedly succeeded.") - self.assertEqual(polyline.addPoints(points[1][0:2]), 0, "addPoints with two point line failed.") + self.assertEqual(polyline.addPoints(points[1][0:1]), QgsGeometry.InvalidInput, "addPoints with one point line unexpectedly succeeded.") + self.assertEqual(polyline.addPoints(points[1][0:2]), QgsGeometry.Success, "addPoints with two point line failed.") expwkt = "MultiLineString ((0 0, 1 0, 1 1, 2 1, 2 0), (3 0, 3 1))" wkt = polyline.exportToWkt() assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) polyline = QgsGeometry.fromPolyline(points[0]) - self.assertEqual(polyline.addPoints(points[1]), 0, "addPoints with %d point line failed." % len(points[1])) + self.assertEqual(polyline.addPoints(points[1]), QgsGeometry.Success, "addPoints with %d point line failed." % len(points[1])) expwkt = "MultiLineString ((0 0, 1 0, 1 1, 2 1, 2 0), (3 0, 3 1, 5 1, 5 0, 6 0))" wkt = polyline.exportToWkt() assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) @@ -1439,7 +1439,7 @@ class TestQgsGeometry(unittest.TestCase): polyline = QgsGeometry.fromPolyline(points[0]) polyline.geometry().addZValue(4.0) points2 = [QgsPoint(p[0], p[1], 3.0, wkbType=QgsWkbTypes.PointZ) for p in points[1]] - self.assertEqual(polyline.addPointsV2(points2), 0) + self.assertEqual(polyline.addPointsV2(points2), QgsGeometry.Success) expwkt = "MultiLineStringZ ((0 0 4, 1 0 4, 1 1 4, 2 1 4, 2 0 4),(3 0 3, 3 1 3, 5 1 3, 5 0 3, 6 0 3))" wkt = polyline.exportToWkt() assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) @@ -1456,12 +1456,12 @@ class TestQgsGeometry(unittest.TestCase): polygon = QgsGeometry.fromPolygon(points[0]) - self.assertEqual(polygon.addPoints(points[1][0][0:1]), 2, "addPoints with one point ring unexpectedly succeeded.") - self.assertEqual(polygon.addPoints(points[1][0][0:2]), 2, "addPoints with two point ring unexpectedly succeeded.") - self.assertEqual(polygon.addPoints(points[1][0][0:3]), 2, "addPoints with unclosed three point ring unexpectedly succeeded.") - self.assertEqual(polygon.addPoints([QgsPointXY(4, 0), QgsPointXY(5, 0), QgsPointXY(4, 0)]), 2, "addPoints with 'closed' three point ring unexpectedly succeeded.") + self.assertEqual(polygon.addPoints(points[1][0][0:1]), QgsGeometry.InvalidInput, "addPoints with one point ring unexpectedly succeeded.") + self.assertEqual(polygon.addPoints(points[1][0][0:2]), QgsGeometry.InvalidInput, "addPoints with two point ring unexpectedly succeeded.") + self.assertEqual(polygon.addPoints(points[1][0][0:3]), QgsGeometry.InvalidInput, "addPoints with unclosed three point ring unexpectedly succeeded.") + self.assertEqual(polygon.addPoints([QgsPointXY(4, 0), QgsPointXY(5, 0), QgsPointXY(4, 0)]), QgsGeometry.InvalidInput, "addPoints with 'closed' three point ring unexpectedly succeeded.") - self.assertEqual(polygon.addPoints(points[1][0]), 0, "addPoints failed") + self.assertEqual(polygon.addPoints(points[1][0]), QgsGeometry.Success, "addPoints failed") expwkt = "MultiPolygon (((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0)),((4 0, 5 0, 5 2, 3 2, 3 1, 4 1, 4 0)))" wkt = polygon.exportToWkt() assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) @@ -1469,13 +1469,13 @@ class TestQgsGeometry(unittest.TestCase): mp = QgsGeometry.fromMultiPolygon(points[:1]) p = QgsGeometry.fromPolygon(points[1]) - self.assertEqual(mp.addPartGeometry(p), 0) + self.assertEqual(mp.addPartGeometry(p), QgsGeometry.Success) wkt = mp.exportToWkt() assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) mp = QgsGeometry.fromMultiPolygon(points[:1]) mp2 = QgsGeometry.fromMultiPolygon(points[1:]) - self.assertEqual(mp.addPartGeometry(mp2), 0) + self.assertEqual(mp.addPartGeometry(mp2), QgsGeometry.Success) wkt = mp.exportToWkt() assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) @@ -1483,7 +1483,7 @@ class TestQgsGeometry(unittest.TestCase): polygon = QgsGeometry.fromPolygon(points[0]) polygon.geometry().addZValue(4.0) points2 = [QgsPoint(pi[0], pi[1], 3.0, wkbType=QgsWkbTypes.PointZ) for pi in points[1][0]] - self.assertEqual(polygon.addPointsV2(points2), 0) + self.assertEqual(polygon.addPointsV2(points2), QgsGeometry.Success) expwkt = "MultiPolygonZ (((0 0 4, 1 0 4, 1 1 4, 2 1 4, 2 2 4, 0 2 4, 0 0 4)),((4 0 3, 5 0 3, 5 2 3, 3 2 3, 3 1 3, 4 1 3, 4 0 3)))" wkt = polygon.exportToWkt() assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) @@ -1492,38 +1492,38 @@ class TestQgsGeometry(unittest.TestCase): empty = QgsGeometry() # if not default type specified, addPart should fail result = empty.addPoints([QgsPointXY(4, 0)]) - assert result != 0, 'Got return code {}'.format(result) + assert result != QgsGeometry.Success, 'Got return code {}'.format(result) result = empty.addPoints([QgsPointXY(4, 0)], QgsWkbTypes.PointGeometry) - self.assertEqual(result, 0, 'Got return code {}'.format(result)) + self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result)) wkt = empty.exportToWkt() expwkt = 'MultiPoint ((4 0))' assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) result = empty.addPoints([QgsPointXY(5, 1)]) - self.assertEqual(result, 0, 'Got return code {}'.format(result)) + self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result)) wkt = empty.exportToWkt() expwkt = 'MultiPoint ((4 0),(5 1))' assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) # next try with lines empty = QgsGeometry() result = empty.addPoints(points[0][0], QgsWkbTypes.LineGeometry) - self.assertEqual(result, 0, 'Got return code {}'.format(result)) + self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result)) wkt = empty.exportToWkt() expwkt = 'MultiLineString ((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0))' assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) result = empty.addPoints(points[1][0]) - self.assertEqual(result, 0, 'Got return code {}'.format(result)) + self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result)) wkt = empty.exportToWkt() expwkt = 'MultiLineString ((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0),(4 0, 5 0, 5 2, 3 2, 3 1, 4 1, 4 0))' assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) # finally try with polygons empty = QgsGeometry() result = empty.addPoints(points[0][0], QgsWkbTypes.PolygonGeometry) - self.assertEqual(result, 0, 'Got return code {}'.format(result)) + self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result)) wkt = empty.exportToWkt() expwkt = 'MultiPolygon (((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0)))' assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt) result = empty.addPoints(points[1][0]) - self.assertEqual(result, 0, 'Got return code {}'.format(result)) + self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result)) wkt = empty.exportToWkt() expwkt = 'MultiPolygon (((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0)),((4 0, 5 0, 5 2, 3 2, 3 1, 4 1, 4 0)))' assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)