topologicalEditing: Fix Z for add feature

This commit is contained in:
lbartoletti 2019-06-18 06:39:25 +02:00 committed by Nyall Dawson
parent 6505e30fc4
commit aaa6f65537
7 changed files with 140 additions and 91 deletions

View File

@ -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
};

View File

@ -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<QgsPoint> &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();

View File

@ -26,6 +26,7 @@
#include "qgsvectorlayerutils.h"
#include "qgsvectorlayer.h"
#include "qgsgeometryoptions.h"
#include "qgsabstractgeometry.h"
#include <limits>
@ -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<QgsPointXY> &list, double &xmin, double &ymin, double &xmax, double &ymax ) const
{

View File

@ -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:
/**

View File

@ -92,6 +92,29 @@ QgsVectorLayer *QgsMapToolEdit::currentVectorLayer()
}
int QgsMapToolEdit::addTopologicalPoints( const QVector<QgsPoint> &geom )
{
if ( !mCanvas )
{
return 1;
}
//find out current vector layer
QgsVectorLayer *vlayer = currentVectorLayer();
if ( !vlayer )
{
return 2;
}
QVector<QgsPoint>::const_iterator list_it = geom.constBegin();
for ( ; list_it != geom.constEnd(); ++list_it )
{
vlayer->addTopologicalPoints( *list_it );
}
return 0;
}
int QgsMapToolEdit::addTopologicalPoints( const QVector<QgsPointXY> &geom )
{
if ( !mCanvas )

View File

@ -74,6 +74,14 @@ class GUI_EXPORT QgsMapToolEdit: public QgsMapTool
*/
int addTopologicalPoints( const QVector<QgsPointXY> &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<QgsPoint> &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.

View File

@ -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<QgsMapLayer *>() << 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<QgsMapLayer *>() << 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<QgsMapLayer *>() << 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<QgsFeatureId> 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"