Skip to content
Permalink
Browse files

Fix crash in node tool after deleting the whole geometry (fixes #15659)

Made sure that both closestVertex() and closestSegment() return negative
distance on error (e.g. with null or emtpy geometry).

Also fixes snapping when dealing with layers with null/invalid geometries

(cherry picked from commit c093d51)
  • Loading branch information
wonder-sk committed Oct 31, 2016
1 parent 9f228d4 commit afd04eb0b860cd1266368741f7eea97f92433f22
@@ -272,12 +272,11 @@ void QgsMapToolNodeTool::canvasPressEvent( QgsMapMouseEvent* e )

// get geometry and find if snapping is near it
int atVertex, beforeVertex, afterVertex;
double dist;
QgsPoint closestLayerVertex = mSelectedFeature->geometry()->closestVertex( layerCoordPoint, atVertex, beforeVertex, afterVertex, dist );
dist = sqrt( dist );
double sqrDist; // will be negative on error
QgsPoint closestLayerVertex = mSelectedFeature->geometry()->closestVertex( layerCoordPoint, atVertex, beforeVertex, afterVertex, sqrDist );

mSnapper.snapToCurrentLayer( e->pos(), snapResults, QgsSnapper::SnapToVertex, tol );
if ( dist <= tol )
if ( sqrDist >= 0 && sqrt( sqrDist ) <= tol )
{
// some vertex selected
mMoving = true;
@@ -825,6 +825,9 @@ double QgsCircularStringV2::closestSegment( const QgsPointV2& pt, QgsPointV2& se
}
}

if ( minDist == std::numeric_limits<double>::max() )
return -1; // error: no segments

segmentPt = minDistSegmentPoint;
vertexAfter = minDistVertexAfter;
vertexAfter.part = 0;
@@ -632,7 +632,7 @@ double QgsCurvePolygonV2::closestSegment( const QgsPointV2& pt, QgsPointV2& segm
{
if ( !mExteriorRing )
{
return 0.0;
return -1;
}
QList<QgsCurveV2*> segmentList;
segmentList.append( mExteriorRing );
@@ -347,6 +347,7 @@ QgsPoint QgsGeometry::closestVertex( const QgsPoint& point, int& atVertex, int&
{
if ( !d->geometry )
{
sqrDist = -1;
return QgsPoint( 0, 0 );
}

@@ -531,12 +532,14 @@ double QgsGeometry::closestVertexWithContext( const QgsPoint& point, int& atVert
{
if ( !d->geometry )
{
return 0.0;
return -1;
}

QgsVertexId vId;
QgsPointV2 pt( point.x(), point.y() );
QgsPointV2 closestPoint = QgsGeometryUtils::closestVertex( *( d->geometry ), pt, vId );
if ( !vId.isValid() )
return -1;
atVertex = vertexNrFromVertexId( vId );
return QgsGeometryUtils::sqrDistance2D( closestPoint, pt );
}
@@ -550,14 +553,16 @@ double QgsGeometry::closestSegmentWithContext(
{
if ( !d->geometry )
{
return 0;
return -1;
}

QgsPointV2 segmentPt;
QgsVertexId vertexAfter;
bool leftOfBool;

double sqrDist = d->geometry->closestSegment( QgsPointV2( point.x(), point.y() ), segmentPt, vertexAfter, &leftOfBool, epsilon );
if ( sqrDist < 0 )
return -1;

minDistPoint.setX( segmentPt.x() );
minDistPoint.setY( segmentPt.y() );
@@ -65,6 +65,7 @@ QgsPointV2 QgsGeometryUtils::closestVertex( const QgsAbstractGeometryV2& geom, c
double minDist = std::numeric_limits<double>::max();
double currentDist = 0;
QgsPointV2 minDistPoint;
id = QgsVertexId(); // set as invalid

QgsVertexId vertexId;
QgsPointV2 vertex;
@@ -37,7 +37,8 @@ class CORE_EXPORT QgsGeometryUtils
*/
static QList<QgsLineStringV2*> extractLineStrings( const QgsAbstractGeometryV2* geom );

/** Returns the closest vertex to a geometry for a specified point
/** Returns the closest vertex to a geometry for a specified point.
* On error null point will be returned and "id" argument will be invalid.
*/
static QgsPointV2 closestVertex( const QgsAbstractGeometryV2& geom, const QgsPointV2& pt, QgsVertexId& id );

@@ -227,7 +228,7 @@ class CORE_EXPORT QgsGeometryUtils
for ( int i = 0; i < container.size(); ++i )
{
sqrDist = container.at( i )->closestSegment( pt, segmentPt, vertexAfter, leftOf, epsilon );
if ( sqrDist < minDist )
if ( sqrDist >= 0 && sqrDist < minDist )
{
minDist = sqrDist;
minDistSegmentX = segmentPt.x();
@@ -257,6 +258,9 @@ class CORE_EXPORT QgsGeometryUtils
}
}

if ( minDist == std::numeric_limits<double>::max() )
return -1; // error: no segments

segmentPt.setX( minDistSegmentX );
segmentPt.setY( minDistSegmentY );
vertexAfter = minDistVertexAfter;
@@ -744,16 +744,10 @@ double QgsLineStringV2::closestSegment( const QgsPointV2& pt, QgsPointV2& segmen
double segmentPtX, segmentPtY;

int size = mX.size();
if ( size == 0 )
if ( size == 0 || size == 1 )
{
vertexAfter = QgsVertexId( 0, 0, 0 );
return sqrDist;
}
else if ( size == 1 )
{
segmentPt = pointN( 0 );
vertexAfter = QgsVertexId( 0, 0, 1 );
return QgsGeometryUtils::sqrDistance2D( pt, segmentPt );
return -1;
}
for ( int i = 1; i < size; ++i )
{
@@ -100,6 +100,8 @@ class QgsPointLocator_VisitorNearestVertex : public IVisitor
int vertexIndex, beforeVertex, afterVertex;
double sqrDist;
QgsPoint pt = geom->closestVertex( mSrcPoint, vertexIndex, beforeVertex, afterVertex, sqrDist );
if ( sqrDist < 0 )
return; // probably empty geometry

QgsPointLocator::Match m( QgsPointLocator::Vertex, mLocator->mLayer, id, sqrt( sqrDist ), pt, vertexIndex );
// in range queries the filter may reject some matches
@@ -2010,11 +2010,10 @@ void TestQgsGeometry::lineStringV2()
//closest segment
QgsLineStringV2 l35;
bool leftOf = false;
p = QgsPointV2(); // reset all coords to zero
( void )l35.closestSegment( QgsPointV2( 1, 2 ), p, v, 0, 0 ); //empty line, just want no crash
l35.setPoints( QgsPointSequenceV2() << QgsPointV2( 5, 10 ) );
QVERIFY( qgsDoubleNear( l35.closestSegment( QgsPointV2( 5, 10 ), p, v, 0, 0 ), 0 ) );
QCOMPARE( p, QgsPointV2( 5, 10 ) );
QCOMPARE( v, QgsVertexId( 0, 0, 1 ) );
QVERIFY( l35.closestSegment( QgsPointV2( 5, 10 ), p, v, 0, 0 ) < 0 );
l35.setPoints( QgsPointSequenceV2() << QgsPointV2( 5, 10 ) << QgsPointV2( 10, 10 ) );
QVERIFY( qgsDoubleNear( l35.closestSegment( QgsPointV2( 4, 11 ), p, v, &leftOf, 0 ), 2.0 ) );
QCOMPARE( p, QgsPointV2( 5, 10 ) );
@@ -23,6 +23,7 @@
#include "qgsgeometry.h"
#include "qgsmaplayerregistry.h"
#include "qgspointlocator.h"
#include "qgspolygonv2.h"


struct FilterExcludePoint : public QgsPointLocator::MatchFilter
@@ -256,6 +257,48 @@ class TestQgsPointLocator : public QObject
QVERIFY( m2.isValid() );
QCOMPARE( m2.point(), QgsPoint( 1, 1 ) );
}

void testNullGeometries()
{
QgsVectorLayer* vlNullGeom = new QgsVectorLayer( "Polygon", "x", "memory" );
QgsFeature ff( 0 );
ff.setGeometry( QgsGeometry() );
QgsFeatureList flist;
flist << ff;
vlNullGeom->dataProvider()->addFeatures( flist );

QgsPointLocator loc( vlNullGeom, 0, nullptr );

QgsPointLocator::Match m1 = loc.nearestVertex( QgsPoint( 2, 2 ), std::numeric_limits<double>::max() );
QVERIFY( !m1.isValid() );

QgsPointLocator::Match m2 = loc.nearestEdge( QgsPoint( 2, 2 ), std::numeric_limits<double>::max() );
QVERIFY( !m2.isValid() );

delete vlNullGeom;
}

void testEmptyGeometries()
{
QgsVectorLayer* vlEmptyGeom = new QgsVectorLayer( "Polygon", "x", "memory" );
QgsFeature ff( 0 );
QgsGeometry g;
g.setGeometry( new QgsPolygonV2() );
ff.setGeometry( g );
QgsFeatureList flist;
flist << ff;
vlEmptyGeom->dataProvider()->addFeatures( flist );

QgsPointLocator loc( vlEmptyGeom, 0, nullptr );

QgsPointLocator::Match m1 = loc.nearestVertex( QgsPoint( 2, 2 ), std::numeric_limits<double>::max() );
QVERIFY( !m1.isValid() );

QgsPointLocator::Match m2 = loc.nearestEdge( QgsPoint( 2, 2 ), std::numeric_limits<double>::max() );
QVERIFY( !m2.isValid() );

delete vlEmptyGeom;
}
};

QTEST_MAIN( TestQgsPointLocator )

0 comments on commit afd04eb

Please sign in to comment.
You can’t perform that action at this time.