diff --git a/python/core/auto_additions/qgis.py b/python/core/auto_additions/qgis.py index 30ffa5bd5ef..759a97a55ae 100644 --- a/python/core/auto_additions/qgis.py +++ b/python/core/auto_additions/qgis.py @@ -1464,7 +1464,10 @@ QgsGeometry.AddRingNotInExistingFeature.__doc__ = "The input ring doesn't have a QgsGeometry.SplitCannotSplitPoint = Qgis.GeometryOperationResult.SplitCannotSplitPoint QgsGeometry.SplitCannotSplitPoint.is_monkey_patched = True QgsGeometry.SplitCannotSplitPoint.__doc__ = "Cannot split points" -Qgis.GeometryOperationResult.__doc__ = "Success or failure of a geometry operation.\n\nThis enum gives details about cause of failure.\n\n.. versionadded:: 3.22\n\n" + '* ``Success``: ' + Qgis.GeometryOperationResult.Success.__doc__ + '\n' + '* ``NothingHappened``: ' + Qgis.GeometryOperationResult.NothingHappened.__doc__ + '\n' + '* ``InvalidBaseGeometry``: ' + Qgis.GeometryOperationResult.InvalidBaseGeometry.__doc__ + '\n' + '* ``InvalidInputGeometryType``: ' + Qgis.GeometryOperationResult.InvalidInputGeometryType.__doc__ + '\n' + '* ``SelectionIsEmpty``: ' + Qgis.GeometryOperationResult.SelectionIsEmpty.__doc__ + '\n' + '* ``SelectionIsGreaterThanOne``: ' + Qgis.GeometryOperationResult.SelectionIsGreaterThanOne.__doc__ + '\n' + '* ``GeometryEngineError``: ' + Qgis.GeometryOperationResult.GeometryEngineError.__doc__ + '\n' + '* ``LayerNotEditable``: ' + Qgis.GeometryOperationResult.LayerNotEditable.__doc__ + '\n' + '* ``AddPartSelectedGeometryNotFound``: ' + Qgis.GeometryOperationResult.AddPartSelectedGeometryNotFound.__doc__ + '\n' + '* ``AddPartNotMultiGeometry``: ' + Qgis.GeometryOperationResult.AddPartNotMultiGeometry.__doc__ + '\n' + '* ``AddRingNotClosed``: ' + Qgis.GeometryOperationResult.AddRingNotClosed.__doc__ + '\n' + '* ``AddRingNotValid``: ' + Qgis.GeometryOperationResult.AddRingNotValid.__doc__ + '\n' + '* ``AddRingCrossesExistingRings``: ' + Qgis.GeometryOperationResult.AddRingCrossesExistingRings.__doc__ + '\n' + '* ``AddRingNotInExistingFeature``: ' + Qgis.GeometryOperationResult.AddRingNotInExistingFeature.__doc__ + '\n' + '* ``SplitCannotSplitPoint``: ' + Qgis.GeometryOperationResult.SplitCannotSplitPoint.__doc__ +QgsGeometry.GeometryTypeHasChanged = Qgis.GeometryOperationResult.GeometryTypeHasChanged +QgsGeometry.GeometryTypeHasChanged.is_monkey_patched = True +QgsGeometry.GeometryTypeHasChanged.__doc__ = "Operation has changed geometry type" +Qgis.GeometryOperationResult.__doc__ = "Success or failure of a geometry operation.\n\nThis enum gives details about cause of failure.\n\n.. versionadded:: 3.22\n\n" + '* ``Success``: ' + Qgis.GeometryOperationResult.Success.__doc__ + '\n' + '* ``NothingHappened``: ' + Qgis.GeometryOperationResult.NothingHappened.__doc__ + '\n' + '* ``InvalidBaseGeometry``: ' + Qgis.GeometryOperationResult.InvalidBaseGeometry.__doc__ + '\n' + '* ``InvalidInputGeometryType``: ' + Qgis.GeometryOperationResult.InvalidInputGeometryType.__doc__ + '\n' + '* ``SelectionIsEmpty``: ' + Qgis.GeometryOperationResult.SelectionIsEmpty.__doc__ + '\n' + '* ``SelectionIsGreaterThanOne``: ' + Qgis.GeometryOperationResult.SelectionIsGreaterThanOne.__doc__ + '\n' + '* ``GeometryEngineError``: ' + Qgis.GeometryOperationResult.GeometryEngineError.__doc__ + '\n' + '* ``LayerNotEditable``: ' + Qgis.GeometryOperationResult.LayerNotEditable.__doc__ + '\n' + '* ``AddPartSelectedGeometryNotFound``: ' + Qgis.GeometryOperationResult.AddPartSelectedGeometryNotFound.__doc__ + '\n' + '* ``AddPartNotMultiGeometry``: ' + Qgis.GeometryOperationResult.AddPartNotMultiGeometry.__doc__ + '\n' + '* ``AddRingNotClosed``: ' + Qgis.GeometryOperationResult.AddRingNotClosed.__doc__ + '\n' + '* ``AddRingNotValid``: ' + Qgis.GeometryOperationResult.AddRingNotValid.__doc__ + '\n' + '* ``AddRingCrossesExistingRings``: ' + Qgis.GeometryOperationResult.AddRingCrossesExistingRings.__doc__ + '\n' + '* ``AddRingNotInExistingFeature``: ' + Qgis.GeometryOperationResult.AddRingNotInExistingFeature.__doc__ + '\n' + '* ``SplitCannotSplitPoint``: ' + Qgis.GeometryOperationResult.SplitCannotSplitPoint.__doc__ + '\n' + '* ``GeometryTypeHasChanged``: ' + Qgis.GeometryOperationResult.GeometryTypeHasChanged.__doc__ # -- Qgis.GeometryOperationResult.baseClass = Qgis QgsGeometry.ValidityFlag = Qgis.GeometryValidityFlag diff --git a/python/core/auto_generated/geometry/qgsgeometry.sip.in b/python/core/auto_generated/geometry/qgsgeometry.sip.in index 0fb7b60e15d..3a53b66397d 100644 --- a/python/core/auto_generated/geometry/qgsgeometry.sip.in +++ b/python/core/auto_generated/geometry/qgsgeometry.sip.in @@ -2430,7 +2430,7 @@ empty if none of the child geometries match the desired type. .. versionadded:: 3.2 %End - int avoidIntersections( const QList &avoidIntersectionsLayers ); + int avoidIntersections( const QList &avoidIntersectionsLayers ) /Deprecated/; %Docstring Modifies geometry to avoid intersections with the layers specified in project properties @@ -2444,6 +2444,25 @@ Modifies geometry to avoid intersections with the layers specified in project pr 4 if the geometry is not intersected by one of the geometries present in the provided layers. .. versionadded:: 1.5 + +.. deprecated:: + QGIS 3.34 +%End + + Qgis::GeometryOperationResult avoidIntersectionsV2( const QList &avoidIntersectionsLayers ); + +%Docstring +Modifies geometry to avoid intersections with the layers specified in project properties + +:param avoidIntersectionsLayers: list of layers to check for intersections + +:return: Success in case of success + InvalidInputGeometryType if geometry is not of polygon type + GeometryTypeHasChanged if avoid intersection has changed the geometry type + InvalidBaseGeometry at least one geometry intersected is invalid. The algorithm may not work and return the same geometry as the input. You must fix your intersecting geometries. + NothingHappened if the geometry is not intersected by one of the geometries present in the provided layers. + +.. versionadded:: 3.34 %End QgsGeometry makeValid( Qgis::MakeValidMethod method = Qgis::MakeValidMethod::Linework, bool keepCollapsed = false ) const throw( QgsNotSupportedException ); diff --git a/python/core/auto_generated/qgis.sip.in b/python/core/auto_generated/qgis.sip.in index ace323980b6..67d37833a60 100644 --- a/python/core/auto_generated/qgis.sip.in +++ b/python/core/auto_generated/qgis.sip.in @@ -891,6 +891,7 @@ The development version AddRingNotInExistingFeature, // Split features SplitCannotSplitPoint, + GeometryTypeHasChanged, }; enum class GeometryValidityFlag diff --git a/src/app/gps/qgsappgpsdigitizing.cpp b/src/app/gps/qgsappgpsdigitizing.cpp index 812d131cc1e..bd3eafbc797 100644 --- a/src/app/gps/qgsappgpsdigitizing.cpp +++ b/src/app/gps/qgsappgpsdigitizing.cpp @@ -277,18 +277,18 @@ void QgsAppGpsDigitizing::createFeature() if ( geometryLayerCrs.type() == Qgis::GeometryType::Polygon ) { - const int avoidIntersectionsReturn = geometryLayerCrs.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() ); - if ( avoidIntersectionsReturn == 1 ) + const Qgis::GeometryOperationResult avoidIntersectionsReturn = geometryLayerCrs.avoidIntersectionsV2( QgsProject::instance()->avoidIntersectionsLayers() ); + if ( avoidIntersectionsReturn == Qgis::GeometryOperationResult::InvalidInputGeometryType ) { //not a polygon type. Impossible to get there } - else if ( avoidIntersectionsReturn == 2 ) + else if ( avoidIntersectionsReturn == Qgis::GeometryOperationResult::GeometryTypeHasChanged ) { //bail out... QgisApp::instance()->messageBar()->pushWarning( tr( "Add Feature" ), tr( "The feature could not be added because removing the polygon intersections would change the geometry type." ) ); return; } - else if ( avoidIntersectionsReturn == 3 ) + else if ( avoidIntersectionsReturn == Qgis::GeometryOperationResult::InvalidBaseGeometry ) { QgisApp::instance()->messageBar()->pushCritical( tr( "Add Feature" ), tr( "The feature has been added, but at least one geometry intersected is invalid. These geometries must be manually repaired." ) ); return; @@ -488,4 +488,3 @@ QVariant QgsAppGpsDigitizing::timestamp( QgsVectorLayer *vlayer, int idx ) const } return value; } - diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index ed5fc8cbc6a..66bdaea0fed 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -10227,7 +10227,7 @@ void QgisApp::pasteFromClipboard( QgsMapLayer *destinationLayer ) } if ( avoidIntersectionsLayers.size() > 0 ) { - geom.avoidIntersections( avoidIntersectionsLayers ); + geom.avoidIntersectionsV2( avoidIntersectionsLayers ); } // count collapsed geometries diff --git a/src/app/qgsmaptooladdpart.cpp b/src/app/qgsmaptooladdpart.cpp index 8c184fcf393..e6b10afd9a4 100644 --- a/src/app/qgsmaptooladdpart.cpp +++ b/src/app/qgsmaptooladdpart.cpp @@ -162,6 +162,7 @@ void QgsMapToolAddPart::finalizeEditCommand( QgsVectorLayer *layer, Qgis::Geomet case Qgis::GeometryOperationResult::LayerNotEditable: case Qgis::GeometryOperationResult::NothingHappened: case Qgis::GeometryOperationResult::SplitCannotSplitPoint: + case Qgis::GeometryOperationResult::GeometryTypeHasChanged: // Should not reach here // Other OperationResults should not be returned by addPart errorMessage = tr( "Unexpected OperationResult: %1" ).arg( qgsEnumValueToKey( errorCode ) ); @@ -228,4 +229,3 @@ QgsVectorLayer *QgsMapToolAddPart::getLayerAndCheckSelection() else return nullptr; } - diff --git a/src/app/qgsmaptooladdring.cpp b/src/app/qgsmaptooladdring.cpp index b412d852e51..63554743f1c 100644 --- a/src/app/qgsmaptooladdring.cpp +++ b/src/app/qgsmaptooladdring.cpp @@ -100,6 +100,7 @@ void QgsMapToolAddRing::polygonCaptured( const QgsCurvePolygon *polygon ) case Qgis::GeometryOperationResult::LayerNotEditable: case Qgis::GeometryOperationResult::AddPartSelectedGeometryNotFound: case Qgis::GeometryOperationResult::AddPartNotMultiGeometry: + case Qgis::GeometryOperationResult::GeometryTypeHasChanged: errorMessage = tr( "an unknown error occurred (%1)" ).arg( qgsEnumValueToKey( addRingReturnCode ) ); break; } diff --git a/src/app/qgsmaptoolreshape.cpp b/src/app/qgsmaptoolreshape.cpp index 292fb3cb5fe..e3d7290d822 100644 --- a/src/app/qgsmaptoolreshape.cpp +++ b/src/app/qgsmaptoolreshape.cpp @@ -194,11 +194,11 @@ void QgsMapToolReshape::reshape( QgsVectorLayer *vlayer ) case Qgis::AvoidIntersectionsMode::AllowIntersections: break; } - int res = -1; + Qgis::GeometryOperationResult res = Qgis::GeometryOperationResult::NothingHappened; if ( avoidIntersectionsLayers.size() > 0 ) { - res = geom.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers(), ignoreFeatures ); - if ( res == 1 ) + res = geom.avoidIntersectionsV2( QgsProject::instance()->avoidIntersectionsLayers(), ignoreFeatures ); + if ( res == Qgis::GeometryOperationResult::InvalidInputGeometryType ) { emit messageEmitted( tr( "An error was reported during intersection removal" ), Qgis::MessageLevel::Critical ); vlayer->destroyEditCommand(); @@ -213,7 +213,7 @@ void QgsMapToolReshape::reshape( QgsVectorLayer *vlayer ) vlayer->destroyEditCommand(); return; } - if ( res == 3 ) + if ( res == Qgis::GeometryOperationResult::InvalidBaseGeometry ) { emit messageEmitted( tr( "At least one geometry intersected is invalid. These geometries must be manually repaired." ), Qgis::MessageLevel::Warning ); } diff --git a/src/app/vertextool/qgsvertextool.cpp b/src/app/vertextool/qgsvertextool.cpp index 60dfad3aa3b..c9f1645246b 100644 --- a/src/app/vertextool/qgsvertextool.cpp +++ b/src/app/vertextool/qgsvertextool.cpp @@ -2425,11 +2425,11 @@ void QgsVertexTool::applyEditsToLayers( QgsVertexTool::VertexEdits &edits ) QgsGeometry featGeom = itFeatEdit.value().geom; if ( avoidIntersectionsLayers.size() > 0 ) { - int avoidIntersectionsReturn = featGeom.avoidIntersections( avoidIntersectionsLayers, ignoreFeatures ); + Qgis::GeometryOperationResult avoidIntersectionsReturn = featGeom.avoidIntersectionsV2( avoidIntersectionsLayers, ignoreFeatures ); switch ( avoidIntersectionsReturn ) { - case 2: // Geometry type was changed, let's try our best to make it compatible with the target layer + case Qgis::GeometryOperationResult::GeometryTypeHasChanged: // Geometry type was changed, let's try our best to make it compatible with the target layer { const QVector newGeoms = featGeom.coerceToType( layer->wkbType() ); if ( newGeoms.count() == 1 ) @@ -2476,7 +2476,7 @@ void QgsVertexTool::applyEditsToLayers( QgsVertexTool::VertexEdits &edits ) break; } - case 3: + case Qgis::GeometryOperationResult::InvalidBaseGeometry: emit messageEmitted( tr( "At least one geometry intersected is invalid. These geometries must be manually repaired." ), Qgis::MessageLevel::Warning ); break; @@ -2485,7 +2485,7 @@ void QgsVertexTool::applyEditsToLayers( QgsVertexTool::VertexEdits &edits ) } // if the geometry has been changed - if ( avoidIntersectionsReturn != 1 && avoidIntersectionsReturn != 4 ) + if ( avoidIntersectionsReturn != Qgis::GeometryOperationResult::InvalidInputGeometryType && avoidIntersectionsReturn != Qgis::GeometryOperationResult::NothingHappened ) { // then add the new points generated by avoidIntersections QgsGeometry oldGeom = layer->getGeometry( itFeatEdit.key() ).convertToType( Qgis::GeometryType::Point, true ); diff --git a/src/core/geometry/qgsgeometry.cpp b/src/core/geometry/qgsgeometry.cpp index 153af6fbb07..f8af8719021 100644 --- a/src/core/geometry/qgsgeometry.cpp +++ b/src/core/geometry/qgsgeometry.cpp @@ -3039,11 +3039,11 @@ bool QgsGeometry::deletePart( int partNum ) return ok; } -int QgsGeometry::avoidIntersections( const QList &avoidIntersectionsLayers, const QHash > &ignoreFeatures ) +Qgis::GeometryOperationResult QgsGeometry::avoidIntersectionsV2( const QList &avoidIntersectionsLayers, const QHash > &ignoreFeatures ) { if ( !d->geometry ) { - return 1; + return Qgis::GeometryOperationResult::InvalidInputGeometryType; } Qgis::WkbType geomTypeBeforeModification = wkbType(); @@ -3059,15 +3059,45 @@ int QgsGeometry::avoidIntersections( const QList &avoidInterse } if ( geomTypeBeforeModification != wkbType() ) - return 2; + return Qgis::GeometryOperationResult::GeometryTypeHasChanged; if ( haveInvalidGeometry ) - return 3; + return Qgis::GeometryOperationResult::InvalidBaseGeometry; if ( !geomModified ) - return 4; + return Qgis::GeometryOperationResult::NothingHappened; - return 0; + return Qgis::GeometryOperationResult::Success; } +int QgsGeometry::avoidIntersections( const QList &avoidIntersectionsLayers, const QHash > &ignoreFeatures ) +{ + const Qgis::GeometryOperationResult result = avoidIntersectionsV2( avoidIntersectionsLayers, ignoreFeatures ); + switch ( result ) + { + case Qgis::GeometryOperationResult::Success: + return 0; + case Qgis::GeometryOperationResult::InvalidInputGeometryType: + return 1; + case Qgis::GeometryOperationResult::GeometryTypeHasChanged: + return 2; + case Qgis::GeometryOperationResult::InvalidBaseGeometry: + return 3; + case Qgis::GeometryOperationResult::NothingHappened: + + // these should never happen + case Qgis::GeometryOperationResult::SelectionIsEmpty: + case Qgis::GeometryOperationResult::SelectionIsGreaterThanOne: + case Qgis::GeometryOperationResult::GeometryEngineError: + case Qgis::GeometryOperationResult::LayerNotEditable: + case Qgis::GeometryOperationResult::AddPartSelectedGeometryNotFound: + case Qgis::GeometryOperationResult::AddPartNotMultiGeometry: + case Qgis::GeometryOperationResult::AddRingNotClosed: + case Qgis::GeometryOperationResult::AddRingNotValid: + case Qgis::GeometryOperationResult::AddRingCrossesExistingRings: + case Qgis::GeometryOperationResult::AddRingNotInExistingFeature: + case Qgis::GeometryOperationResult::SplitCannotSplitPoint: + return 4; + } +} QgsGeometry QgsGeometry::makeValid( Qgis::MakeValidMethod method, bool keepCollapsed ) const { diff --git a/src/core/geometry/qgsgeometry.h b/src/core/geometry/qgsgeometry.h index eb69fbddc1a..3ce2ddad1ed 100644 --- a/src/core/geometry/qgsgeometry.h +++ b/src/core/geometry/qgsgeometry.h @@ -2550,9 +2550,24 @@ class CORE_EXPORT QgsGeometry * 3 at least one geometry intersected is invalid. The algorithm may not work and return the same geometry as the input. You must fix your intersecting geometries. * 4 if the geometry is not intersected by one of the geometries present in the provided layers. * \since QGIS 1.5 + * \deprecated QGIS 3.34 */ - int avoidIntersections( const QList &avoidIntersectionsLayers, - const QHash > &ignoreFeatures SIP_PYARGREMOVE = ( QHash >() ) ); + Q_DECL_DEPRECATED int avoidIntersections( const QList &avoidIntersectionsLayers, + const QHash > &ignoreFeatures SIP_PYARGREMOVE = ( QHash >() ) ) SIP_DEPRECATED; + + /** + * Modifies geometry to avoid intersections with the layers specified in project properties + * \param avoidIntersectionsLayers list of layers to check for intersections + * \param ignoreFeatures possibility to give a list of features where intersections should be ignored (not available in Python bindings) + * \returns Success in case of success + * InvalidInputGeometryType if geometry is not of polygon type + * GeometryTypeHasChanged if avoid intersection has changed the geometry type + * InvalidBaseGeometry at least one geometry intersected is invalid. The algorithm may not work and return the same geometry as the input. You must fix your intersecting geometries. + * NothingHappened if the geometry is not intersected by one of the geometries present in the provided layers. + * \since QGIS 3.34 + */ + Qgis::GeometryOperationResult avoidIntersectionsV2( const QList &avoidIntersectionsLayers, + const QHash > &ignoreFeatures SIP_PYARGREMOVE = ( QHash >() ) ); /** * Attempts to make an invalid geometry valid without losing vertices. diff --git a/src/core/qgis.h b/src/core/qgis.h index b48b63e24c3..95a117a0547 100644 --- a/src/core/qgis.h +++ b/src/core/qgis.h @@ -1483,6 +1483,7 @@ class CORE_EXPORT Qgis AddRingNotInExistingFeature, //!< The input ring doesn't have any existing ring to fit into // Split features SplitCannotSplitPoint, //!< Cannot split points + GeometryTypeHasChanged, //!< Operation has changed geometry type }; Q_ENUM( GeometryOperationResult ) diff --git a/src/gui/maptools/qgsmaptoolcapturelayergeometry.cpp b/src/gui/maptools/qgsmaptoolcapturelayergeometry.cpp index c3a10c89064..7fd9f7f0ca1 100644 --- a/src/gui/maptools/qgsmaptoolcapturelayergeometry.cpp +++ b/src/gui/maptools/qgsmaptoolcapturelayergeometry.cpp @@ -58,8 +58,8 @@ void QgsMapToolCaptureLayerGeometry::geometryCaptured( const QgsGeometry &geomet } if ( avoidIntersectionsLayers.size() > 0 ) { - const int avoidIntersectionsReturn = g.avoidIntersections( avoidIntersectionsLayers ); - if ( avoidIntersectionsReturn == 3 ) + const Qgis::GeometryOperationResult avoidIntersectionsReturn = g.avoidIntersectionsV2( avoidIntersectionsLayers ); + if ( avoidIntersectionsReturn == Qgis::GeometryOperationResult::InvalidBaseGeometry ) { emit messageEmitted( tr( "The feature has been added, but at least one geometry intersected is invalid. These geometries must be manually repaired." ), Qgis::MessageLevel::Warning ); }