Add all topological points to editable layers when reshaping

This commit is contained in:
uclaros 2025-06-13 11:10:04 +03:00
parent 6c62b930b0
commit d0d74da7a1
2 changed files with 117 additions and 23 deletions

View File

@ -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<QgsPointLocator::Match> 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<QgsMapLayer *> 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<QgsVectorLayer *>( 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();
}
}
}
}

View File

@ -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<QgsMapLayer *>() << mLayerTopo );
mCanvas->setLayers( QList<QgsMapLayer *>() << 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<QgsMapLayer *> 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 );
}