Skip to content
Permalink
Browse files

QgsGeometryUtils fixes

- add QgsGeometryUtils::normalizedAngle for restricting an angle
to [0, 2PI)
- fix calculations of QgsGeometryUtils::averageAngle, was returning
perpendicular angle and angles > 2Pi
- improve docs and add unit tests
  • Loading branch information
nyalldawson committed Nov 27, 2015
1 parent a0405b8 commit 6ab718d6ace5460bc0c9c20eba7a506c07d36f61
Showing with 96 additions and 30 deletions.
  1. +21 −26 src/core/geometry/qgsgeometryutils.cpp
  2. +30 −4 src/core/geometry/qgsgeometryutils.h
  3. +45 −0 tests/src/core/testqgsgeometry.cpp
@@ -613,6 +613,20 @@ QString QgsGeometryUtils::pointsToJSON( const QList<QgsPointV2>& points, int pre
return json;
}

double QgsGeometryUtils::normalizedAngle( double angle )
{
double clippedAngle = angle;
if ( clippedAngle >= M_PI * 2 || clippedAngle <= -2 * M_PI )
{
clippedAngle = fmod( clippedAngle, 2 * M_PI );
}
if ( clippedAngle < 0.0 )
{
clippedAngle += 2 * M_PI;
}
return clippedAngle;
}

QPair<QgsWKBTypes::Type, QString> QgsGeometryUtils::wktReadBlock( const QString &wkt )
{
QgsWKBTypes::Type wkbType = QgsWKBTypes::parseType( wkt );
@@ -662,38 +676,28 @@ double QgsGeometryUtils::lineAngle( double x1, double y1, double x2, double y2 )
{
double at = atan2( y2 - y1, x2 - x1 );
double a = -at + M_PI / 2.0;
if ( a < 0 )
{
a = 2 * M_PI + a;
}
if ( a >= 2 * M_PI )
{
a -= 2 * M_PI;
}
return a;
return normalizedAngle( a );
}

double QgsGeometryUtils::linePerpendicularAngle( double x1, double y1, double x2, double y2 )
{
double a = lineAngle( x1, y1, x2, y2 );
a += ( M_PI / 2.0 );
if ( a >= 2 * M_PI )
{
a -= ( 2 * M_PI );
}
return a;
return normalizedAngle( a );
}

double QgsGeometryUtils::averageAngle( double x1, double y1, double x2, double y2, double x3, double y3 )
{
// calc average angle between the previous and next point
double a1 = linePerpendicularAngle( x1, y1, x2, y2 );
double a2 = linePerpendicularAngle( x2, y2, x3, y3 );
double a1 = lineAngle( x1, y1, x2, y2 );
double a2 = lineAngle( x2, y2, x3, y3 );
return averageAngle( a1, a2 );
}

double QgsGeometryUtils::averageAngle( double a1, double a2 )
{
a1 = normalizedAngle( a1 );
a2 = normalizedAngle( a2 );
double clockwiseDiff = 0.0;
if ( a2 >= a1 )
{
@@ -714,14 +718,5 @@ double QgsGeometryUtils::averageAngle( double a1, double a2 )
{
resultAngle = a1 - counterClockwiseDiff / 2.0;
}

if ( resultAngle >= 2 * M_PI )
{
resultAngle -= 2 * M_PI;
}
else if ( resultAngle < 0 )
{
resultAngle = 2 * M_PI - resultAngle;
}
return resultAngle;
return normalizedAngle( resultAngle );
}
@@ -151,15 +151,41 @@ class CORE_EXPORT QgsGeometryUtils
static QDomElement pointsToGML3( const QList<QgsPointV2>& points, QDomDocument &doc, int precision, const QString& ns, bool is3D );
/** Returns a geoJSON coordinates string */
static QString pointsToJSON( const QList<QgsPointV2>& points, int precision );
/** Calculates direction of line (clockwise from north direction) in radians*/

/** Ensures that an angle is in the range 0 <= angle < 2 pi.
* @param angle angle in radians
* @returns equivalent angle within the range [0, 2 pi)
*/
static double normalizedAngle( double angle );

/** Calculates the direction of line joining two points in radians, clockwise from the north direction.
* @param x1 x-coordinate of line start
* @param y1 y-coordinate of line start
* @param x2 x-coordinate of line end
* @param y2 y-coordinate of line end
* @returns angle in radians. Returned value is undefined if start and end point are the same.
*/
static double lineAngle( double x1, double y1, double x2, double y2 );
/** Calculates angle perpendicular to line*/

/** Calculates the perpendicular angle to a line joining two points. Returned angle is in radians,
* clockwise from the north direction.
* @param x1 x-coordinate of line start
* @param y1 y-coordinate of line start
* @param x2 x-coordinate of line end
* @param y2 y-coordinate of line end
* @returns angle in radians. Returned value is undefined if start and end point are the same.
*/
static double linePerpendicularAngle( double x1, double y1, double x2, double y2 );

/** Angle between two linear segments*/
static double averageAngle( double x1, double y1, double x2, double y2, double x3, double y3 );
/** Averages two angles*/
static double averageAngle( double a1, double a2 );

/** Averages two angles, correctly handling negative angles and ensuring the result is between 0 and 2 pi.
* @param a1 first angle (in radians)
* @param a2 second angle (in radians)
* @returns average angle (in radians)
*/
static double averageAngle( double a1, double a2 );

/** Parses a WKT block of the format "TYPE( contents )" and returns a pair of geometry type to contents ("Pair(wkbType, "contents")")
*/
@@ -28,6 +28,7 @@
//qgis includes...
#include <qgsapplication.h>
#include <qgsgeometry.h>
#include "qgsgeometryutils.h"
#include <qgspoint.h>
#include "qgspointv2.h"
#include "qgslinestringv2.h"
@@ -56,6 +57,7 @@ class TestQgsGeometry : public QObject
void isEmpty();
void pointV2(); //test QgsPointV2
void lineStringV2(); //test QgsLineStringV2
void utils(); //test QgsGeometryUtils

void fromQgsPoint();
void fromQPoint();
@@ -1842,6 +1844,49 @@ void TestQgsGeometry::lineStringV2()

}

void TestQgsGeometry::utils()
{
//test normalizedAngle
QVERIFY( qgsDoubleNear( QgsGeometryUtils::normalizedAngle( 0.0 ), 0.0, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::normalizedAngle( 1.5708 ), 1.5708, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::normalizedAngle( 3.1416 ), 3.1416, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::normalizedAngle( 4.7124 ), 4.7124, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::normalizedAngle( 2 * M_PI ), 0.0, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::normalizedAngle( 6.80678 ), 0.5236, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::normalizedAngle( 12.5664 ), 0.0, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::normalizedAngle( 12.7409 ), 0.174533, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::normalizedAngle( -0.174533 ), 6.10865, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::normalizedAngle( -6.28318 ), 0.0, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::normalizedAngle( -6.45772 ), 6.10865, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::normalizedAngle( -13.2645 ), 5.58505, 0.0001 ) );

//test lineAngle
( void )QgsGeometryUtils::lineAngle( 0.0, 0.0, 0.0, 0.0 ); //undefined, but don't want a crash
QVERIFY( qgsDoubleNear( QgsGeometryUtils::lineAngle( 0.0, 0.0, 0.0, 1.0 ), 0.0, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::lineAngle( 0.0, 0.0, 1.0, 1.0 ), 0.7854, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::lineAngle( 0.0, 0.0, 1.0, 0.0 ), 1.5708, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::lineAngle( 0.0, 0.0, 0.0, -1.0 ), 3.1416, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::lineAngle( 0.0, 0.0, -1.0, 0.0 ), 4.7124, 0.0001 ) );

//test linePerpendicularAngle
( void )QgsGeometryUtils::linePerpendicularAngle( 0.0, 0.0, 0.0, 0.0 ); //undefined, but don't want a crash
QVERIFY( qgsDoubleNear( QgsGeometryUtils::linePerpendicularAngle( 0.0, 0.0, 0.0, 1.0 ), 1.5708, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::linePerpendicularAngle( 0.0, 0.0, 1.0, 1.0 ), 2.3562, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::linePerpendicularAngle( 0.0, 0.0, 1.0, 0.0 ), 3.1416, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::linePerpendicularAngle( 0.0, 0.0, 0.0, -1.0 ), 4.7124, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::linePerpendicularAngle( 0.0, 0.0, -1.0, 0.0 ), 0.0, 0.0001 ) );

//test averageAngle
QVERIFY( qgsDoubleNear( QgsGeometryUtils::averageAngle( 0.0, 0.0 ), 0.0, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::averageAngle( 0.0, 6.28319 ), 0.0, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::averageAngle( 0.0, 12.5664 ), 0.0, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::averageAngle( 6.28319, 0.0 ), 0.0, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::averageAngle( -6.28318, 0.0 ), 0.0, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::averageAngle( -6.28318, -6.28318 ), 0.0, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::averageAngle( 0.0, 3.141592 ), 1.5708, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::averageAngle( 0.0, -3.141592 ), 4.71239, 0.0001 ) );
QVERIFY( qgsDoubleNear( QgsGeometryUtils::averageAngle( 5.49779, 4.71239 ), 5.1051, 0.0001 ) );
}

void TestQgsGeometry::fromQgsPoint()
{

0 comments on commit 6ab718d

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