Skip to content

Commit

Permalink
Add geometry methods for calculating the distance to a specified
Browse files Browse the repository at this point in the history
vertex from the start of the geometry
  • Loading branch information
nyalldawson committed Apr 6, 2016
1 parent d464f86 commit 4c704be
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 0 deletions.
8 changes: 8 additions & 0 deletions python/core/geometry/qgsgeometry.sip
Expand Up @@ -184,6 +184,14 @@ class QgsGeometry
//TODO QGIS 3.0 - rename beforeVertex to previousVertex, afterVertex to nextVertex
QgsPoint closestVertex( const QgsPoint& point, int& atVertex /Out/, int& beforeVertex /Out/, int& afterVertex /Out/, double& sqrDist /Out/ ) const;

/**
* Returns the distance along this geometry from its first vertex to the specified vertex.
* @param vertex vertex index to calculate distance to
* @returns distance to vertex (following geometry), or -1 for invalid vertex numbers
* @note added in QGIS 2.16
*/
double distanceToVertex( int vertex ) const;

/**
* Returns the indexes of the vertices before and after the given vertex index.
*
Expand Down
16 changes: 16 additions & 0 deletions src/core/geometry/qgsgeometry.cpp
Expand Up @@ -361,6 +361,22 @@ QgsPoint QgsGeometry::closestVertex( const QgsPoint& point, int& atVertex, int&
return QgsPoint( vp.x(), vp.y() );
}

double QgsGeometry::distanceToVertex( int vertex ) const
{
if ( !d->geometry )
{
return -1;
}

QgsVertexId id;
if ( !vertexIdFromVertexNr( vertex, id ) )
{
return -1;
}

return QgsGeometryUtils::distanceToVertex( *( d->geometry ), id );
}

void QgsGeometry::adjacentVertices( int atVertex, int& beforeVertex, int& afterVertex ) const
{
if ( !d->geometry )
Expand Down
8 changes: 8 additions & 0 deletions src/core/geometry/qgsgeometry.h
Expand Up @@ -226,6 +226,14 @@ class CORE_EXPORT QgsGeometry
//TODO QGIS 3.0 - rename beforeVertex to previousVertex, afterVertex to nextVertex
QgsPoint closestVertex( const QgsPoint& point, int& atVertex, int& beforeVertex, int& afterVertex, double& sqrDist ) const;

/**
* Returns the distance along this geometry from its first vertex to the specified vertex.
* @param vertex vertex index to calculate distance to
* @returns distance to vertex (following geometry), or -1 for invalid vertex numbers
* @note added in QGIS 2.16
*/
double distanceToVertex( int vertex ) const;

/**
* Returns the indexes of the vertices before and after the given vertex index.
*
Expand Down
29 changes: 29 additions & 0 deletions src/core/geometry/qgsgeometryutils.cpp
Expand Up @@ -89,6 +89,35 @@ QgsPointV2 QgsGeometryUtils::closestVertex( const QgsAbstractGeometryV2& geom, c
return minDistPoint;
}

double QgsGeometryUtils::distanceToVertex( const QgsAbstractGeometryV2 &geom, const QgsVertexId &id )
{
double currentDist = 0;
QgsVertexId vertexId;
QgsPointV2 vertex;
QgsPointV2 previousVertex;

bool first = true;
while ( geom.nextVertex( vertexId, vertex ) )
{
if ( !first )
{
currentDist += sqrt( QgsGeometryUtils::sqrDistance2D( previousVertex, vertex ) );

This comment has been minimized.

Copy link
@luipir

luipir Apr 6, 2016

Contributor

https://qgis.org/api/qgsgeometryutils_8cpp_source.html#l00161
does not take into account that QgsPointV2 can belogs to a GeometryV2 geometry with Z:

}

previousVertex = vertex;
first = false;

if ( vertexId == id )
{
//found target vertex
return currentDist;
}
}

//could not find target vertex
return -1;
}

void QgsGeometryUtils::adjacentVertices( const QgsAbstractGeometryV2& geom, QgsVertexId atVertex, QgsVertexId& beforeVertex, QgsVertexId& afterVertex )
{
bool polygonType = ( geom.dimension() == 2 );
Expand Down
8 changes: 8 additions & 0 deletions src/core/geometry/qgsgeometryutils.h
Expand Up @@ -41,6 +41,14 @@ class CORE_EXPORT QgsGeometryUtils
*/
static QgsPointV2 closestVertex( const QgsAbstractGeometryV2& geom, const QgsPointV2& pt, QgsVertexId& id );

/** Returns the distance along a geometry from its first vertex to the specified vertex.
* @param geom geometry
* @param id vertex id to find distance to
* @returns distance to vertex (following geometry)
* @note added in QGIS 2.16
*/
static double distanceToVertex( const QgsAbstractGeometryV2& geom, const QgsVertexId& id );

/** Returns vertices adjacent to a specified vertex within a geometry.
*/
static void adjacentVertices( const QgsAbstractGeometryV2& geom, QgsVertexId atVertex, QgsVertexId& beforeVertex, QgsVertexId& afterVertex );
Expand Down
31 changes: 31 additions & 0 deletions tests/src/core/testqgsgeometryutils.cpp
Expand Up @@ -43,6 +43,7 @@ class TestQgsGeometryUtils: public QObject
void testLinePerpendicularAngle();
void testAverageAngle_data();
void testAverageAngle();
void testDistanceToVertex();
};


Expand Down Expand Up @@ -330,6 +331,36 @@ void TestQgsGeometryUtils::testAverageAngle()
QVERIFY( qgsDoubleNear( averageAngle, expected, 0.0000000001 ) );
}

void TestQgsGeometryUtils::testDistanceToVertex()
{
//test with linestring
QgsLineStringV2* outerRing1 = new QgsLineStringV2();

This comment has been minimized.

Copy link
@luipir

luipir Apr 6, 2016

Contributor

It is intended to test only on 2D without considering Z value ability of the QgsLineStringV2 ?

This comment has been minimized.

Copy link
@nyalldawson

nyalldawson Apr 6, 2016

Author Collaborator

I don't think any of the geometry methods consider z values in distances yet. I think it would need to be an optional extra parameter.

This comment has been minimized.

Copy link
@luipir

luipir Apr 6, 2016

Contributor

the problem I find is that distanceToVertex hide the fact that distances are proyected in a 2d plane. probably a comment in the function description and function renaming (e.g. addig 2d) can defer the Z management.

This comment has been minimized.

Copy link
@nyalldawson

nyalldawson Apr 6, 2016

Author Collaborator

Could we move this discussion to the dev list for wider exposure? I don't work with 3d geometries often enough to have a strong opinion about what the correct behaviour is. I figure there's 4 options:

  • always do 2d calculations
  • always use 3d calculations if z present
  • rename these methods to distanceToVertex2d and add seperate 3d methods (clutters API)
  • add an extra bool parameter for ignoring/considering z values (more confusing API)

This comment has been minimized.

Copy link
@luipir

luipir Apr 7, 2016

Contributor

Other AND option can be deprecate QgsGeometryUtils::sqrDistance2D and add a sqrDistance managing 3d if present

Btw I strongly agree to move the discussion to dev list

This comment has been minimized.

Copy link
@luipir

luipir Apr 10, 2016

Contributor

well seems no one is interested discussing this.

outerRing1->setPoints( QList<QgsPointV2>() << QgsPointV2( 1, 1 ) << QgsPointV2( 1, 2 ) << QgsPointV2( 2, 2 ) << QgsPointV2( 2, 1 ) << QgsPointV2( 1, 1 ) );
QCOMPARE( QgsGeometryUtils::distanceToVertex( *outerRing1, QgsVertexId( 0, 0, 0 ) ), 0.0 );
QCOMPARE( QgsGeometryUtils::distanceToVertex( *outerRing1, QgsVertexId( 0, 0, 1 ) ), 1.0 );
QCOMPARE( QgsGeometryUtils::distanceToVertex( *outerRing1, QgsVertexId( 0, 0, 2 ) ), 2.0 );
QCOMPARE( QgsGeometryUtils::distanceToVertex( *outerRing1, QgsVertexId( 0, 0, 3 ) ), 3.0 );
QCOMPARE( QgsGeometryUtils::distanceToVertex( *outerRing1, QgsVertexId( 0, 0, 4 ) ), 4.0 );
QCOMPARE( QgsGeometryUtils::distanceToVertex( *outerRing1, QgsVertexId( 0, 0, 5 ) ), -1.0 );
QCOMPARE( QgsGeometryUtils::distanceToVertex( *outerRing1, QgsVertexId( 0, 1, 1 ) ), -1.0 );

//test with polygon
QgsPolygonV2 polygon1;
polygon1.setExteriorRing( outerRing1 );
QCOMPARE( QgsGeometryUtils::distanceToVertex( polygon1, QgsVertexId( 0, 0, 0 ) ), 0.0 );
QCOMPARE( QgsGeometryUtils::distanceToVertex( polygon1, QgsVertexId( 0, 0, 1 ) ), 1.0 );
QCOMPARE( QgsGeometryUtils::distanceToVertex( polygon1, QgsVertexId( 0, 0, 2 ) ), 2.0 );
QCOMPARE( QgsGeometryUtils::distanceToVertex( polygon1, QgsVertexId( 0, 0, 3 ) ), 3.0 );
QCOMPARE( QgsGeometryUtils::distanceToVertex( polygon1, QgsVertexId( 0, 0, 4 ) ), 4.0 );
QCOMPARE( QgsGeometryUtils::distanceToVertex( polygon1, QgsVertexId( 0, 0, 5 ) ), -1.0 );
QCOMPARE( QgsGeometryUtils::distanceToVertex( polygon1, QgsVertexId( 0, 1, 1 ) ), -1.0 );

//test with point
QgsPointV2 point( 1, 2 );
QCOMPARE( QgsGeometryUtils::distanceToVertex( point, QgsVertexId( 0, 0, 0 ) ), 0.0 );
QCOMPARE( QgsGeometryUtils::distanceToVertex( point, QgsVertexId( 0, 0, 1 ) ), -1.0 );
}


QTEST_MAIN( TestQgsGeometryUtils )
#include "testqgsgeometryutils.moc"
16 changes: 16 additions & 0 deletions tests/src/python/test_qgsgeometry.py
Expand Up @@ -1837,6 +1837,22 @@ def testAddMValue(self):
wkt = geom.exportToWkt()
assert compareWkt(expWkt, wkt), "addMValue to Point failed: mismatch Expected:\n%s\nGot:\n%s\n" % (expWkt, wkt)

def testDistanceToVertex(self):
""" Test distanceToVertex calculation """
g = QgsGeometry()
self.assertEqual(g.distanceToVertex(0), -1)

g = QgsGeometry.fromWkt('LineString ()')
self.assertEqual(g.distanceToVertex(0), -1)

g = QgsGeometry.fromWkt('Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))')
self.assertEqual(g.distanceToVertex(0), 0)
self.assertEqual(g.distanceToVertex(1), 1)
self.assertEqual(g.distanceToVertex(2), 2)
self.assertEqual(g.distanceToVertex(3), 3)
self.assertEqual(g.distanceToVertex(4), 4)
self.assertEqual(g.distanceToVertex(5), -1)

def testRelates(self):
""" Test relationships between geometries. Note the bulk of these tests were taken from the PostGIS relate testdata """
with open(os.path.join(TEST_DATA_DIR, 'relates_data.csv'), 'r') as d:
Expand Down

0 comments on commit 4c704be

Please sign in to comment.