mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-05 00:09:32 -04:00
Merge pull request #62295 from uclaros/fix-reshape-topological
Add all topological points to all editable layers when reshaping
This commit is contained in:
commit
69fabc24f8
@ -343,6 +343,7 @@ Merge features into a single one.
|
||||
%End
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
|
@ -343,6 +343,7 @@ Merge features into a single one.
|
||||
%End
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
|
@ -144,68 +144,8 @@ void QgsMapToolAddFeature::featureDigitized( const QgsFeature &feature )
|
||||
}
|
||||
if ( topologicalEditing )
|
||||
{
|
||||
QgsFeatureRequest request = QgsFeatureRequest().setNoAttributes().setFlags( Qgis::FeatureRequestFlag::NoGeometry ).setLimit( 1 );
|
||||
const QgsRectangle bbox = feature.geometry().boundingBox();
|
||||
const QList<QgsMapLayer *> layers = canvas()->layers( true );
|
||||
|
||||
for ( QgsMapLayer *layer : layers )
|
||||
{
|
||||
QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
|
||||
QgsRectangle searchRect;
|
||||
QgsFeature f;
|
||||
QgsCoordinateTransform transform;
|
||||
|
||||
if ( !vectorLayer || !vectorLayer->isEditable() )
|
||||
continue;
|
||||
|
||||
if ( !( vectorLayer->geometryType() == Qgis::GeometryType::Polygon || vectorLayer->geometryType() == Qgis::GeometryType::Line ) )
|
||||
continue;
|
||||
|
||||
if ( vectorLayer->crs() == vlayer->crs() )
|
||||
{
|
||||
searchRect = QgsRectangle( bbox );
|
||||
}
|
||||
else
|
||||
{
|
||||
transform = QgsCoordinateTransform( vlayer->crs(), vectorLayer->crs(), vectorLayer->transformContext() );
|
||||
searchRect = transform.transformBoundingBox( bbox );
|
||||
}
|
||||
|
||||
searchRect.grow( QgsVectorLayerEditUtils::getTopologicalSearchRadius( vectorLayer ) );
|
||||
request.setFilterRect( searchRect );
|
||||
|
||||
// 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 'Add Feature'" ) );
|
||||
|
||||
int res = 2;
|
||||
if ( vectorLayer->crs() != vlayer->crs() )
|
||||
{
|
||||
QgsGeometry transformedGeom = feature.geometry();
|
||||
try
|
||||
{
|
||||
// transform digitized geometry from vlayer crs to vectorLayer crs and add topological points
|
||||
transformedGeom.transform( transform );
|
||||
res = vectorLayer->addTopologicalPoints( transformedGeom );
|
||||
}
|
||||
catch ( QgsCsException &cse )
|
||||
{
|
||||
Q_UNUSED( cse )
|
||||
QgsDebugError( QStringLiteral( "transformation to vectorLayer coordinate failed" ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
res = vectorLayer->addTopologicalPoints( feature.geometry() );
|
||||
}
|
||||
|
||||
if ( res == 0 ) // i.e. if any points were added
|
||||
vectorLayer->endEditCommand();
|
||||
else
|
||||
vectorLayer->destroyEditCommand();
|
||||
}
|
||||
QgsVectorLayerEditUtils::addTopologicalPointsToLayers( feature.geometry(), vlayer, layers, mToolName );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,18 +213,13 @@ 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 )
|
||||
{
|
||||
if ( sm.at( i ).layer() )
|
||||
{
|
||||
sm.at( i ).layer()->addTopologicalPoints( pts.at( i ) );
|
||||
}
|
||||
}
|
||||
//check if we need to add topological points to other layers
|
||||
const QList<QgsMapLayer *> layers = canvas()->layers( true );
|
||||
QgsGeometry pointsAsGeom( new QgsMultiPoint( pts ) );
|
||||
QgsVectorLayerEditUtils::addTopologicalPointsToLayers( pointsAsGeom, vlayer, layers, mToolName );
|
||||
}
|
||||
|
||||
vlayer->endEditCommand();
|
||||
|
@ -231,6 +231,74 @@ double QgsVectorLayerEditUtils::getTopologicalSearchRadius( const QgsVectorLayer
|
||||
}
|
||||
return threshold;
|
||||
}
|
||||
|
||||
void QgsVectorLayerEditUtils::addTopologicalPointsToLayers( const QgsGeometry &geom, QgsVectorLayer *vlayer, const QList<QgsMapLayer *> &layers, const QString &toolName )
|
||||
{
|
||||
QgsFeatureRequest request = QgsFeatureRequest().setNoAttributes().setFlags( Qgis::FeatureRequestFlag::NoGeometry ).setLimit( 1 );
|
||||
QgsFeature f;
|
||||
|
||||
for ( QgsMapLayer *layer : layers )
|
||||
{
|
||||
QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layer );
|
||||
if ( vectorLayer && vectorLayer->isEditable() && vectorLayer->isSpatial() && ( vectorLayer->geometryType() == Qgis::GeometryType::Line || vectorLayer->geometryType() == Qgis::GeometryType::Polygon ) )
|
||||
{
|
||||
// boundingBox() is cached, it doesn't matter calling it in the loop
|
||||
QgsRectangle bbox = geom.boundingBox();
|
||||
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( 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( QObject::tr( "Topological points added by '%1'" ).arg( toolName ) );
|
||||
|
||||
int returnValue = 2;
|
||||
if ( vectorLayer->crs() != vlayer->crs() )
|
||||
{
|
||||
try
|
||||
{
|
||||
// transform digitized geometry from vlayer crs to vectorLayer crs and add topological points
|
||||
QgsGeometry transformedGeom( geom );
|
||||
transformedGeom.transform( ct );
|
||||
returnValue = vectorLayer->addTopologicalPoints( transformedGeom );
|
||||
}
|
||||
catch ( QgsCsException & )
|
||||
{
|
||||
QgsDebugError( QStringLiteral( "transformation to vectorLayer coordinate failed" ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
returnValue = vectorLayer->addTopologicalPoints( geom );
|
||||
}
|
||||
|
||||
if ( returnValue == 0 )
|
||||
{
|
||||
vectorLayer->endEditCommand();
|
||||
}
|
||||
else
|
||||
{
|
||||
// the layer was not modified, leave the undo buffer intact
|
||||
vectorLayer->destroyEditCommand();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
///@endcond
|
||||
|
||||
Qgis::GeometryOperationResult QgsVectorLayerEditUtils::addRing( const QVector<QgsPointXY> &ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId )
|
||||
|
@ -284,6 +284,8 @@ class CORE_EXPORT QgsVectorLayerEditUtils
|
||||
|
||||
///@cond PRIVATE
|
||||
static double getTopologicalSearchRadius( const QgsVectorLayer *layer ) SIP_SKIP;
|
||||
|
||||
static void addTopologicalPointsToLayers( const QgsGeometry &geom, QgsVectorLayer *vlayer, const QList<QgsMapLayer *> &layers, const QString &toolName ) SIP_SKIP;
|
||||
///@endcond
|
||||
|
||||
private:
|
||||
|
@ -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 );
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user