diff --git a/src/app/qgsmaptoolreshape.cpp b/src/app/qgsmaptoolreshape.cpp index 3a67d3a77d4..7acc5c7f6bd 100644 --- a/src/app/qgsmaptoolreshape.cpp +++ b/src/app/qgsmaptoolreshape.cpp @@ -24,6 +24,8 @@ #include "qgsvectorlayer.h" #include "qgisapp.h" #include "qgsmapmouseevent.h" +#include "qgsvectorlayereditutils.h" +#include "qgsmultipoint.h" QgsMapToolReshape::QgsMapToolReshape( QgsMapCanvas *canvas ) : QgsMapToolCapture( canvas, QgisApp::instance()->cadDockWidget(), QgsMapToolCapture::CaptureLine ) @@ -211,16 +213,71 @@ void QgsMapToolReshape::reshape( QgsVectorLayer *vlayer ) if ( reshapeDone ) { - // Add topological points due to snapping + // Add topological points if ( QgsProject::instance()->topologicalEditing() ) { - const QList sm = snappingMatches(); - Q_ASSERT( pts.size() == sm.size() ); - for ( int i = 0; i < sm.size(); ++i ) + //check if we need to add topological points to other layers + const QList layers = canvas()->layers( true ); + QgsFeatureRequest request = QgsFeatureRequest().setNoAttributes().setFlags( Qgis::FeatureRequestFlag::NoGeometry ).setLimit( 1 ); + QgsGeometry pointsAsGeom( new QgsMultiPoint( pts ) ); + QgsRectangle bbox = pointsAsGeom.boundingBox(); + + for ( QgsMapLayer *layer : layers ) { - if ( sm.at( i ).layer() ) + QgsVectorLayer *vectorLayer = qobject_cast( layer ); + if ( vectorLayer && vectorLayer->isEditable() && vectorLayer->isSpatial() && ( vectorLayer->geometryType() == Qgis::GeometryType::Line || vectorLayer->geometryType() == Qgis::GeometryType::Polygon ) ) { - sm.at( i ).layer()->addTopologicalPoints( pts.at( i ) ); + QgsCoordinateTransform ct; + if ( vectorLayer->crs() != vlayer->crs() ) + { + ct = QgsCoordinateTransform( vlayer->crs(), vectorLayer->crs(), vectorLayer->transformContext() ); + try + { + bbox = ct.transformBoundingBox( bbox ); + } + catch ( QgsCsException & ) + { + QgsDebugError( QStringLiteral( "Bounding box transformation failed, skipping topological points for layer %1" ).arg( vlayer->id() ) ); + continue; + } + } + bbox.grow( QgsVectorLayerEditUtils::getTopologicalSearchRadius( vectorLayer ) ); + request.setFilterRect( bbox ); + + // We check that there is actually at least one feature intersecting our geometry in the layer to avoid creating an empty edit command and calling costly addTopologicalPoint + if ( !vectorLayer->getFeatures( request ).nextFeature( f ) ) + continue; + + vectorLayer->beginEditCommand( tr( "Topological points added by Reshape" ) ); + + int returnValue = 2; + if ( vectorLayer->crs() != vlayer->crs() ) + { + try + { + // transform digitized geometry from vlayer crs to vectorLayer crs and add topological points + pointsAsGeom.transform( ct ); + returnValue = vectorLayer->addTopologicalPoints( pointsAsGeom ); + } + catch ( QgsCsException & ) + { + QgsDebugError( QStringLiteral( "transformation to vectorLayer coordinate failed" ) ); + } + } + else + { + returnValue = vectorLayer->addTopologicalPoints( pts ); + } + + if ( returnValue == 0 ) + { + vectorLayer->endEditCommand(); + } + else + { + // the layer was not modified, leave the undo buffer intact + vectorLayer->destroyEditCommand(); + } } } } diff --git a/tests/src/app/testqgsmaptoolreshape.cpp b/tests/src/app/testqgsmaptoolreshape.cpp index ae682388022..7de3a30cfc9 100644 --- a/tests/src/app/testqgsmaptoolreshape.cpp +++ b/tests/src/app/testqgsmaptoolreshape.cpp @@ -44,6 +44,7 @@ class TestQgsMapToolReshape : public QObject void testReshapeZ(); void testTopologicalEditing(); + void testTopologicalEditingNoSnap(); void testAvoidIntersectionAndTopoEdit(); void reshapeWithBindingLine(); void testWithTracing(); @@ -153,10 +154,10 @@ void TestQgsMapToolReshape::initTestCase() QCOMPARE( mLayerPolygonZ->getFeature( 1 ).geometry().asWkt(), wkt5 ); mLayerTopo->startEditing(); - const QString wkt6 = "Polygon ((0 0, 4 0, 4 4, 0 4))"; + const QString wkt6 = "Polygon ((0 0, 4 0, 4 4, 0 4, 0 0))"; QgsFeature f6; f6.setGeometry( QgsGeometry::fromWkt( wkt6 ) ); - const QString wkt7 = "Polygon ((7 0, 8 0, 8 4, 7 4))"; + const QString wkt7 = "Polygon ((7 0, 8 0, 8 4, 7 4, 7 0))"; QgsFeature f7; f7.setGeometry( QgsGeometry::fromWkt( wkt7 ) ); QgsFeatureList flistTopo; @@ -168,10 +169,10 @@ void TestQgsMapToolReshape::initTestCase() mLayerTopo2->startEditing(); QgsFeature f8; - f8.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon ((0 5, 4 5, 4 7, 0 7))" ) ) ); + f8.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon ((0 5, 4 5, 4 7, 0 7, 0 5))" ) ) ); mLayerTopo2->dataProvider()->addFeatures( QgsFeatureList() << f8 ); QCOMPARE( mLayerTopo2->featureCount(), 1 ); - QCOMPARE( mLayerTopo2->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 5, 4 5, 4 7, 0 7))" ) ); + QCOMPARE( mLayerTopo2->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 5, 4 5, 4 7, 0 7, 0 5))" ) ); mLayerLine->startEditing(); const QString wkt9 = QStringLiteral( "LineString (0 0, 10 10)" ); @@ -267,25 +268,61 @@ void TestQgsMapToolReshape::testTopologicalEditing() QgsSettingsRegistryCore::settingsDigitizingDefaultZValue->setValue( 333 ); utils.mouseClick( 4, 4, Qt::LeftButton, Qt::KeyboardModifiers(), true ); - utils.mouseClick( 7, 2, Qt::LeftButton, Qt::KeyboardModifiers(), true ); + utils.mouseClick( 8, 2, Qt::LeftButton, Qt::KeyboardModifiers(), true ); utils.mouseClick( 4, 0, Qt::LeftButton, Qt::KeyboardModifiers(), true ); utils.mouseClick( 4, 0, Qt::RightButton ); const QString wkt = "Polygon ((4 0, 8 2, 4 4, 0 4, 0 0, 4 0))"; - const QString wkt2 = "Polygon ((7 0, 8 0, 8 2, 8 4, 7 4))"; + const QString wkt2 = "Polygon ((7 0, 8 0, 8 2, 8 4, 7 4, 7 0))"; QCOMPARE( mLayerTopo->getFeature( 1 ).geometry().asWkt(), wkt ); QCOMPARE( mLayerTopo->getFeature( 2 ).geometry().asWkt(), wkt2 ); mLayerTopo->undoStack()->undo(); - QCOMPARE( mLayerTopo2->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 5, 4 5, 4 7, 0 7))" ) ); - QCOMPARE( mLayerTopo->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 0, 4 0, 4 4, 0 4))" ) ); - QCOMPARE( mLayerTopo->getFeature( 2 ).geometry().asWkt(), QStringLiteral( "Polygon ((7 0, 8 0, 8 4, 7 4))" ) ); + QCOMPARE( mLayerTopo2->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 5, 4 5, 4 7, 0 7, 0 5))" ) ); + QCOMPARE( mLayerTopo->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 0, 4 0, 4 4, 0 4, 0 0))" ) ); + QCOMPARE( mLayerTopo->getFeature( 2 ).geometry().asWkt(), QStringLiteral( "Polygon ((7 0, 8 0, 8 4, 7 4, 7 0))" ) ); QgsProject::instance()->setTopologicalEditing( topologicalEditing ); } +void TestQgsMapToolReshape::testTopologicalEditingNoSnap() +{ + QgsProject::instance()->addMapLayers( QList() << mLayerTopo ); + mCanvas->setLayers( QList() << mLayerTopo ); + + QgsSnappingConfig cfg = mCanvas->snappingUtils()->config(); + cfg.setEnabled( false ); + mCanvas->snappingUtils()->setConfig( cfg ); + + const bool topologicalEditing = QgsProject::instance()->topologicalEditing(); + QgsProject::instance()->setTopologicalEditing( true ); + mCanvas->setCurrentLayer( mLayerTopo ); + TestQgsMapToolAdvancedDigitizingUtils utils( mCaptureTool ); + + utils.mouseClick( 4, 4, Qt::LeftButton, Qt::KeyboardModifiers(), true ); + utils.mouseClick( 8, 2, Qt::LeftButton, Qt::KeyboardModifiers(), true ); + utils.mouseClick( 4, 0, Qt::LeftButton, Qt::KeyboardModifiers(), true ); + utils.mouseClick( 4, 0, Qt::RightButton ); + + const QString wkt = "Polygon ((4 0, 8 2, 4 4, 0 4, 0 0, 4 0))"; + const QString wkt2 = "Polygon ((7 0, 8 0, 8 2, 8 4, 7 4, 7 0))"; + + QCOMPARE( mLayerTopo->getFeature( 1 ).geometry().asWkt(), wkt ); + QCOMPARE( mLayerTopo->getFeature( 2 ).geometry().asWkt(), wkt2 ); + + mLayerTopo->undoStack()->undo(); + + QCOMPARE( mLayerTopo2->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 5, 4 5, 4 7, 0 7, 0 5))" ) ); + QCOMPARE( mLayerTopo->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 0, 4 0, 4 4, 0 4, 0 0))" ) ); + QCOMPARE( mLayerTopo->getFeature( 2 ).geometry().asWkt(), QStringLiteral( "Polygon ((7 0, 8 0, 8 4, 7 4, 7 0))" ) ); + + QgsProject::instance()->setTopologicalEditing( topologicalEditing ); + cfg.setEnabled( true ); + mCanvas->snappingUtils()->setConfig( cfg ); +} + void TestQgsMapToolReshape::testAvoidIntersectionAndTopoEdit() { QList layers = { mLayerTopo, mLayerTopo2 }; @@ -312,15 +349,15 @@ void TestQgsMapToolReshape::testAvoidIntersectionAndTopoEdit() utils.mouseClick( 4, 5, Qt::RightButton ); QCOMPARE( mLayerTopo2->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 5, 0 7, 4 7, 4 5, 3 4, 1 4, 0 5))" ) ); - QCOMPARE( mLayerTopo->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 0, 4 0, 4 4, 3 4, 1 4, 0 4))" ) ); - QCOMPARE( mLayerTopo->getFeature( 2 ).geometry().asWkt(), QStringLiteral( "Polygon ((7 0, 8 0, 8 4, 7 4))" ) ); + QCOMPARE( mLayerTopo->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 0, 4 0, 4 4, 3 4, 1 4, 0 4, 0 0))" ) ); + QCOMPARE( mLayerTopo->getFeature( 2 ).geometry().asWkt(), QStringLiteral( "Polygon ((7 0, 8 0, 8 4, 7 4, 7 0))" ) ); mLayerTopo2->undoStack()->undo(); mLayerTopo->undoStack()->undo(); - QCOMPARE( mLayerTopo2->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 5, 4 5, 4 7, 0 7))" ) ); - QCOMPARE( mLayerTopo->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 0, 4 0, 4 4, 0 4))" ) ); - QCOMPARE( mLayerTopo->getFeature( 2 ).geometry().asWkt(), QStringLiteral( "Polygon ((7 0, 8 0, 8 4, 7 4))" ) ); + QCOMPARE( mLayerTopo2->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 5, 4 5, 4 7, 0 7, 0 5))" ) ); + QCOMPARE( mLayerTopo->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 0, 4 0, 4 4, 0 4, 0 0))" ) ); + QCOMPARE( mLayerTopo->getFeature( 2 ).geometry().asWkt(), QStringLiteral( "Polygon ((7 0, 8 0, 8 4, 7 4, 7 0))" ) ); QgsProject::instance()->setTopologicalEditing( topologicalEditing ); QgsProject::instance()->setAvoidIntersectionsMode( mode ); @@ -417,13 +454,13 @@ void TestQgsMapToolReshape::testWithTracing() utils.mouseClick( 7, 4, Qt::LeftButton, Qt::KeyboardModifiers(), true ); utils.mouseClick( 8, 5, Qt::RightButton ); - QCOMPARE( mLayerTopo->getFeature( 1 ).geometry().asWkt(), "Polygon ((0 0, 4 0, 4 4, 0 4))" ); + QCOMPARE( mLayerTopo->getFeature( 1 ).geometry().asWkt(), "Polygon ((0 0, 4 0, 4 4, 0 4, 0 0))" ); QCOMPARE( mLayerTopo->getFeature( 2 ).geometry().asWkt(), "Polygon ((7 0, 8 0, 8 4, 7 4, 4 4, 4 0, 7 0))" ); mLayerTopo->undoStack()->undo(); - QCOMPARE( mLayerTopo->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 0, 4 0, 4 4, 0 4))" ) ); - QCOMPARE( mLayerTopo->getFeature( 2 ).geometry().asWkt(), QStringLiteral( "Polygon ((7 0, 8 0, 8 4, 7 4))" ) ); + QCOMPARE( mLayerTopo->getFeature( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 0, 4 0, 4 4, 0 4, 0 0))" ) ); + QCOMPARE( mLayerTopo->getFeature( 2 ).geometry().asWkt(), QStringLiteral( "Polygon ((7 0, 8 0, 8 4, 7 4, 7 0))" ) ); QgsProject::instance()->setTopologicalEditing( topologicalEditing ); }