Skip to content

Commit

Permalink
Add more tests to QgsLineStringV2, fix vertexAngle calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Nov 27, 2015
1 parent 6ab718d commit 1f33011
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 13 deletions.
8 changes: 6 additions & 2 deletions python/core/geometry/qgsabstractgeometryv2.sip
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,12 @@ class QgsAbstractGeometryV2
/** Returns a geometry without curves. Caller takes ownership*/
virtual QgsAbstractGeometryV2* segmentize() const /Factory/;

/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
@return rotation in radians, clockwise from north*/
/** Returns approximate angle at a vertex. This is usually the average angle between adjacent
* segments, and can be pictured as the orientation of a line following the curvature of the
* geometry at the specified vertex.
* @param vertex the vertex id
* @return rotation in radians, clockwise from north
*/
virtual double vertexAngle( const QgsVertexId& vertex ) const = 0;

virtual int vertexCount(int part = 0, int ring = 0) const = 0;
Expand Down
9 changes: 6 additions & 3 deletions src/core/geometry/qgsabstractgeometryv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,12 @@ class CORE_EXPORT QgsAbstractGeometryV2
*/
virtual QgsAbstractGeometryV2* segmentize() const { return clone(); }

/** Returns approximate rotation angle for a vertex. Usually average angle between adjacent segments.
@param vertex the vertex id
@return rotation in radians, clockwise from north*/
/** Returns approximate angle at a vertex. This is usually the average angle between adjacent
* segments, and can be pictured as the orientation of a line following the curvature of the
* geometry at the specified vertex.
* @param vertex the vertex id
* @return rotation in radians, clockwise from north
*/
virtual double vertexAngle( const QgsVertexId& vertex ) const = 0;

virtual int vertexCount( int part = 0, int ring = 0 ) const = 0;
Expand Down
14 changes: 10 additions & 4 deletions src/core/geometry/qgslinestringv2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -705,12 +705,18 @@ void QgsLineStringV2::close()

double QgsLineStringV2::vertexAngle( const QgsVertexId& vertex ) const
{
if ( mX.count() < 2 )
{
//undefined
return 0.0;
}

if ( vertex.vertex == 0 || vertex.vertex >= ( numPoints() - 1 ) )
{
if ( isClosed() )
{
double previousX = mX.at( numPoints() - 1 );
double previousY = mY.at( numPoints() - 1 );
double previousX = mX.at( numPoints() - 2 );
double previousY = mY.at( numPoints() - 2 );
double currentX = mX.at( 0 );
double currentY = mY.at( 0 );
double afterX = mX.at( 1 );
Expand All @@ -719,13 +725,13 @@ double QgsLineStringV2::vertexAngle( const QgsVertexId& vertex ) const
}
else if ( vertex.vertex == 0 )
{
return QgsGeometryUtils::linePerpendicularAngle( mX.at( 0 ), mY.at( 0 ), mX.at( 1 ), mY.at( 1 ) );
return QgsGeometryUtils::lineAngle( mX.at( 0 ), mY.at( 0 ), mX.at( 1 ), mY.at( 1 ) );
}
else
{
int a = numPoints() - 2;
int b = numPoints() - 1;
return QgsGeometryUtils::linePerpendicularAngle( mX.at( a ), mY.at( a ), mX.at( b ), mY.at( b ) );
return QgsGeometryUtils::lineAngle( mX.at( a ), mY.at( a ), mX.at( b ), mY.at( b ) );
}
}
else
Expand Down
87 changes: 83 additions & 4 deletions tests/src/core/testqgsgeometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,10 @@ void TestQgsGeometry::lineStringV2()
QVERIFY( qgsDoubleNear( l21.pointN( 0 ).y(), -39.722, 0.001 ) );
QVERIFY( qgsDoubleNear( l21.pointN( 1 ).x(), 176.959, 0.001 ) );
QVERIFY( qgsDoubleNear( l21.pointN( 1 ).y(), -38.798, 0.001 ) );
QVERIFY( qgsDoubleNear( l21.boundingBox().xMinimum(), 175.771, 0.001 ) );
QVERIFY( qgsDoubleNear( l21.boundingBox().yMinimum(), -39.722, 0.001 ) );
QVERIFY( qgsDoubleNear( l21.boundingBox().xMaximum(), 176.959, 0.001 ) );
QVERIFY( qgsDoubleNear( l21.boundingBox().yMaximum(), -38.798, 0.001 ) );

//3d CRS transform
QgsLineStringV2 l22;
Expand Down Expand Up @@ -1373,7 +1377,7 @@ void TestQgsGeometry::lineStringV2()
l23.transform( qtr );
QCOMPARE( l23.pointN( 0 ), QgsPointV2( QgsWKBTypes::PointZM, 2, 6, 3, 4 ) );
QCOMPARE( l23.pointN( 1 ), QgsPointV2( QgsWKBTypes::PointZM, 22, 36, 13, 14 ) );

QCOMPARE( l23.boundingBox(), QgsRectangle( 2, 6, 22, 36 ) );

//insert vertex

Expand Down Expand Up @@ -1784,7 +1788,12 @@ void TestQgsGeometry::lineStringV2()
QCOMPARE( l34.centroid(), QgsPointV2( 10, 5 ) );
l34.setPoints( QList< QgsPointV2 >() << QgsPointV2( 0, 0 ) << QgsPointV2( 0, 9 ) << QgsPointV2( 2, 9 ) << QgsPointV2( 2, 0 ) );
QCOMPARE( l34.centroid(), QgsPointV2( 1, 4.95 ) );

//linestring with 0 length segment
l34.setPoints( QList< QgsPointV2 >() << QgsPointV2( 0, 0 ) << QgsPointV2( 0, 9 ) << QgsPointV2( 2, 9 ) << QgsPointV2( 2, 9 ) << QgsPointV2( 2, 0 ) );
QCOMPARE( l34.centroid(), QgsPointV2( 1, 4.95 ) );
//linestring with 0 total length segment
l34.setPoints( QList< QgsPointV2 >() << QgsPointV2( 5, 4 ) << QgsPointV2( 5, 4 ) << QgsPointV2( 5, 4 ) );
QCOMPARE( l34.centroid(), QgsPointV2( 5, 4 ) );

//closest segment
QgsLineStringV2 l35;
Expand Down Expand Up @@ -1837,10 +1846,80 @@ void TestQgsGeometry::lineStringV2()
l36.sumUpArea( area );
QVERIFY( qgsDoubleNear( area, 7.0 ) );

//boundingBox

//boundingBox - test that bounding box is updated after every modification to the line string
QgsLineStringV2 l37;
QVERIFY( l37.boundingBox().isNull() );
l37.setPoints( QList< QgsPointV2 >() << QgsPointV2( 5, 10 ) << QgsPointV2( 10, 15 ) );
QCOMPARE( l37.boundingBox(), QgsRectangle( 5, 10, 10, 15 ) );
l37.setPoints( QList< QgsPointV2 >() << QgsPointV2( -5, -10 ) << QgsPointV2( -6, -10 ) << QgsPointV2( -5.5, -9 ) );
QCOMPARE( l37.boundingBox(), QgsRectangle( -6, -10, -5, -9 ) );
//setXAt
l37.setXAt( 2, -4 );
QCOMPARE( l37.boundingBox(), QgsRectangle( -6, -10, -4, -9 ) );
//setYAt
l37.setYAt( 1, -15 );
QCOMPARE( l37.boundingBox(), QgsRectangle( -6, -15, -4, -9 ) );
//append
toAppend.reset( new QgsLineStringV2() );
toAppend->setPoints( QList< QgsPointV2 >() << QgsPointV2( 1, 2 ) << QgsPointV2( 4, 0 ) );
l37.append( toAppend.data() );
QCOMPARE( l37.boundingBox(), QgsRectangle( -6, -15, 4, 2 ) );
l37.addVertex( QgsPointV2( 6, 3 ) );
QCOMPARE( l37.boundingBox(), QgsRectangle( -6, -15, 6, 3 ) );
l37.clear();
QVERIFY( l37.boundingBox().isNull() );
l37.setPoints( QList< QgsPointV2 >() << QgsPointV2( 5, 10 ) << QgsPointV2( 10, 15 ) );
wkb = toAppend->asWkb( size );
l37.fromWkb( wkb );
delete wkb;
wkb = 0;
QCOMPARE( l37.boundingBox(), QgsRectangle( 1, 0, 4, 2 ) );
l37.fromWkt( QString( "LineString( 1 5, 3 4, 6 3 )" ) );
QCOMPARE( l37.boundingBox(), QgsRectangle( 1, 3, 6, 5 ) );
l37.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPointV2( -1, 7 ) );
QCOMPARE( l37.boundingBox(), QgsRectangle( -1, 3, 6, 7 ) );
l37.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPointV2( -3, 10 ) );
QCOMPARE( l37.boundingBox(), QgsRectangle( -3, 3, 6, 10 ) );
l37.deleteVertex( QgsVertexId( 0, 0, 1 ) );
QCOMPARE( l37.boundingBox(), QgsRectangle( 1, 3, 6, 5 ) );

//angle
QgsLineStringV2 l38;
( void )l38.vertexAngle( QgsVertexId() ); //just want no crash
( void )l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ); //just want no crash
l38.setPoints( QList< QgsPointV2 >() << QgsPointV2( 0, 0 ) );
( void )l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ); //just want no crash, any answer is meaningless
l38.setPoints( QList< QgsPointV2 >() << QgsPointV2( 0, 0 ) << QgsPointV2( 1, 0 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 1.5708, 0.0001 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 1.5708, 0.0001 ) );
( void )l38.vertexAngle( QgsVertexId( 0, 0, 2 ) ); //no crash
l38.setPoints( QList< QgsPointV2 >() << QgsPointV2( 0, 0 ) << QgsPointV2( 0, 1 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 0.0 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 0.0 ) );
l38.setPoints( QList< QgsPointV2 >() << QgsPointV2( 1, 0 ) << QgsPointV2( 0, 0 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 4.71239, 0.0001 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 4.71239, 0.0001 ) );
l38.setPoints( QList< QgsPointV2 >() << QgsPointV2( 0, 1 ) << QgsPointV2( 0, 0 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 3.1416, 0.0001 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 3.1416, 0.0001 ) );
l38.setPoints( QList< QgsPointV2 >() << QgsPointV2( 0, 0 ) << QgsPointV2( 1, 0 ) << QgsPointV2( 1, 1 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 1.5708, 0.0001 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 0.7854, 0.0001 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 0.0, 0.0001 ) );
l38.setPoints( QList< QgsPointV2 >() << QgsPointV2( 0, 0 ) << QgsPointV2( 0.5, 0 ) << QgsPointV2( 1, 0 )
<< QgsPointV2( 2, 1 ) << QgsPointV2( 1, 2 ) << QgsPointV2( 0, 2 ) );
( void )l38.vertexAngle( QgsVertexId( 0, 0, 20 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 1.5708, 0.0001 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 1.5708, 0.0001 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 1.17809, 0.00001 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 3 ) ), 0.0, 0.00001 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 4 ) ), 5.10509, 0.00001 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 5 ) ), 4.71239, 0.00001 ) );
//closed line string
l38.close();
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 5 ) ), 3.92699, 0.00001 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 2.35619, 0.00001 ) );
QVERIFY( qgsDoubleNear( l38.vertexAngle( QgsVertexId( 0, 0, 6 ) ), 2.35619, 0.00001 ) );

}

Expand Down

0 comments on commit 1f33011

Please sign in to comment.