Skip to content

Commit

Permalink
Merge pull request #56873 from GispoCoding/fix_50433
Browse files Browse the repository at this point in the history
Make avoid overlap work with more digitizing tools (fixes #50433)
  • Loading branch information
troopa81 committed Apr 10, 2024
2 parents c4f94e8 + 18e78a1 commit 40d2260
Show file tree
Hide file tree
Showing 9 changed files with 506 additions and 0 deletions.
25 changes: 25 additions & 0 deletions src/app/qgsmaptoolmovefeature.cpp
Expand Up @@ -15,6 +15,7 @@

#include "qgisapp.h"
#include "qgsadvanceddigitizingdockwidget.h"
#include "qgsavoidintersectionsoperation.h"
#include "qgsfeatureiterator.h"
#include "qgsgeometry.h"
#include "qgslogger.h"
Expand Down Expand Up @@ -217,6 +218,13 @@ void QgsMapToolMoveFeature::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
request.setFilterFids( mMovedFeatures ).setNoAttributes();
QgsFeatureIterator fi = vlayer->getFeatures( request );
QgsFeature f;

QgsAvoidIntersectionsOperation avoidIntersections;
connect( &avoidIntersections, &QgsAvoidIntersectionsOperation::messageEmitted, this, &QgsMapTool::messageEmitted );

// when removing intersections don't check for intersections with selected features
const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > ignoreFeatures {{ vlayer, mMovedFeatures }};

while ( fi.nextFeature( f ) )
{
if ( !f.hasGeometry() )
Expand All @@ -227,6 +235,23 @@ void QgsMapToolMoveFeature::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
continue;

const QgsFeatureId id = f.id();

if ( vlayer->geometryType() == Qgis::GeometryType::Polygon )
{
const QgsAvoidIntersectionsOperation::Result res = avoidIntersections.apply( vlayer, id, geom, ignoreFeatures );

if ( res.operationResult == Qgis::GeometryOperationResult::InvalidInputGeometryType || geom.isEmpty() )
{
const QString errorMessage = ( geom.isEmpty() ) ?
tr( "The feature cannot be moved because the resulting geometry would be empty" ) :
tr( "An error was reported during intersection removal" );

emit messageEmitted( errorMessage, Qgis::MessageLevel::Warning );
vlayer->destroyEditCommand();
return;
}
}

vlayer->changeGeometry( id, geom );

if ( QgsProject::instance()->topologicalEditing() )
Expand Down
25 changes: 25 additions & 0 deletions src/app/qgsmaptooloffsetcurve.cpp
Expand Up @@ -17,6 +17,7 @@
#include <QGridLayout>
#include <QLabel>

#include "qgsavoidintersectionsoperation.h"
#include "qgsdoublespinbox.h"
#include "qgsfeatureiterator.h"
#include "qgsmaptooloffsetcurve.h"
Expand Down Expand Up @@ -346,6 +347,30 @@ void QgsMapToolOffsetCurve::applyOffset( double offset, Qt::KeyboardModifiers mo

destLayer->beginEditCommand( tr( "Offset curve" ) );

QgsAvoidIntersectionsOperation avoidIntersections;

connect( &avoidIntersections, &QgsAvoidIntersectionsOperation::messageEmitted, this, &QgsMapTool::messageEmitted );

const QSet<QgsFeatureId> ignoredFeatures = ( modifiers & Qt::ControlModifier ) ?
QSet<QgsFeatureId>() :
QSet<QgsFeatureId>( {mModifiedFeature} );

const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > ignoreFeatures = {{ destLayer, {ignoredFeatures} }};

const QgsAvoidIntersectionsOperation::Result res = avoidIntersections.apply( destLayer, mModifiedFeature, mModifiedGeometry, ignoreFeatures );

if ( res.operationResult == Qgis::GeometryOperationResult::InvalidInputGeometryType || mModifiedGeometry.isEmpty() )
{
const QString errorMessage = ( mModifiedGeometry.isEmpty() ) ?
tr( "The feature cannot be modified because the resulting geometry would be empty" ) :
tr( "An error was reported during intersection removal" );

emit messageEmitted( errorMessage, Qgis::MessageLevel::Warning );
destLayer->destroyEditCommand();
cancel();
return;
}

bool editOk = true;
if ( !mCtrlHeldOnFirstClick && !( modifiers & Qt::ControlModifier ) )
{
Expand Down
27 changes: 27 additions & 0 deletions src/app/qgsmaptoolrotatefeature.cpp
Expand Up @@ -23,6 +23,7 @@
#include <cmath>

#include "qgsadvanceddigitizingdockwidget.h"
#include "qgsavoidintersectionsoperation.h"
#include "qgsmaptoolrotatefeature.h"
#include "qgsfeatureiterator.h"
#include "qgsgeometry.h"
Expand Down Expand Up @@ -414,11 +415,37 @@ void QgsMapToolRotateFeature::applyRotation( double rotation )
request.setFilterFids( mRotatedFeatures ).setNoAttributes();
QgsFeatureIterator fi = vlayer->getFeatures( request );
QgsFeature f;

QgsAvoidIntersectionsOperation avoidIntersections;
connect( &avoidIntersections, &QgsAvoidIntersectionsOperation::messageEmitted, this, &QgsMapTool::messageEmitted );

// when removing intersections don't check for intersections with selected features
const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > ignoreFeatures {{vlayer, mRotatedFeatures}};

while ( fi.nextFeature( f ) )
{
const QgsFeatureId id = f.id();
QgsGeometry geom = f.geometry();
geom.rotate( mRotation, anchorPoint );

if ( vlayer->geometryType() == Qgis::GeometryType::Polygon )
{
const QgsAvoidIntersectionsOperation::Result res = avoidIntersections.apply( vlayer, id, geom, ignoreFeatures );

if ( res.operationResult == Qgis::GeometryOperationResult::InvalidInputGeometryType || geom.isEmpty() )
{
const QString errorMessage = ( geom.isEmpty() ) ?
tr( "The feature cannot be rotated because the resulting geometry would be empty" ) :
tr( "An error was reported during intersection removal" );

emit messageEmitted( errorMessage, Qgis::MessageLevel::Warning );
vlayer->destroyEditCommand();
deleteRotationWidget();
deleteRubberband();
return;
}
}

vlayer->changeGeometry( id, geom );
}

Expand Down
27 changes: 27 additions & 0 deletions src/app/qgsmaptoolscalefeature.cpp
Expand Up @@ -23,6 +23,7 @@
#include <cmath>

#include "qgsadvanceddigitizingdockwidget.h"
#include "qgsavoidintersectionsoperation.h"
#include "qgsmaptoolscalefeature.h"
#include "qgsfeatureiterator.h"
#include "qgsgeometry.h"
Expand Down Expand Up @@ -368,6 +369,13 @@ void QgsMapToolScaleFeature::applyScaling( double scale )
request.setFilterFids( mScaledFeatures ).setNoAttributes();
QgsFeatureIterator fi = vlayer->getFeatures( request );
QgsFeature feat;

QgsAvoidIntersectionsOperation avoidIntersections;
connect( &avoidIntersections, &QgsAvoidIntersectionsOperation::messageEmitted, this, &QgsMapTool::messageEmitted );

// when removing intersections don't check for intersections with selected features
const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > ignoreFeatures {{vlayer, mScaledFeatures}};

while ( fi.nextFeature( feat ) )
{
if ( !feat.hasGeometry() )
Expand All @@ -378,6 +386,25 @@ void QgsMapToolScaleFeature::applyScaling( double scale )
continue;

const QgsFeatureId id = feat.id();

if ( vlayer->geometryType() == Qgis::GeometryType::Polygon )
{
const QgsAvoidIntersectionsOperation::Result res = avoidIntersections.apply( vlayer, id, geom, ignoreFeatures );

if ( res.operationResult == Qgis::GeometryOperationResult::InvalidInputGeometryType || geom.isEmpty() )
{
const QString errorMessage = ( geom.isEmpty() ) ?
tr( "The feature cannot be scaled because the resulting geometry would be empty" ) :
tr( "An error was reported during intersection removal" );

emit messageEmitted( errorMessage, Qgis::MessageLevel::Warning );
vlayer->destroyEditCommand();
deleteScalingWidget();
deleteRubberband();
return;
}
}

vlayer->changeGeometry( id, geom );
}

Expand Down
1 change: 1 addition & 0 deletions tests/src/app/CMakeLists.txt
Expand Up @@ -41,6 +41,7 @@ set(TESTS
testqgsmaptoolregularpolygon.cpp
testqgsmaptoolsplitparts.cpp
testqgsmaptoolsplitfeatures.cpp
testqgsmaptooloffsetcurve.cpp
testqgsmeasuretool.cpp
testqgsmeasurebearingtool.cpp
testqgsvertexeditor.cpp
Expand Down
25 changes: 25 additions & 0 deletions tests/src/app/testqgsmaptoolmovefeature.cpp
Expand Up @@ -46,6 +46,7 @@ class TestQgsMapToolMoveFeature: public QObject

void testMoveFeature();
void testTopologicalMoveFeature();
void testAvoidIntersectionAndTopoEdit();

private:
QgisApp *mQgisApp = nullptr;
Expand Down Expand Up @@ -163,5 +164,29 @@ void TestQgsMapToolMoveFeature::testTopologicalMoveFeature()
QgsProject::instance()->setTopologicalEditing( topologicalEditing );
}

void TestQgsMapToolMoveFeature::testAvoidIntersectionAndTopoEdit()
{
const bool topologicalEditing = QgsProject::instance()->topologicalEditing();
const Qgis::AvoidIntersectionsMode mode( QgsProject::instance()->avoidIntersectionsMode() );

QgsProject::instance()->setAvoidIntersectionsMode( Qgis::AvoidIntersectionsMode::AvoidIntersectionsCurrentLayer );
QgsProject::instance()->setTopologicalEditing( true );

TestQgsMapToolAdvancedDigitizingUtils utils( mCaptureTool );

utils.mouseClick( 1, 1, Qt::LeftButton, Qt::KeyboardModifiers(), true );
utils.mouseClick( 2.5, 1, Qt::LeftButton, Qt::KeyboardModifiers(), true );

const QString wkt1 = "Polygon ((1.5 1, 2 1, 2 0, 1.5 0, 1.5 1))";
QCOMPARE( mLayerBase->getFeature( 1 ).geometry().asWkt(), wkt1 );
const QString wkt2 = "Polygon ((2 0, 2 1, 2 5, 3 5, 3 0, 2 0))";
QCOMPARE( mLayerBase->getFeature( 2 ).geometry().asWkt(), wkt2 );

mLayerBase->undoStack()->undo();

QgsProject::instance()->setTopologicalEditing( topologicalEditing );
QgsProject::instance()->setAvoidIntersectionsMode( mode );
}

QGSTEST_MAIN( TestQgsMapToolMoveFeature )
#include "testqgsmaptoolmovefeature.moc"

0 comments on commit 40d2260

Please sign in to comment.