mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-08 00:06:51 -05:00
Merge pull request #57504 from GispoCoding/fix_copy_move_overlap
Apply avoid overlap with the copy move map tool
This commit is contained in:
commit
92f182568f
@ -2091,6 +2091,7 @@ QgisApp::QgisApp()
|
||||
mMapTools = std::make_unique< QgsAppMapTools >( mMapCanvas, mAdvancedDigitizingDockWidget );
|
||||
mDigitizingTechniqueManager = new QgsMapToolsDigitizingTechniqueManager( this );
|
||||
|
||||
mVectorLayerTools = new QgsGuiVectorLayerTools();
|
||||
mBearingNumericFormat.reset( QgsLocalDefaultSettings::bearingFormat() );
|
||||
|
||||
connect( mLayerTreeView, &QgsLayerTreeView::currentLayerChanged, this, &QgisApp::onActiveLayerChanged );
|
||||
|
||||
@ -18,12 +18,14 @@
|
||||
|
||||
#include "qgsguivectorlayertools.h"
|
||||
|
||||
#include "qgsavoidintersectionsoperation.h"
|
||||
#include "qgisapp.h"
|
||||
#include "qgsfeatureaction.h"
|
||||
#include "qgsmessagebar.h"
|
||||
#include "qgsmessagebaritem.h"
|
||||
#include "qgsmessageviewer.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsvectorlayerutils.h"
|
||||
|
||||
bool QgsGuiVectorLayerTools::addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feat, QWidget *parentWidget, bool showModal, bool hideParent ) const
|
||||
{
|
||||
@ -163,6 +165,51 @@ bool QgsGuiVectorLayerTools::stopEditing( QgsVectorLayer *layer, bool allowCance
|
||||
return res;
|
||||
}
|
||||
|
||||
bool QgsGuiVectorLayerTools::copyMoveFeatures( QgsVectorLayer *layer, QgsFeatureRequest &request, double dx, double dy, QString *errorMsg, const bool topologicalEditing, QgsVectorLayer *topologicalLayer, QString *childrenInfoMsg ) const
|
||||
{
|
||||
bool res = QgsVectorLayerTools::copyMoveFeatures( layer, request, dx, dy, errorMsg, topologicalEditing, topologicalLayer, childrenInfoMsg );
|
||||
|
||||
if ( res && layer->geometryType() == Qgis::GeometryType::Polygon )
|
||||
{
|
||||
res = avoidIntersection( layer, request, errorMsg );
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool QgsGuiVectorLayerTools::avoidIntersection( QgsVectorLayer *layer, QgsFeatureRequest &request, QString *errorMsg ) const
|
||||
{
|
||||
QgsAvoidIntersectionsOperation avoidIntersections;
|
||||
|
||||
QgsFeatureIterator fi = layer->getFeatures( request );
|
||||
QgsFeature f;
|
||||
|
||||
const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > ignoreFeatures {{ layer, request.filterFids() }};
|
||||
|
||||
while ( fi.nextFeature( f ) )
|
||||
{
|
||||
QgsGeometry geom = f.geometry();
|
||||
const QgsFeatureId id = f.id();
|
||||
|
||||
const QgsAvoidIntersectionsOperation::Result res = avoidIntersections.apply( layer, id, geom, ignoreFeatures );
|
||||
|
||||
if ( res.operationResult == Qgis::GeometryOperationResult::InvalidInputGeometryType || geom.isEmpty() )
|
||||
{
|
||||
if ( errorMsg )
|
||||
{
|
||||
*errorMsg = ( geom.isEmpty() ) ?
|
||||
tr( "The feature cannot be moved because 1 or more resulting geometries would be empty" ) :
|
||||
tr( "An error was reported during intersection removal" );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
layer->changeGeometry( id, geom );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsGuiVectorLayerTools::commitError( QgsVectorLayer *vlayer ) const
|
||||
{
|
||||
QgsMessageViewer *mv = new QgsMessageViewer();
|
||||
|
||||
@ -79,8 +79,26 @@ class QgsGuiVectorLayerTools : public QgsVectorLayerTools
|
||||
*/
|
||||
bool saveEdits( QgsVectorLayer *layer ) const override;
|
||||
|
||||
/**
|
||||
* Copy and move features with defined translation.
|
||||
*
|
||||
* \param layer The layer
|
||||
* \param request The request for the features to be moved. It will be assigned to a new feature request with the newly copied features.
|
||||
* \param dx The translation on x
|
||||
* \param dy The translation on y
|
||||
* \param errorMsg If given, it will contain the error message
|
||||
* \param topologicalEditing If TRUE, the function will perform topological
|
||||
* editing of the vertices of \a layer on \a layer and \a topologicalLayer
|
||||
* \param topologicalLayer The layer where vertices from the moved features of \a layer will be added
|
||||
* \param childrenInfoMsg If given, it will contain messages related to the creation of child features
|
||||
* \returns TRUE if all features could be copied.
|
||||
*
|
||||
*/
|
||||
bool copyMoveFeatures( QgsVectorLayer *layer, QgsFeatureRequest &request SIP_INOUT, double dx = 0, double dy = 0, QString *errorMsg SIP_OUT = nullptr, const bool topologicalEditing = false, QgsVectorLayer *topologicalLayer = nullptr, QString *childrenInfoMsg = nullptr ) const override;
|
||||
|
||||
private:
|
||||
void commitError( QgsVectorLayer *vlayer ) const;
|
||||
bool avoidIntersection( QgsVectorLayer *layer, QgsFeatureRequest &request, QString *errorMsg = nullptr ) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -277,7 +277,10 @@ void QgsMapToolMoveFeature::cadCanvasReleaseEvent( QgsMapMouseEvent *e )
|
||||
{
|
||||
emit messageEmitted( errorMsg, Qgis::MessageLevel::Critical );
|
||||
deleteRubberband();
|
||||
vlayer->deleteFeatures( request.filterFids() );
|
||||
vlayer->destroyEditCommand();
|
||||
mSnapIndicator->setMatch( QgsPointLocator::Match() );
|
||||
return;
|
||||
}
|
||||
if ( !childrenInfoMsg.isEmpty() )
|
||||
{
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
#include "qgssettings.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsmapmouseevent.h"
|
||||
#include "qgsguivectorlayertools.h"
|
||||
#include "testqgsmaptoolutils.h"
|
||||
|
||||
|
||||
@ -45,13 +46,16 @@ class TestQgsMapToolMoveFeature: public QObject
|
||||
void cleanupTestCase();// will be called after the last testfunction was executed.
|
||||
|
||||
void testMoveFeature();
|
||||
void testCopyMoveFeature();
|
||||
void testTopologicalMoveFeature();
|
||||
void testAvoidIntersectionAndTopoEdit();
|
||||
void testAvoidIntersectionsCopyMove();
|
||||
|
||||
private:
|
||||
QgisApp *mQgisApp = nullptr;
|
||||
QgsMapCanvas *mCanvas = nullptr;
|
||||
QgsMapToolMoveFeature *mCaptureTool = nullptr;
|
||||
QgsMapToolMoveFeature *mCopyMoveTool = nullptr;
|
||||
QgsVectorLayer *mLayerBase = nullptr;
|
||||
};
|
||||
|
||||
@ -73,6 +77,7 @@ void TestQgsMapToolMoveFeature::initTestCase()
|
||||
|
||||
mQgisApp = new QgisApp();
|
||||
|
||||
|
||||
mCanvas = new QgsMapCanvas();
|
||||
|
||||
mCanvas->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3946" ) ) );
|
||||
@ -113,7 +118,8 @@ void TestQgsMapToolMoveFeature::initTestCase()
|
||||
mCanvas->setLayers( QList<QgsMapLayer *>() << mLayerBase );
|
||||
mCanvas->setCurrentLayer( mLayerBase );
|
||||
|
||||
// create the tool
|
||||
// create the tools
|
||||
mCopyMoveTool = new QgsMapToolMoveFeature( mCanvas, QgsMapToolMoveFeature::CopyMove );
|
||||
mCaptureTool = new QgsMapToolMoveFeature( mCanvas, QgsMapToolMoveFeature::Move );
|
||||
mCanvas->setMapTool( mCaptureTool );
|
||||
|
||||
@ -125,6 +131,7 @@ void TestQgsMapToolMoveFeature::initTestCase()
|
||||
void TestQgsMapToolMoveFeature::cleanupTestCase()
|
||||
{
|
||||
delete mCaptureTool;
|
||||
delete mCopyMoveTool;
|
||||
delete mCanvas;
|
||||
QgsApplication::exitQgis();
|
||||
}
|
||||
@ -144,6 +151,34 @@ void TestQgsMapToolMoveFeature::testMoveFeature()
|
||||
mLayerBase->undoStack()->undo();
|
||||
}
|
||||
|
||||
void TestQgsMapToolMoveFeature::testCopyMoveFeature()
|
||||
{
|
||||
mCanvas->setMapTool( mCopyMoveTool );
|
||||
TestQgsMapToolAdvancedDigitizingUtils utils( mCopyMoveTool );
|
||||
|
||||
utils.mouseClick( 1, 1, Qt::LeftButton, Qt::KeyboardModifiers(), true );
|
||||
utils.mouseClick( 2, 1, Qt::LeftButton, Qt::KeyboardModifiers(), true );
|
||||
|
||||
const QString wkt1 = "Polygon ((0 0, 0 1, 1 1, 1 0, 0 0))";
|
||||
QCOMPARE( mLayerBase->getFeature( 1 ).geometry().asWkt(), wkt1 );
|
||||
const QString wkt2 = "Polygon ((2 0, 2 5, 3 5, 3 0, 2 0))";
|
||||
QCOMPARE( mLayerBase->getFeature( 2 ).geometry().asWkt(), wkt2 );
|
||||
|
||||
// copied feature
|
||||
const QString wkt3 = "Polygon ((1 0, 1 1, 2 1, 2 0, 1 0))";
|
||||
QgsFeatureIterator fi1 = mLayerBase->getFeatures();
|
||||
QgsFeature f1;
|
||||
|
||||
while ( fi1.nextFeature( f1 ) )
|
||||
{
|
||||
QCOMPARE( f1.geometry().asWkt( 2 ), wkt3 );
|
||||
break;
|
||||
}
|
||||
|
||||
mLayerBase->undoStack()->undo();
|
||||
mCanvas->setMapTool( mCaptureTool );
|
||||
}
|
||||
|
||||
void TestQgsMapToolMoveFeature::testTopologicalMoveFeature()
|
||||
{
|
||||
const bool topologicalEditing = QgsProject::instance()->topologicalEditing();
|
||||
@ -188,5 +223,42 @@ void TestQgsMapToolMoveFeature::testAvoidIntersectionAndTopoEdit()
|
||||
QgsProject::instance()->setAvoidIntersectionsMode( mode );
|
||||
}
|
||||
|
||||
void TestQgsMapToolMoveFeature::testAvoidIntersectionsCopyMove()
|
||||
{
|
||||
const bool topologicalEditing = QgsProject::instance()->topologicalEditing();
|
||||
const Qgis::AvoidIntersectionsMode mode( QgsProject::instance()->avoidIntersectionsMode() );
|
||||
|
||||
QgsProject::instance()->setAvoidIntersectionsMode( Qgis::AvoidIntersectionsMode::AvoidIntersectionsCurrentLayer );
|
||||
QgsProject::instance()->setTopologicalEditing( true );
|
||||
|
||||
mCanvas->setMapTool( mCopyMoveTool );
|
||||
TestQgsMapToolAdvancedDigitizingUtils utils( mCopyMoveTool );
|
||||
|
||||
utils.mouseClick( 1, 1, Qt::LeftButton, Qt::KeyboardModifiers(), true );
|
||||
utils.mouseClick( 2.5, 1, Qt::LeftButton, Qt::KeyboardModifiers(), true );
|
||||
|
||||
const QString wkt1 = "Polygon ((0 0, 0 1, 1 1, 1 0, 0 0))";
|
||||
QCOMPARE( mLayerBase->getFeature( 1 ).geometry().asWkt(), wkt1 );
|
||||
const QString wkt2 = "Polygon ((2 0, 2 1, 2 5, 3 5, 3 0, 2.5 0, 2 0))";
|
||||
QCOMPARE( mLayerBase->getFeature( 2 ).geometry().asWkt(), wkt2 );
|
||||
|
||||
// copied feature
|
||||
const QString wkt3 = "Polygon ((1.5 1, 2 1, 2 0, 1.5 0, 1.5 1))";
|
||||
QgsFeatureIterator fi1 = mLayerBase->getFeatures();
|
||||
QgsFeature f1;
|
||||
|
||||
while ( fi1.nextFeature( f1 ) )
|
||||
{
|
||||
QCOMPARE( f1.geometry().asWkt( 2 ), wkt3 );
|
||||
break;
|
||||
}
|
||||
|
||||
mLayerBase->undoStack()->undo();
|
||||
mCanvas->setMapTool( mCaptureTool );
|
||||
|
||||
QgsProject::instance()->setTopologicalEditing( topologicalEditing );
|
||||
QgsProject::instance()->setAvoidIntersectionsMode( mode );
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsMapToolMoveFeature )
|
||||
#include "testqgsmaptoolmovefeature.moc"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user