diff --git a/src/app/vertextool/qgsvertextool.cpp b/src/app/vertextool/qgsvertextool.cpp index aff531ec1444..e626dfd1b4eb 100644 --- a/src/app/vertextool/qgsvertextool.cpp +++ b/src/app/vertextool/qgsvertextool.cpp @@ -163,11 +163,14 @@ class OneFeatureFilter : public QgsPointLocator::MatchFilter }; -//! a filter just to gather all matches at the same place +/** + * For polygons we need to filter out the last vertex, because it will have + * the same coordinates than the first and we would have a duplicate match which + * would make topology editing mode behave incorrectly + */ class MatchCollectingFilter : public QgsPointLocator::MatchFilter { public: - QList matches; QgsVertexTool *vertextool = nullptr; MatchCollectingFilter( QgsVertexTool *vertextool ) @@ -175,43 +178,18 @@ class MatchCollectingFilter : public QgsPointLocator::MatchFilter bool acceptMatch( const QgsPointLocator::Match &match ) override { - if ( match.distance() > 0 ) - return false; + if ( match.layer()->geometryType() != Qgis::GeometryType::Polygon ) + return true; - // there may be multiple points at the same location, but we get only one - // result... the locator API needs a new method verticesInRect() - QgsGeometry matchGeom = vertextool->cachedGeometry( match.layer(), match.featureId() ); - bool isPolygon = matchGeom.type() == Qgis::GeometryType::Polygon; - QgsVertexId polygonRingVid; QgsVertexId vid; - QgsPoint pt; - while ( matchGeom.constGet()->nextVertex( vid, pt ) ) - { - int vindex = matchGeom.vertexNrFromVertexId( vid ); - if ( pt.x() == match.point().x() && pt.y() == match.point().y() ) - { - if ( isPolygon ) - { - // for polygons we need to handle the case where the first vertex is matching because the - // last point will have the same coordinates and we would have a duplicate match which - // would make subsequent code behave incorrectly (topology editing mode would add a single - // vertex twice) - if ( vid.vertex == 0 ) - { - polygonRingVid = vid; - } - else if ( vid.ringEqual( polygonRingVid ) && vid.vertex == matchGeom.constGet()->vertexCount( vid.part, vid.ring ) - 1 ) - { - continue; - } - } + QgsGeometry matchGeom = vertextool->cachedGeometry( match.layer(), match.featureId() ); + if ( !matchGeom.vertexIdFromVertexNr( match.vertexIndex(), vid ) ) + // should not happen because vertex index in match object was created with vertexNrFromVertexId + // so the methods are reversible and we will have a vid + return false; - QgsPointLocator::Match extra_match( match.type(), match.layer(), match.featureId(), - 0, match.point(), vindex ); - matches.append( extra_match ); - } - } - return true; + // filter out the vertex if it is the last one (of its ring, in its part) + return vid.vertex != matchGeom.constGet()->vertexCount( vid.part, vid.ring ) - 1; } }; @@ -1012,7 +990,7 @@ QgsPointLocator::Match QgsVertexTool::snapToPolygonInterior( QgsMapMouseEvent *e } -QList QgsVertexTool::findEditableLayerMatches( const QgsPointXY &mapPoint, QgsVectorLayer *layer ) +QgsPointLocator::MatchList QgsVertexTool::findEditableLayerMatches( const QgsPointXY &mapPoint, QgsVectorLayer *layer ) { QgsPointLocator::MatchList matchList; @@ -1935,29 +1913,29 @@ void QgsVertexTool::buildDragBandsForVertices( const QSet &movingVertice } } -QList QgsVertexTool::layerVerticesSnappedToPoint( QgsVectorLayer *layer, const QgsPointXY &mapPoint ) +QgsPointLocator::MatchList QgsVertexTool::layerVerticesSnappedToPoint( QgsVectorLayer *layer, const QgsPointXY &mapPoint ) { MatchCollectingFilter myfilter( this ); QgsPointLocator *loc = canvas()->snappingUtils()->locatorForLayer( layer ); - loc->nearestVertex( mapPoint, 0, &myfilter, true ); - return myfilter.matches; + double tol = QgsTolerance::vertexSearchRadius( canvas()->mapSettings() ); + return loc->verticesInRect( mapPoint, tol, &myfilter, true ); } -QList QgsVertexTool::layerSegmentsSnappedToSegment( QgsVectorLayer *layer, const QgsPointXY &mapPoint1, const QgsPointXY &mapPoint2 ) +QgsPointLocator::MatchList QgsVertexTool::layerSegmentsSnappedToSegment( QgsVectorLayer *layer, const QgsPointXY &mapPoint1, const QgsPointXY &mapPoint2 ) { - QList finalMatches; + QgsPointLocator::MatchList finalMatches; // we want segment matches that have exactly the same vertices as the given segment (mapPoint1, mapPoint2) // so rather than doing nearest edge search which could return any segment within a tolerance, // we first find matches for one endpoint and then see if there is a matching other endpoint. - const QList matches1 = layerVerticesSnappedToPoint( layer, mapPoint1 ); + const QgsPointLocator::MatchList matches1 = layerVerticesSnappedToPoint( layer, mapPoint1 ); for ( const QgsPointLocator::Match &m : matches1 ) { QgsGeometry g = cachedGeometry( layer, m.featureId() ); int v0, v1; g.adjacentVertices( m.vertexIndex(), v0, v1 ); - if ( v0 != -1 && QgsPointXY( g.vertexAt( v0 ) ) == mapPoint2 ) + if ( v0 != -1 && toMapCoordinates( layer, QgsPointXY( g.vertexAt( v0 ) ) ) == mapPoint2 ) finalMatches << QgsPointLocator::Match( QgsPointLocator::Edge, layer, m.featureId(), 0, m.point(), v0 ); - else if ( v1 != -1 && QgsPointXY( g.vertexAt( v1 ) ) == mapPoint2 ) + else if ( v1 != -1 && toMapCoordinates( layer, QgsPointXY( g.vertexAt( v1 ) ) ) == mapPoint2 ) finalMatches << QgsPointLocator::Match( QgsPointLocator::Edge, layer, m.featureId(), 0, m.point(), m.vertexIndex() ); } return finalMatches;