From 5bc83af75b65aaad3cd24fcefc5ea984da8338bf Mon Sep 17 00:00:00 2001 From: Juho Ervasti Date: Wed, 5 Jun 2024 12:09:21 +0300 Subject: [PATCH 1/7] Test topo editing with features from sep. layers --- .../testqgsmaptooladdfeatureline.cpp | 82 ++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/tests/src/app/maptooladdfeatureline/testqgsmaptooladdfeatureline.cpp b/tests/src/app/maptooladdfeatureline/testqgsmaptooladdfeatureline.cpp index 51c22e9e6226..48e0d649386d 100644 --- a/tests/src/app/maptooladdfeatureline/testqgsmaptooladdfeatureline.cpp +++ b/tests/src/app/maptooladdfeatureline/testqgsmaptooladdfeatureline.cpp @@ -78,6 +78,7 @@ class TestQgsMapToolAddFeatureLine : public QObject void testStreamTolerance(); void testWithTopologicalEditingDifferentCanvasCrs(); void testWithTopologicalEditingWIthDiffLayerWithDiffCrs(); + void testWithTopologicalEditingWithMoreThanOneLayer(); private: QgisApp *mQgisApp = nullptr; @@ -94,8 +95,13 @@ class TestQgsMapToolAddFeatureLine : public QObject QgsVectorLayer *mLayerSelfSnapLine = nullptr; QgsVectorLayer *mLayerCRS3946Line = nullptr; QgsVectorLayer *mLayerCRS3945Line = nullptr; + QgsVectorLayer *mLayerTopo1 = nullptr; + QgsVectorLayer *mLayerTopo2 = nullptr; + QgsVectorLayer *mLayerTopo3 = nullptr; QgsFeatureId mFidLineF1 = 0; QgsFeatureId mFidCurvedF1 = 0; + QgsFeatureId mFidTopoLineF1 = 0; + QgsFeatureId mFidTopoLineF2 = 0; }; TestQgsMapToolAddFeatureLine::TestQgsMapToolAddFeatureLine() = default; @@ -228,8 +234,44 @@ void TestQgsMapToolAddFeatureLine::initTestCase() QgsProject::instance()->addMapLayers( QList() << mLayerCRS3945Line ); mLayerCRS3945Line->startEditing(); + // make layers with overlapping features to test topo editing + mLayerTopo1 = new QgsVectorLayer( QStringLiteral( "LineString?crs=EPSG:27700" ), QStringLiteral( "layer line topo1" ), QStringLiteral( "memory" ) ); + QVERIFY( mLayerTopo1 ->isValid() ); + QgsProject::instance()->addMapLayers( QList() << mLayerTopo1 ); + mLayerTopo1->startEditing(); + + QgsFeature topoLineF1; + topoLineF1.setGeometry( QgsGeometry::fromWkt( "LineString (1 6, 1 7, 2 7, 2 6)" ) ); + + mLayerTopo1->addFeature( topoLineF1 ); + mFidTopoLineF1 = topoLineF1.id(); + QCOMPARE( mLayerTopo1->featureCount(), ( long )1 ); + + // just one added feature + QCOMPARE( mLayerTopo1->undoStack()->index(), 1 ); + + mLayerTopo2 = new QgsVectorLayer( QStringLiteral( "LineString?crs=EPSG:27700" ), QStringLiteral( "layer line topo2" ), QStringLiteral( "memory" ) ); + QVERIFY( mLayerTopo2 ->isValid() ); + QgsProject::instance()->addMapLayers( QList() << mLayerTopo2 ); + mLayerTopo2->startEditing(); + + QgsFeature topoLineF2; + topoLineF2.setGeometry( QgsGeometry::fromWkt( "LineString (0 7, 3 7)" ) ); + + mLayerTopo2->addFeature( topoLineF2 ); + mFidTopoLineF2 = topoLineF2.id(); + QCOMPARE( mLayerTopo2->featureCount(), ( long )1 ); + + // just one added feature + QCOMPARE( mLayerTopo2->undoStack()->index(), 1 ); + + mLayerTopo3 = new QgsVectorLayer( QStringLiteral( "LineString?crs=EPSG:27700" ), QStringLiteral( "layer line topo3" ), QStringLiteral( "memory" ) ); + QVERIFY( mLayerTopo3 ->isValid() ); + mLayerTopo3->startEditing(); + QgsProject::instance()->addMapLayers( QList() << mLayerTopo3 ); + // add layers to canvas - mCanvas->setLayers( QList() << mLayerLine << mLayerLineCurved << mLayerLineCurvedOffset << mLayerLineZ << mLayerLine2D << mLayerSelfSnapLine << mLayerCRS3946Line << mLayerCRS3945Line ); + mCanvas->setLayers( QList() << mLayerLine << mLayerLineCurved << mLayerLineCurvedOffset << mLayerLineZ << mLayerLine2D << mLayerSelfSnapLine << mLayerCRS3946Line << mLayerCRS3945Line << mLayerTopo1 << mLayerTopo2 ); mCanvas->setSnappingUtils( new QgsMapCanvasSnappingUtils( mCanvas, this ) ); // create the tool @@ -1070,6 +1112,44 @@ void TestQgsMapToolAddFeatureLine::testWithTopologicalEditingWIthDiffLayerWithDi snapConfig.project()->setTopologicalEditing( topologicalEditing ); } +void TestQgsMapToolAddFeatureLine::testWithTopologicalEditingWithMoreThanOneLayer() +{ + TestQgsMapToolAdvancedDigitizingUtils utils( mCaptureTool ); + + mCanvas->setCurrentLayer( mLayerTopo3 ); + mCaptureTool->setLayer( mLayerTopo3 ); + + QgsSnappingConfig snapConfig = mCanvas->snappingUtils()->config(); + snapConfig.setEnabled( true ); + snapConfig.setMode( Qgis::SnappingMode::AllLayers ); + snapConfig.setTypeFlag( Qgis::SnappingType::Segment ); + bool topologicalEditing = snapConfig.project()->topologicalEditing(); + snapConfig.project()->setTopologicalEditing( true ); + mCanvas->snappingUtils()->setConfig( snapConfig ); + + const QSet oldFids = utils.existingFeatureIds(); + utils.mouseMove( 2, 5 ); + utils.mouseClick( 2, 5, Qt::LeftButton ); + utils.mouseMove( 1.5, 7 ); + utils.mouseClick( 1.5, 7, Qt::LeftButton ); + utils.mouseClick( 1.5, 7, Qt::RightButton ); + + const QgsFeatureId newFid = utils.newFeatureId( oldFids ); + + const QString wkt1 = "LineString (1 6, 1 7, 1.5 7, 2 7, 2 6)"; + QCOMPARE( mLayerTopo1->getFeature( mFidTopoLineF1 ).geometry(), QgsGeometry::fromWkt( wkt1 ) ); + + const QString wkt2 = "LineString (0 7, 1.5 7, 3 7)"; + QCOMPARE( mLayerTopo2->getFeature( mFidTopoLineF2 ).geometry(), QgsGeometry::fromWkt( wkt2 ) ); + + const QString wkt3 = "LineString (2 5, 1.5 7)"; + QCOMPARE( mLayerTopo3->getFeature( newFid ).geometry(), QgsGeometry::fromWkt( wkt3 ) ); + + mLayerTopo1->undoStack()->undo(); + mLayerTopo2->undoStack()->undo(); + mLayerTopo3->undoStack()->undo(); + snapConfig.project()->setTopologicalEditing( topologicalEditing ); +} QGSTEST_MAIN( TestQgsMapToolAddFeatureLine ) #include "testqgsmaptooladdfeatureline.moc" From 1765ebc1b86bda43cc00309b9995b57317f97d45 Mon Sep 17 00:00:00 2001 From: Juho Ervasti Date: Mon, 10 Jun 2024 08:52:44 +0300 Subject: [PATCH 2/7] Add topo point to editable layers at snapped point Fixes #56689 --- src/app/qgsmaptooladdfeature.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/app/qgsmaptooladdfeature.cpp b/src/app/qgsmaptooladdfeature.cpp index b7a9a4a2699d..e8b59e883dd5 100644 --- a/src/app/qgsmaptooladdfeature.cpp +++ b/src/app/qgsmaptooladdfeature.cpp @@ -122,18 +122,31 @@ void QgsMapToolAddFeature::featureDigitized( const QgsFeature &feature ) if ( topologicalEditing ) { const QList sm = snappingMatches(); + + QList layers; + for ( QgsVectorLayer *lyr : vlayer->project()->layers() ) + { + if ( lyr->isEditable() && lyr != vlayer ) + layers.append( lyr ); + } + for ( int i = 0; i < sm.size() ; ++i ) { - if ( sm.at( i ).layer() && sm.at( i ).layer()->isEditable() && sm.at( i ).layer() != vlayer ) + QgsVectorLayer *snapLayer = sm.at( i ).layer(); + if ( !snapLayer || snapLayer == vlayer ) + continue; + + for ( QgsVectorLayer *layer : layers ) { QgsPoint topologicalPoint{ feature.geometry().vertexAt( i ) }; - if ( sm.at( i ).layer()->crs() != vlayer->crs() ) + + if ( layer->crs() != vlayer->crs() ) { - // transform digitized geometry from vlayer crs to snapping layer crs and add topological point + // transform digitized geometry from vlayer crs to layer crs and add topological point try { - topologicalPoint.transform( QgsCoordinateTransform( vlayer->crs(), sm.at( i ).layer()->crs(), sm.at( i ).layer()->transformContext() ) ); - sm.at( i ).layer()->addTopologicalPoints( topologicalPoint ); + topologicalPoint.transform( QgsCoordinateTransform( vlayer->crs(), layer->crs(), layer->transformContext() ) ); + layer->addTopologicalPoints( topologicalPoint ); } catch ( QgsCsException &cse ) { @@ -143,7 +156,7 @@ void QgsMapToolAddFeature::featureDigitized( const QgsFeature &feature ) } else { - sm.at( i ).layer()->addTopologicalPoints( topologicalPoint ); + layer->addTopologicalPoints( topologicalPoint ); } } } From 967cde7d20748991546e97492769c5bd57d89453 Mon Sep 17 00:00:00 2001 From: Juho Ervasti Date: Mon, 10 Jun 2024 09:58:40 +0300 Subject: [PATCH 3/7] Add comments --- src/app/qgsmaptooladdfeature.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/qgsmaptooladdfeature.cpp b/src/app/qgsmaptooladdfeature.cpp index e8b59e883dd5..9b8ec19b624d 100644 --- a/src/app/qgsmaptooladdfeature.cpp +++ b/src/app/qgsmaptooladdfeature.cpp @@ -146,6 +146,7 @@ void QgsMapToolAddFeature::featureDigitized( const QgsFeature &feature ) try { topologicalPoint.transform( QgsCoordinateTransform( vlayer->crs(), layer->crs(), layer->transformContext() ) ); + // the function adds a topological point if a segment overlaps it layer->addTopologicalPoints( topologicalPoint ); } catch ( QgsCsException &cse ) @@ -156,6 +157,7 @@ void QgsMapToolAddFeature::featureDigitized( const QgsFeature &feature ) } else { + // the function adds a topological point if a segment overlaps it layer->addTopologicalPoints( topologicalPoint ); } } From 8b8374b98cf30717e809a56f32ff66e8eb41c380 Mon Sep 17 00:00:00 2001 From: Juho Ervasti Date: Mon, 10 Jun 2024 16:13:17 +0300 Subject: [PATCH 4/7] Add whole geom of digitized feature as topo points --- src/app/qgsmaptooladdfeature.cpp | 47 ++++++++++++-------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/src/app/qgsmaptooladdfeature.cpp b/src/app/qgsmaptooladdfeature.cpp index 9b8ec19b624d..018d0d743ce3 100644 --- a/src/app/qgsmaptooladdfeature.cpp +++ b/src/app/qgsmaptooladdfeature.cpp @@ -121,46 +121,35 @@ void QgsMapToolAddFeature::featureDigitized( const QgsFeature &feature ) } if ( topologicalEditing ) { - const QList sm = snappingMatches(); + const QVector layers = vlayer->project()->layers(); - QList layers; - for ( QgsVectorLayer *lyr : vlayer->project()->layers() ) + for ( QgsVectorLayer *layer : layers ) { - if ( lyr->isEditable() && lyr != vlayer ) - layers.append( lyr ); - } + if ( !layer->isEditable() || layer == vlayer ) + continue; - for ( int i = 0; i < sm.size() ; ++i ) - { - QgsVectorLayer *snapLayer = sm.at( i ).layer(); - if ( !snapLayer || snapLayer == vlayer ) + if ( !( layer->geometryType() == Qgis::GeometryType::Polygon || layer->geometryType() == Qgis::GeometryType::Line ) ) continue; - for ( QgsVectorLayer *layer : layers ) + if ( layer->crs() != vlayer->crs() ) { - QgsPoint topologicalPoint{ feature.geometry().vertexAt( i ) }; - - if ( layer->crs() != vlayer->crs() ) + QgsGeometry transformedGeom = feature.geometry(); + try { - // transform digitized geometry from vlayer crs to layer crs and add topological point - try - { - topologicalPoint.transform( QgsCoordinateTransform( vlayer->crs(), layer->crs(), layer->transformContext() ) ); - // the function adds a topological point if a segment overlaps it - layer->addTopologicalPoints( topologicalPoint ); - } - catch ( QgsCsException &cse ) - { - Q_UNUSED( cse ) - QgsDebugError( QStringLiteral( "transformation to layer coordinate failed" ) ); - } + // transform digitized geometry from vlayer crs to layer crs and add topological points + transformedGeom.transform( QgsCoordinateTransform( vlayer->crs(), layer->crs(), layer->transformContext() ) ); + layer->addTopologicalPoints( transformedGeom ); } - else + catch ( QgsCsException &cse ) { - // the function adds a topological point if a segment overlaps it - layer->addTopologicalPoints( topologicalPoint ); + Q_UNUSED( cse ) + QgsDebugError( QStringLiteral( "transformation to layer coordinate failed" ) ); } } + else + { + layer->addTopologicalPoints( feature.geometry() ); + } } vlayer->addTopologicalPoints( feature.geometry() ); } From a00d39e6439418840e03df398ee6fdcdd710692a Mon Sep 17 00:00:00 2001 From: Juho Ervasti Date: Mon, 10 Jun 2024 16:26:14 +0300 Subject: [PATCH 5/7] Add edit command --- src/app/qgsmaptooladdfeature.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/app/qgsmaptooladdfeature.cpp b/src/app/qgsmaptooladdfeature.cpp index 018d0d743ce3..1f55de47181c 100644 --- a/src/app/qgsmaptooladdfeature.cpp +++ b/src/app/qgsmaptooladdfeature.cpp @@ -131,6 +131,8 @@ void QgsMapToolAddFeature::featureDigitized( const QgsFeature &feature ) if ( !( layer->geometryType() == Qgis::GeometryType::Polygon || layer->geometryType() == Qgis::GeometryType::Line ) ) continue; + layer->beginEditCommand( tr( "Topological points added by 'Add Feature'" ) ); + if ( layer->crs() != vlayer->crs() ) { QgsGeometry transformedGeom = feature.geometry(); @@ -144,12 +146,16 @@ void QgsMapToolAddFeature::featureDigitized( const QgsFeature &feature ) { Q_UNUSED( cse ) QgsDebugError( QStringLiteral( "transformation to layer coordinate failed" ) ); + layer->destroyEditCommand(); + continue; } } else { layer->addTopologicalPoints( feature.geometry() ); } + + layer->endEditCommand(); } vlayer->addTopologicalPoints( feature.geometry() ); } From 4da7b751345fa8383105073f54a3814db0530ca7 Mon Sep 17 00:00:00 2001 From: Juho Ervasti Date: Mon, 10 Jun 2024 16:34:55 +0300 Subject: [PATCH 6/7] Destroy edit command if no points were added --- src/app/qgsmaptooladdfeature.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/app/qgsmaptooladdfeature.cpp b/src/app/qgsmaptooladdfeature.cpp index 1f55de47181c..ea2a48129487 100644 --- a/src/app/qgsmaptooladdfeature.cpp +++ b/src/app/qgsmaptooladdfeature.cpp @@ -133,6 +133,7 @@ void QgsMapToolAddFeature::featureDigitized( const QgsFeature &feature ) layer->beginEditCommand( tr( "Topological points added by 'Add Feature'" ) ); + int res; if ( layer->crs() != vlayer->crs() ) { QgsGeometry transformedGeom = feature.geometry(); @@ -140,7 +141,7 @@ void QgsMapToolAddFeature::featureDigitized( const QgsFeature &feature ) { // transform digitized geometry from vlayer crs to layer crs and add topological points transformedGeom.transform( QgsCoordinateTransform( vlayer->crs(), layer->crs(), layer->transformContext() ) ); - layer->addTopologicalPoints( transformedGeom ); + res = layer->addTopologicalPoints( transformedGeom ); } catch ( QgsCsException &cse ) { @@ -152,10 +153,13 @@ void QgsMapToolAddFeature::featureDigitized( const QgsFeature &feature ) } else { - layer->addTopologicalPoints( feature.geometry() ); + res = layer->addTopologicalPoints( feature.geometry() ); } - layer->endEditCommand(); + if ( res == 0 ) // i.e. if any points were added + layer->endEditCommand(); + else + layer->destroyEditCommand(); } vlayer->addTopologicalPoints( feature.geometry() ); } From af1e04a8e16c5f04ef17041ce25831f75f7464c3 Mon Sep 17 00:00:00 2001 From: Juho Ervasti Date: Tue, 11 Jun 2024 13:52:58 +0300 Subject: [PATCH 7/7] Address review - Don't differentiate current layer - Get layers from canvas, not project - Init result variable at failure state --- src/app/qgsmaptooladdfeature.cpp | 33 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/app/qgsmaptooladdfeature.cpp b/src/app/qgsmaptooladdfeature.cpp index ea2a48129487..c21f5bb9ee63 100644 --- a/src/app/qgsmaptooladdfeature.cpp +++ b/src/app/qgsmaptooladdfeature.cpp @@ -121,47 +121,46 @@ void QgsMapToolAddFeature::featureDigitized( const QgsFeature &feature ) } if ( topologicalEditing ) { - const QVector layers = vlayer->project()->layers(); + const QList layers = canvas()->layers( true ); - for ( QgsVectorLayer *layer : layers ) + for ( QgsMapLayer *layer : layers ) { - if ( !layer->isEditable() || layer == vlayer ) + QgsVectorLayer *vectorLayer = qobject_cast( layer ); + + if ( !vectorLayer || !vectorLayer->isEditable() ) continue; - if ( !( layer->geometryType() == Qgis::GeometryType::Polygon || layer->geometryType() == Qgis::GeometryType::Line ) ) + if ( !( vectorLayer->geometryType() == Qgis::GeometryType::Polygon || vectorLayer->geometryType() == Qgis::GeometryType::Line ) ) continue; - layer->beginEditCommand( tr( "Topological points added by 'Add Feature'" ) ); + vectorLayer->beginEditCommand( tr( "Topological points added by 'Add Feature'" ) ); - int res; - if ( layer->crs() != vlayer->crs() ) + int res = 2; + if ( vectorLayer->crs() != vlayer->crs() ) { QgsGeometry transformedGeom = feature.geometry(); try { - // transform digitized geometry from vlayer crs to layer crs and add topological points - transformedGeom.transform( QgsCoordinateTransform( vlayer->crs(), layer->crs(), layer->transformContext() ) ); - res = layer->addTopologicalPoints( transformedGeom ); + // transform digitized geometry from vlayer crs to vectorLayer crs and add topological points + transformedGeom.transform( QgsCoordinateTransform( vlayer->crs(), vectorLayer->crs(), vectorLayer->transformContext() ) ); + res = vectorLayer->addTopologicalPoints( transformedGeom ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ) - QgsDebugError( QStringLiteral( "transformation to layer coordinate failed" ) ); - layer->destroyEditCommand(); - continue; + QgsDebugError( QStringLiteral( "transformation to vectorLayer coordinate failed" ) ); } } else { - res = layer->addTopologicalPoints( feature.geometry() ); + res = vectorLayer->addTopologicalPoints( feature.geometry() ); } if ( res == 0 ) // i.e. if any points were added - layer->endEditCommand(); + vectorLayer->endEditCommand(); else - layer->destroyEditCommand(); + vectorLayer->destroyEditCommand(); } - vlayer->addTopologicalPoints( feature.geometry() ); } } }