Skip to content

Commit 9f5664a

Browse files
wonder-sknyalldawson
authored andcommittedNov 6, 2018
[vertex tool] Respect topo editing when adding a vertex (fixes #18046)
When adding a vertex to a segment that is coincident with some other segments and topological editing is enabled, vertex tool will now correctly add new vertex also the coincident segments to preserve shared borders. (cherry picked from commit 5dd5664)
1 parent 97cbf92 commit 9f5664a

File tree

3 files changed

+135
-0
lines changed

3 files changed

+135
-0
lines changed
 

‎src/app/vertextool/qgsvertextool.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,6 +1372,26 @@ QList<QgsPointLocator::Match> QgsVertexTool::layerVerticesSnappedToPoint( QgsVec
13721372
return myfilter.matches;
13731373
}
13741374

1375+
QList<QgsPointLocator::Match> QgsVertexTool::layerSegmentsSnappedToSegment( QgsVectorLayer *layer, const QgsPointXY &mapPoint1, const QgsPointXY &mapPoint2 )
1376+
{
1377+
QList<QgsPointLocator::Match> finalMatches;
1378+
// we want segment matches that have exactly the same vertices as the given segment (mapPoint1, mapPoint2)
1379+
// so rather than doing nearest edge search which could return any segment within a tolerance,
1380+
// we first find matches for one endpoint and then see if there is a matching other endpoint.
1381+
const QList<QgsPointLocator::Match> matches1 = layerVerticesSnappedToPoint( layer, mapPoint1 );
1382+
for ( const QgsPointLocator::Match &m : matches1 )
1383+
{
1384+
QgsGeometry g = cachedGeometry( layer, m.featureId() );
1385+
int v0, v1;
1386+
g.adjacentVertices( m.vertexIndex(), v0, v1 );
1387+
if ( v0 != -1 && QgsPointXY( g.vertexAt( v0 ) ) == mapPoint2 )
1388+
finalMatches << QgsPointLocator::Match( QgsPointLocator::Edge, layer, m.featureId(), 0, m.point(), v0 );
1389+
else if ( v1 != -1 && QgsPointXY( g.vertexAt( v1 ) ) == mapPoint2 )
1390+
finalMatches << QgsPointLocator::Match( QgsPointLocator::Edge, layer, m.featureId(), 0, m.point(), m.vertexIndex() );
1391+
}
1392+
return finalMatches;
1393+
}
1394+
13751395
void QgsVertexTool::startDraggingAddVertex( const QgsPointLocator::Match &m )
13761396
{
13771397
Q_ASSERT( m.hasEdge() );
@@ -1383,6 +1403,7 @@ void QgsVertexTool::startDraggingAddVertex( const QgsPointLocator::Match &m )
13831403
mDraggingVertexType = AddingVertex;
13841404
mDraggingExtraVertices.clear();
13851405
mDraggingExtraVerticesOffset.clear();
1406+
mDraggingExtraSegments.clear();
13861407

13871408
QgsGeometry geom = cachedGeometry( m.layer(), m.featureId() );
13881409

@@ -1398,6 +1419,36 @@ void QgsVertexTool::startDraggingAddVertex( const QgsPointLocator::Match &m )
13981419
if ( v1.x() != 0 || v1.y() != 0 )
13991420
addDragBand( map_v1, m.point() );
14001421

1422+
if ( QgsProject::instance()->topologicalEditing() )
1423+
{
1424+
// find other segments coincident with the one user just picked and store them in a list
1425+
// so we can add a new vertex also in those to keep topology correct
1426+
const auto layers = canvas()->layers();
1427+
for ( QgsMapLayer *layer : layers )
1428+
{
1429+
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
1430+
if ( !vlayer || !vlayer->isEditable() )
1431+
continue;
1432+
1433+
if ( vlayer->geometryType() != QgsWkbTypes::LineGeometry && vlayer->geometryType() != QgsWkbTypes::PolygonGeometry )
1434+
continue;
1435+
1436+
QgsPointXY pt1, pt2;
1437+
m.edgePoints( pt1, pt2 );
1438+
const auto snappedSegments = layerSegmentsSnappedToSegment( vlayer, pt1, pt2 );
1439+
for ( const QgsPointLocator::Match &otherMatch : snappedSegments )
1440+
{
1441+
if ( otherMatch.layer() == m.layer() &&
1442+
otherMatch.featureId() == m.featureId() &&
1443+
otherMatch.vertexIndex() == m.vertexIndex() )
1444+
continue;
1445+
1446+
// start dragging of snapped point of current layer
1447+
mDraggingExtraSegments << Vertex( otherMatch.layer(), otherMatch.featureId(), otherMatch.vertexIndex() );
1448+
}
1449+
}
1450+
}
1451+
14011452
cadDockWidget()->setPoints( QList<QgsPointXY>() << m.point() << m.point() );
14021453
}
14031454

@@ -1563,6 +1614,13 @@ void QgsVertexTool::moveVertex( const QgsPointXY &mapPoint, const QgsPointLocato
15631614

15641615
addExtraVerticesToEdits( edits, mapPoint, dragLayer, layerPoint );
15651616

1617+
if ( addingVertex && !addingAtEndpoint && QgsProject::instance()->topologicalEditing() )
1618+
{
1619+
// topo editing: when adding a vertex to an existing segment, there may be other coincident segments
1620+
// that also need adding the same vertex
1621+
addExtraSegmentsToEdits( edits, mapPoint, dragLayer, layerPoint );
1622+
}
1623+
15661624
applyEditsToLayers( edits );
15671625

15681626
if ( QgsProject::instance()->topologicalEditing() && mapPointMatch->hasEdge() && mapPointMatch->layer() )
@@ -1625,6 +1683,40 @@ void QgsVertexTool::addExtraVerticesToEdits( QgsVertexTool::VertexEdits &edits,
16251683
}
16261684

16271685

1686+
void QgsVertexTool::addExtraSegmentsToEdits( QgsVertexTool::VertexEdits &edits, const QgsPointXY &mapPoint, QgsVectorLayer *dragLayer, const QgsPointXY &layerPoint )
1687+
{
1688+
// insert new vertex also to other geometries/layers
1689+
for ( int i = 0; i < mDraggingExtraSegments.count(); ++i )
1690+
{
1691+
const Vertex &topo = mDraggingExtraSegments[i];
1692+
1693+
QHash<QgsFeatureId, QgsGeometry> &layerEdits = edits[topo.layer];
1694+
QgsGeometry topoGeom;
1695+
if ( layerEdits.contains( topo.fid ) )
1696+
topoGeom = QgsGeometry( edits[topo.layer][topo.fid] );
1697+
else
1698+
topoGeom = QgsGeometry( cachedGeometryForVertex( topo ) );
1699+
1700+
QgsPointXY point;
1701+
if ( dragLayer && topo.layer->crs() == dragLayer->crs() )
1702+
point = layerPoint; // this point may come from exact match so it may be more precise
1703+
else
1704+
point = toLayerCoordinates( topo.layer, mapPoint );
1705+
1706+
QgsPoint pt( point );
1707+
if ( QgsWkbTypes::hasZ( topo.layer->wkbType() ) )
1708+
pt.addZValue( defaultZValue() );
1709+
1710+
if ( !topoGeom.insertVertex( pt, topo.vertexId + 1 ) )
1711+
{
1712+
QgsDebugMsg( QStringLiteral( "[topo] segment insert vertex failed!" ) );
1713+
continue;
1714+
}
1715+
edits[topo.layer][topo.fid] = topoGeom;
1716+
}
1717+
}
1718+
1719+
16281720
void QgsVertexTool::applyEditsToLayers( QgsVertexTool::VertexEdits &edits )
16291721
{
16301722
QHash<QgsVectorLayer *, QHash<QgsFeatureId, QgsGeometry> >::iterator it = edits.begin();

‎src/app/vertextool/qgsvertextool.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ class APP_EXPORT QgsVertexTool : public QgsMapToolAdvancedDigitizing
156156
//! Gets list of matches of all vertices of a layer exactly snapped to a map point
157157
QList<QgsPointLocator::Match> layerVerticesSnappedToPoint( QgsVectorLayer *layer, const QgsPointXY &mapPoint );
158158

159+
//! Gets list of matches of all segments of a layer coincident with the given segment
160+
QList<QgsPointLocator::Match> layerSegmentsSnappedToSegment( QgsVectorLayer *layer, const QgsPointXY &mapPoint1, const QgsPointXY &mapPoint2 );
161+
159162
void startDraggingAddVertex( const QgsPointLocator::Match &m );
160163

161164
void startDraggingAddVertexAtEndpoint( const QgsPointXY &mapPoint );
@@ -177,6 +180,8 @@ class APP_EXPORT QgsVertexTool : public QgsMapToolAdvancedDigitizing
177180

178181
void addExtraVerticesToEdits( VertexEdits &edits, const QgsPointXY &mapPoint, QgsVectorLayer *dragLayer = nullptr, const QgsPointXY &layerPoint = QgsPointXY() );
179182

183+
void addExtraSegmentsToEdits( QgsVertexTool::VertexEdits &edits, const QgsPointXY &mapPoint, QgsVectorLayer *dragLayer, const QgsPointXY &layerPoint );
184+
180185
void applyEditsToLayers( VertexEdits &edits );
181186

182187

@@ -330,6 +335,12 @@ class APP_EXPORT QgsVertexTool : public QgsMapToolAdvancedDigitizing
330335
*/
331336
QList<QgsVector> mDraggingExtraVerticesOffset;
332337

338+
/**
339+
* list of Vertex instances identifying segments (by their first vertex index) that should
340+
* also get a new vertex: this is used for topo editing when adding a vertex to existing segment
341+
*/
342+
QList<Vertex> mDraggingExtraSegments;
343+
333344
// members for selection handling
334345

335346
//! list of Vertex instances of vertices that are selected

‎tests/src/app/testqgsvertextool.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class TestQgsVertexTool : public QObject
6464
void testMoveMultipleVertices();
6565
void testMoveVertexTopo();
6666
void testDeleteVertexTopo();
67+
void testAddVertexTopo();
6768
void testActiveLayerPriority();
6869
void testSelectedFeaturesPriority();
6970

@@ -532,6 +533,37 @@ void TestQgsVertexTool::testDeleteVertexTopo()
532533
QgsProject::instance()->setTopologicalEditing( false );
533534
}
534535

536+
void TestQgsVertexTool::testAddVertexTopo()
537+
{
538+
// test addition of a vertex on a segment shared with another geometry
539+
540+
// add a temporary polygon
541+
QgsFeature fTmp;
542+
fTmp.setGeometry( QgsGeometry::fromWkt( "POLYGON((4 4, 7 4, 7 6, 4 6, 4 4))" ) );
543+
bool resAdd = mLayerPolygon->addFeature( fTmp );
544+
QVERIFY( resAdd );
545+
QgsFeatureId fTmpId = fTmp.id();
546+
547+
QCOMPARE( mLayerPolygon->undoStack()->index(), 2 );
548+
549+
QgsProject::instance()->setTopologicalEditing( true );
550+
551+
mouseClick( 5.5, 4, Qt::LeftButton );
552+
mouseClick( 5, 5, Qt::LeftButton );
553+
554+
QCOMPARE( mLayerPolygon->undoStack()->index(), 3 );
555+
556+
QCOMPARE( mLayerPolygon->getFeature( mFidPolygonF1 ).geometry(), QgsGeometry::fromWkt( "POLYGON((4 1, 7 1, 7 4, 5 5, 4 4, 4 1))" ) );
557+
QCOMPARE( mLayerPolygon->getFeature( fTmpId ).geometry(), QgsGeometry::fromWkt( "POLYGON((4 4, 5 5, 7 4, 7 6, 4 6, 4 4))" ) );
558+
559+
mLayerPolygon->undoStack()->undo();
560+
mLayerPolygon->undoStack()->undo();
561+
562+
QCOMPARE( mLayerPolygon->getFeature( mFidPolygonF1 ).geometry(), QgsGeometry::fromWkt( "POLYGON((4 1, 7 1, 7 4, 4 4, 4 1))" ) );
563+
564+
QgsProject::instance()->setTopologicalEditing( false );
565+
}
566+
535567
void TestQgsVertexTool::testActiveLayerPriority()
536568
{
537569
// check that features from current layer get priority when picking points

0 commit comments

Comments
 (0)
Please sign in to comment.