From aaa6f65537e6088792850ccbfd70197a1d6a3aab Mon Sep 17 00:00:00 2001 From: lbartoletti Date: Tue, 18 Jun 2019 06:39:25 +0200 Subject: [PATCH] topologicalEditing: Fix Z for add feature --- .../qgsvectorlayereditutils.sip.in | 14 +++ .../gui/auto_generated/qgsmaptooledit.sip.in | 11 ++ src/core/qgsvectorlayereditutils.cpp | 103 +++--------------- src/core/qgsvectorlayereditutils.h | 11 ++ src/gui/qgsmaptooledit.cpp | 23 ++++ src/gui/qgsmaptooledit.h | 8 ++ .../src/app/testqgsmaptooladdfeatureline.cpp | 61 ++++++++++- 7 files changed, 140 insertions(+), 91 deletions(-) diff --git a/python/core/auto_generated/qgsvectorlayereditutils.sip.in b/python/core/auto_generated/qgsvectorlayereditutils.sip.in index c65db9fd841..ff893c362c3 100644 --- a/python/core/auto_generated/qgsvectorlayereditutils.sip.in +++ b/python/core/auto_generated/qgsvectorlayereditutils.sip.in @@ -190,6 +190,20 @@ editing. :param p: position of the vertex :return: 0 in case of success +%End + + int addTopologicalPoints( const QgsPoint &p ); +%Docstring +Adds a vertex to segments which intersect point p but don't +already have a vertex there. If a feature already has a vertex at position p, +no additional vertex is inserted. This method is useful for topological +editing. + +:param p: position of the vertex + +:return: 0 in case of success + +.. versionadded:: 3.10 %End }; diff --git a/python/gui/auto_generated/qgsmaptooledit.sip.in b/python/gui/auto_generated/qgsmaptooledit.sip.in index f7156f8d410..18b49da43fc 100644 --- a/python/gui/auto_generated/qgsmaptooledit.sip.in +++ b/python/gui/auto_generated/qgsmaptooledit.sip.in @@ -68,6 +68,17 @@ Adds vertices to other features to keep topology up to date, e.g. to neighbourin :param geom: list of points (in layer coordinate system) :return: 0 in case of success +%End + + int addTopologicalPoints( const QVector &geom ); +%Docstring +Adds vertices to other features to keep topology up to date, e.g. to neighbouring polygons. + +:param geom: list of points (in layer coordinate system) + +:return: 0 in case of success + +.. versionadded:: 3.10 %End void notifyNotVectorLayer(); diff --git a/src/core/qgsvectorlayereditutils.cpp b/src/core/qgsvectorlayereditutils.cpp index 8f482f0c4b1..0a885ca260d 100644 --- a/src/core/qgsvectorlayereditutils.cpp +++ b/src/core/qgsvectorlayereditutils.cpp @@ -26,6 +26,7 @@ #include "qgsvectorlayerutils.h" #include "qgsvectorlayer.h" #include "qgsgeometryoptions.h" +#include "qgsabstractgeometry.h" #include @@ -514,101 +515,20 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsGeometry &geom ) int returnVal = 0; - QgsWkbTypes::Type wkbType = geom.wkbType(); - - switch ( QgsWkbTypes::geometryType( wkbType ) ) + QgsAbstractGeometry::vertex_iterator it = geom.vertices_begin(); + while ( it != geom.vertices_end() ) { - //line - case QgsWkbTypes::LineGeometry: + if ( addTopologicalPoints( *it ) != 0 ) { - if ( !QgsWkbTypes::isMultiType( wkbType ) ) - { - QgsPolylineXY line = geom.asPolyline(); - QgsPolylineXY::const_iterator line_it = line.constBegin(); - for ( ; line_it != line.constEnd(); ++line_it ) - { - if ( addTopologicalPoints( *line_it ) != 0 ) - { - returnVal = 2; - } - } - } - else - { - QgsMultiPolylineXY multiLine = geom.asMultiPolyline(); - QgsPolylineXY currentPolyline; - - for ( int i = 0; i < multiLine.size(); ++i ) - { - QgsPolylineXY::const_iterator line_it = currentPolyline.constBegin(); - for ( ; line_it != currentPolyline.constEnd(); ++line_it ) - { - if ( addTopologicalPoints( *line_it ) != 0 ) - { - returnVal = 2; - } - } - } - } - break; + returnVal = 2; } - - case QgsWkbTypes::PolygonGeometry: - { - if ( !QgsWkbTypes::isMultiType( wkbType ) ) - { - QgsPolygonXY polygon = geom.asPolygon(); - QgsPolylineXY currentRing; - - for ( int i = 0; i < polygon.size(); ++i ) - { - currentRing = polygon.at( i ); - QgsPolylineXY::const_iterator line_it = currentRing.constBegin(); - for ( ; line_it != currentRing.constEnd(); ++line_it ) - { - if ( addTopologicalPoints( *line_it ) != 0 ) - { - returnVal = 2; - } - } - } - } - else - { - QgsMultiPolygonXY multiPolygon = geom.asMultiPolygon(); - QgsPolygonXY currentPolygon; - QgsPolylineXY currentRing; - - for ( int i = 0; i < multiPolygon.size(); ++i ) - { - currentPolygon = multiPolygon.at( i ); - for ( int j = 0; j < currentPolygon.size(); ++j ) - { - currentRing = currentPolygon.at( j ); - QgsPolylineXY::const_iterator line_it = currentRing.constBegin(); - for ( ; line_it != currentRing.constEnd(); ++line_it ) - { - if ( addTopologicalPoints( *line_it ) != 0 ) - { - returnVal = 2; - } - } - } - } - } - break; - } - - case QgsWkbTypes::PointGeometry: - case QgsWkbTypes::UnknownGeometry: - case QgsWkbTypes::NullGeometry: - break; + it++; } + return returnVal; } - -int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p ) +int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPoint &p ) { if ( !mLayer->isSpatial() ) return 1; @@ -673,7 +593,7 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p ) if ( sqrDistVertexSnap < sqrSnappingTolerance ) continue; // the vertex already exists - do not insert it - if ( !mLayer->insertVertex( p.x(), p.y(), fid, segmentAfterVertex ) ) + if ( !mLayer->insertVertex( p, fid, segmentAfterVertex ) ) { QgsDebugMsg( QStringLiteral( "failed to insert topo point" ) ); } @@ -682,6 +602,11 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p ) return 0; } +int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p ) +{ + return addTopologicalPoints( QgsPoint( p ) ); +} + bool QgsVectorLayerEditUtils::boundingBoxFromPointList( const QVector &list, double &xmin, double &ymin, double &xmax, double &ymax ) const { diff --git a/src/core/qgsvectorlayereditutils.h b/src/core/qgsvectorlayereditutils.h index 34800ca259d..0c1183d9e31 100644 --- a/src/core/qgsvectorlayereditutils.h +++ b/src/core/qgsvectorlayereditutils.h @@ -176,6 +176,17 @@ class CORE_EXPORT QgsVectorLayerEditUtils */ int addTopologicalPoints( const QgsPointXY &p ); + /** + * Adds a vertex to segments which intersect point p but don't + * already have a vertex there. If a feature already has a vertex at position p, + * no additional vertex is inserted. This method is useful for topological + * editing. + * \param p position of the vertex + * \return 0 in case of success + * \since QGIS 3.10 + */ + int addTopologicalPoints( const QgsPoint &p ); + private: /** diff --git a/src/gui/qgsmaptooledit.cpp b/src/gui/qgsmaptooledit.cpp index 3376ddcdb31..df8de587623 100644 --- a/src/gui/qgsmaptooledit.cpp +++ b/src/gui/qgsmaptooledit.cpp @@ -92,6 +92,29 @@ QgsVectorLayer *QgsMapToolEdit::currentVectorLayer() } +int QgsMapToolEdit::addTopologicalPoints( const QVector &geom ) +{ + if ( !mCanvas ) + { + return 1; + } + + //find out current vector layer + QgsVectorLayer *vlayer = currentVectorLayer(); + + if ( !vlayer ) + { + return 2; + } + + QVector::const_iterator list_it = geom.constBegin(); + for ( ; list_it != geom.constEnd(); ++list_it ) + { + vlayer->addTopologicalPoints( *list_it ); + } + return 0; +} + int QgsMapToolEdit::addTopologicalPoints( const QVector &geom ) { if ( !mCanvas ) diff --git a/src/gui/qgsmaptooledit.h b/src/gui/qgsmaptooledit.h index 0c4fbc18fb6..c64bd8c7b8c 100644 --- a/src/gui/qgsmaptooledit.h +++ b/src/gui/qgsmaptooledit.h @@ -74,6 +74,14 @@ class GUI_EXPORT QgsMapToolEdit: public QgsMapTool */ int addTopologicalPoints( const QVector &geom ); + /** + * Adds vertices to other features to keep topology up to date, e.g. to neighbouring polygons. + * \param geom list of points (in layer coordinate system) + * \returns 0 in case of success + * \since QGIS 3.10 + */ + int addTopologicalPoints( const QVector &geom ); + //! Display a timed message bar noting the active layer is not vector. void notifyNotVectorLayer(); //! Display a timed message bar noting the active vector layer is not editable. diff --git a/tests/src/app/testqgsmaptooladdfeatureline.cpp b/tests/src/app/testqgsmaptooladdfeatureline.cpp index eab3c027741..4e7364af5e8 100644 --- a/tests/src/app/testqgsmaptooladdfeatureline.cpp +++ b/tests/src/app/testqgsmaptooladdfeatureline.cpp @@ -29,7 +29,6 @@ #include "qgsmapmouseevent.h" #include "testqgsmaptoolutils.h" - bool operator==( const QgsGeometry &g1, const QgsGeometry &g2 ) { if ( g1.isNull() && g2.isNull() ) @@ -68,6 +67,7 @@ class TestQgsMapToolAddFeatureLine : public QObject void testTracingWithOffset(); void testZ(); void testZMSnapping(); + void testTopologicalEditingZ(); private: QgisApp *mQgisApp = nullptr; @@ -78,6 +78,7 @@ class TestQgsMapToolAddFeatureLine : public QObject QgsVectorLayer *mLayerLine = nullptr; QgsVectorLayer *mLayerLineZ = nullptr; QgsVectorLayer *mLayerPointZM = nullptr; + QgsVectorLayer *mLayerTopoZ = nullptr; QgsFeatureId mFidLineF1 = 0; }; @@ -156,7 +157,22 @@ void TestQgsMapToolAddFeatureLine::initTestCase() mLayerPointZM->addFeature( pointF ); QCOMPARE( mLayerPointZM->featureCount(), ( long )1 ); - mCanvas->setLayers( QList() << mLayerLine << mLayerLineZ << mLayerPointZM ); //<< mLayerPolygon << mLayerPoint ); + // make layer for topologicalEditing with Z + mLayerTopoZ = new QgsVectorLayer( QStringLiteral( "MultiLineStringZ?crs=EPSG:27700" ), QStringLiteral( "layer topologicalEditing Z" ), QStringLiteral( "memory" ) ); + QVERIFY( mLayerTopoZ->isValid() ); + QgsProject::instance()->addMapLayers( QList() << mLayerTopoZ ); + + mLayerTopoZ->startEditing(); + QgsFeature topoFeat; + topoFeat.setGeometry( QgsGeometry::fromWkt( "MultiLineStringZ ((7.25 6 0, 7.25 7 0, 7.5 7 0, 7.5 6 0, 7.25 6 0),(6 6 0, 6 7 0, 7 7 0, 7 6 0, 6 6 0),(6.25 6.25 0, 6.75 6.25 0, 6.75 6.75 0, 6.25 6.75 0, 6.25 6.25 0)));" ) ); + qDebug() << topoFeat.geometry().asWkt() << "\n"; + + mLayerTopoZ->addFeature( topoFeat ); + QCOMPARE( mLayerTopoZ->featureCount(), ( long ) 1 ); + + qDebug() << mLayerTopoZ->getFeature( 1 ).geometry().asWkt() << "\n"; + + mCanvas->setLayers( QList() << mLayerLine << mLayerLineZ << mLayerPointZM << mLayerTopoZ ); mCanvas->setSnappingUtils( new QgsMapCanvasSnappingUtils( mCanvas, this ) ); @@ -397,5 +413,46 @@ void TestQgsMapToolAddFeatureLine::testZMSnapping() mCanvas->snappingUtils()->setConfig( cfg ); } +void TestQgsMapToolAddFeatureLine::testTopologicalEditingZ() +{ + TestQgsMapToolAdvancedDigitizingUtils utils( mCaptureTool ); + + mCanvas->setCurrentLayer( mLayerTopoZ ); + + // test with default Z value = 333 + QgsSettings().setValue( QStringLiteral( "/qgis/digitizing/default_z_value" ), 333 ); + + QSet oldFids = utils.existingFeatureIds(); + + qDebug() << mLayerTopoZ->getFeature( 0 ).geometry().asWkt() << "\n"; + + QgsSnappingConfig cfg = mCanvas->snappingUtils()->config(); + bool topologicalEditing = cfg.project()->topologicalEditing(); + cfg.project()->setTopologicalEditing( true ); + + cfg.setMode( QgsSnappingConfig::AllLayers ); + cfg.setEnabled( true ); + mCanvas->snappingUtils()->setConfig( cfg ); + + oldFids = utils.existingFeatureIds(); + utils.mouseClick( 6, 6.5, Qt::LeftButton ); + utils.mouseClick( 6.25, 6.5, Qt::LeftButton ); + utils.mouseClick( 6.75, 6.5, Qt::LeftButton ); + utils.mouseClick( 7.25, 6.5, Qt::LeftButton ); + utils.mouseClick( 7.5, 6.5, Qt::LeftButton ); + utils.mouseClick( 8, 6.5, Qt::RightButton ); + QgsFeatureId newFid = utils.newFeatureId( oldFids ); + + QString wkt = "LineStringZ (6 6.5 333, 6.25 6.5 333, 6.75 6.5 333, 7.25 6.5 333, 7.5 6.5 333)"; + QCOMPARE( mLayerTopoZ->getFeature( newFid ).geometry(), QgsGeometry::fromWkt( wkt ) ); + wkt = "MultiLineStringZ ((7.25 6 0, 7.25 6.5 333, 7.25 7 0, 7.5 7 0, 7.5 6.5 333, 7.5 6 0, 7.25 6 0),(6 6 0, 6 6.5 333, 6 7 0, 7 7 0, 7 6 0, 6 6 0),(6.25 6.25 0, 6.75 6.25 0, 6.75 6.5 333, 6.75 6.75 0, 6.25 6.75 0, 6.25 6.5 333, 6.25 6.25 0))"; + QCOMPARE( mLayerTopoZ->getFeature( oldFids.toList().last() ).geometry(), QgsGeometry::fromWkt( wkt ) ); + + mLayerLine->undoStack()->undo(); + + cfg.setEnabled( false ); + mCanvas->snappingUtils()->setConfig( cfg ); + cfg.project()->setTopologicalEditing( topologicalEditing ); +} QGSTEST_MAIN( TestQgsMapToolAddFeatureLine ) #include "testqgsmaptooladdfeatureline.moc"