Skip to content

Commit 524d22f

Browse files
committedAug 29, 2016
Add QgsGeometry method to get bisector of angle at vertex
Sponsored by Andreas Neumann (cherry-picked from 986b531)
1 parent 41a9631 commit 524d22f

File tree

6 files changed

+109
-3
lines changed

6 files changed

+109
-3
lines changed
 

‎python/core/geometry/qgsgeometry.sip

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,15 @@ class QgsGeometry
192192
*/
193193
double distanceToVertex( int vertex ) const;
194194

195+
/**
196+
* Returns the bisector angle for this geometry at the specified vertex.
197+
* @param vertex vertex index to calculate bisector angle at
198+
* @returns bisector angle, in radians clockwise from north
199+
* @note added in QGIS 3.0
200+
* @see interpolateAngle()
201+
*/
202+
double angleAtVertex( int vertex ) const;
203+
195204
/**
196205
* Returns the indexes of the vertices before and after the given vertex index.
197206
*
@@ -494,6 +503,7 @@ class QgsGeometry
494503
* of the node is returned.
495504
* @param distance distance along geometry
496505
* @note added in QGIS 3.0
506+
* @see angleAtVertex()
497507
*/
498508
double interpolateAngle( double distance ) const;
499509

‎src/core/geometry/qgsgeometry.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,46 @@ double QgsGeometry::distanceToVertex( int vertex ) const
382382
return QgsGeometryUtils::distanceToVertex( *( d->geometry ), id );
383383
}
384384

385+
double QgsGeometry::angleAtVertex( int vertex ) const
386+
{
387+
if ( !d->geometry )
388+
{
389+
return 0;
390+
}
391+
392+
QgsVertexId v2;
393+
if ( !vertexIdFromVertexNr( vertex, v2 ) )
394+
{
395+
return 0;
396+
}
397+
398+
QgsVertexId v1;
399+
QgsVertexId v3;
400+
QgsGeometryUtils::adjacentVertices( *d->geometry, v2, v1, v3 );
401+
if ( v1.isValid() && v3.isValid() )
402+
{
403+
QgsPointV2 p1 = d->geometry->vertexAt( v1 );
404+
QgsPointV2 p2 = d->geometry->vertexAt( v2 );
405+
QgsPointV2 p3 = d->geometry->vertexAt( v3 );
406+
double angle1 = QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
407+
double angle2 = QgsGeometryUtils::lineAngle( p2.x(), p2.y(), p3.x(), p3.y() );
408+
return QgsGeometryUtils::averageAngle( angle1, angle2 );
409+
}
410+
else if ( v3.isValid() )
411+
{
412+
QgsPointV2 p1 = d->geometry->vertexAt( v2 );
413+
QgsPointV2 p2 = d->geometry->vertexAt( v3 );
414+
return QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
415+
}
416+
else if ( v1.isValid() )
417+
{
418+
QgsPointV2 p1 = d->geometry->vertexAt( v1 );
419+
QgsPointV2 p2 = d->geometry->vertexAt( v2 );
420+
return QgsGeometryUtils::lineAngle( p1.x(), p1.y(), p2.x(), p2.y() );
421+
}
422+
return 0.0;
423+
}
424+
385425
void QgsGeometry::adjacentVertices( int atVertex, int& beforeVertex, int& afterVertex ) const
386426
{
387427
if ( !d->geometry )

‎src/core/geometry/qgsgeometry.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,15 @@ class CORE_EXPORT QgsGeometry
234234
*/
235235
double distanceToVertex( int vertex ) const;
236236

237+
/**
238+
* Returns the bisector angle for this geometry at the specified vertex.
239+
* @param vertex vertex index to calculate bisector angle at
240+
* @returns bisector angle, in radians clockwise from north
241+
* @note added in QGIS 3.0
242+
* @see interpolateAngle()
243+
*/
244+
double angleAtVertex( int vertex ) const;
245+
237246
/**
238247
* Returns the indexes of the vertices before and after the given vertex index.
239248
*
@@ -537,6 +546,7 @@ class CORE_EXPORT QgsGeometry
537546
* of the node is returned.
538547
* @param distance distance along geometry
539548
* @note added in QGIS 3.0
549+
* @see angleAtVertex()
540550
*/
541551
double interpolateAngle( double distance ) const;
542552

‎src/core/geometry/qgsgeometryutils.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,16 @@ void QgsGeometryUtils::adjacentVertices( const QgsAbstractGeometryV2& geom, QgsV
201201
}
202202
else if ( atVertex.vertex == 0 )
203203
{
204-
afterVertex.part = atVertex.part;
205-
afterVertex.ring = atVertex.ring;
206-
afterVertex.vertex = atVertex.vertex + 1;
204+
if ( ring.size() > 1 )
205+
{
206+
afterVertex.part = atVertex.part;
207+
afterVertex.ring = atVertex.ring;
208+
afterVertex.vertex = atVertex.vertex + 1;
209+
}
210+
else
211+
{
212+
afterVertex = QgsVertexId(); //after vertex invalid
213+
}
207214
if ( polygonType && ring.size() > 3 )
208215
{
209216
beforeVertex.part = atVertex.part;

‎tests/src/core/testqgsgeometryutils.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,12 @@ void TestQgsGeometryUtils::testAdjacentVertices()
350350
QgsGeometryUtils::adjacentVertices( polygon1, QgsVertexId( 0, 0, 0 ), previous, next );
351351
QCOMPARE( previous, QgsVertexId( 0, 0, 3 ) );
352352
QCOMPARE( next, QgsVertexId( 0, 0, 1 ) );
353+
354+
// test point - both vertices should be invalid
355+
QgsPointV2 point( 1, 2 );
356+
QgsGeometryUtils::adjacentVertices( point, QgsVertexId( 0, 0, 0 ), previous, next );
357+
QVERIFY( !previous.isValid() );
358+
QVERIFY( !next.isValid() );
353359
}
354360

355361
void TestQgsGeometryUtils::testDistanceToVertex()

‎tests/src/python/test_qgsgeometry.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3481,6 +3481,39 @@ def testInterpolate(self):
34813481
self.assertTrue(compareWkt(result, exp, 0.00001),
34823482
"Interpolate: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
34833483

3484+
def testAngleAtVertex(self):
3485+
""" test QgsGeometry.angleAtVertex """
3486+
3487+
empty = QgsGeometry()
3488+
# just test no crash
3489+
self.assertEqual(empty.angleAtVertex(0), 0)
3490+
3491+
# not a linestring
3492+
point = QgsGeometry.fromWkt('Point(1 2)')
3493+
# no meaning, just test no crash!
3494+
self.assertEqual(point.angleAtVertex(0), 0)
3495+
3496+
# linestring
3497+
linestring = QgsGeometry.fromWkt('LineString(0 0, 10 0, 20 10, 20 20, 10 20)')
3498+
self.assertAlmostEqual(linestring.angleAtVertex(1), math.radians(67.5), places=3)
3499+
self.assertAlmostEqual(linestring.angleAtVertex(2), math.radians(22.5), places=3)
3500+
self.assertAlmostEqual(linestring.angleAtVertex(3), math.radians(315.0), places=3)
3501+
self.assertAlmostEqual(linestring.angleAtVertex(5), 0, places=3)
3502+
self.assertAlmostEqual(linestring.angleAtVertex(-1), 0, places=3)
3503+
3504+
# test first and last points in a linestring - angle should be angle of
3505+
# first/last segment
3506+
linestring = QgsGeometry.fromWkt('LineString(20 0, 10 0, 10 -10)')
3507+
self.assertAlmostEqual(linestring.angleAtVertex(0), math.radians(270), places=3)
3508+
self.assertAlmostEqual(linestring.angleAtVertex(2), math.radians(180), places=3)
3509+
3510+
# polygon
3511+
polygon = QgsGeometry.fromWkt('Polygon((0 0, 10 0, 10 10, 0 10, 0 0))')
3512+
self.assertAlmostEqual(polygon.angleAtVertex(0), math.radians(135.0), places=3)
3513+
self.assertAlmostEqual(polygon.angleAtVertex(1), math.radians(45.0), places=3)
3514+
self.assertAlmostEqual(polygon.angleAtVertex(2), math.radians(315.0), places=3)
3515+
self.assertAlmostEqual(polygon.angleAtVertex(3), math.radians(225.0), places=3)
3516+
self.assertAlmostEqual(polygon.angleAtVertex(4), math.radians(135.0), places=3)
34843517

34853518
if __name__ == '__main__':
34863519
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.