Skip to content

Commit

Permalink
fix(vertextool): topo edition when map CRS != layer CRS
Browse files Browse the repository at this point in the history
  • Loading branch information
Djedouas committed Aug 9, 2024
1 parent 38e9552 commit 120fef1
Showing 1 changed file with 23 additions and 45 deletions.
68 changes: 23 additions & 45 deletions src/app/vertextool/qgsvertextool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,55 +163,33 @@ 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<QgsPointLocator::Match> matches;
QgsVertexTool *vertextool = nullptr;

MatchCollectingFilter( QgsVertexTool *vertextool )
: vertextool( vertextool ) {}

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

Expand Down Expand Up @@ -1012,7 +990,7 @@ QgsPointLocator::Match QgsVertexTool::snapToPolygonInterior( QgsMapMouseEvent *e
}


QList<QgsPointLocator::Match> QgsVertexTool::findEditableLayerMatches( const QgsPointXY &mapPoint, QgsVectorLayer *layer )
QgsPointLocator::MatchList QgsVertexTool::findEditableLayerMatches( const QgsPointXY &mapPoint, QgsVectorLayer *layer )
{
QgsPointLocator::MatchList matchList;

Expand Down Expand Up @@ -1935,29 +1913,29 @@ void QgsVertexTool::buildDragBandsForVertices( const QSet<Vertex> &movingVertice
}
}

QList<QgsPointLocator::Match> 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<QgsPointLocator::Match> QgsVertexTool::layerSegmentsSnappedToSegment( QgsVectorLayer *layer, const QgsPointXY &mapPoint1, const QgsPointXY &mapPoint2 )
QgsPointLocator::MatchList QgsVertexTool::layerSegmentsSnappedToSegment( QgsVectorLayer *layer, const QgsPointXY &mapPoint1, const QgsPointXY &mapPoint2 )
{
QList<QgsPointLocator::Match> 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<QgsPointLocator::Match> 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;
Expand Down

0 comments on commit 120fef1

Please sign in to comment.