Skip to content

Commit

Permalink
Add geometry method to calculate mid point on an arc from p1->p2 with…
Browse files Browse the repository at this point in the history
… given center

And improve some docstrings
  • Loading branch information
nyalldawson committed Apr 23, 2018
1 parent 2d7632f commit fbf6b4a
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 6 deletions.
19 changes: 16 additions & 3 deletions python/core/geometry/qgsgeometryutils.sip.in
Expand Up @@ -270,9 +270,21 @@ Calculates angle of a circular string part defined by pt1, pt2, pt3

static bool segmentMidPoint( const QgsPoint &p1, const QgsPoint &p2, QgsPoint &result /Out/, double radius, const QgsPoint &mousePos );
%Docstring
Calculates midpoint on circle passing through p1 and p2, closest to
given coordinate. Z dimension is supported and is retrieved from the
Calculates midpoint on circle passing through ``p1`` and ``p2``, closest to
the given coordinate ``mousePos``. Z dimension is supported and is retrieved from the
first 3D point amongst ``p1`` and ``p2``.

.. seealso:: :py:func:`segmentMidPointFromCenter`
%End

static QgsPoint segmentMidPointFromCenter( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center );
%Docstring
Calculates the midpoint on the circle passing through ``p1`` and ``p2``,
with the specified ``center`` coordinate.

.. versionadded:: 3.2

.. seealso:: :py:func:`segmentMidPoint`
%End

static double circleTangentDirection( const QgsPoint &tangentPoint, const QgsPoint &cp1, const QgsPoint &cp2, const QgsPoint &cp3 );
Expand Down Expand Up @@ -363,7 +375,8 @@ clockwise from the north direction.

static double averageAngle( double x1, double y1, double x2, double y2, double x3, double y3 );
%Docstring
Angle between two linear segments
Calculates the average angle (in radians) between the two linear segments from
(``x1``, ``y1``) to (``x2``, ``y2``) and (``x2``, ``y2``) to (``x3``, ``y3``).
%End

static double averageAngle( double a1, double a2 );
Expand Down
7 changes: 7 additions & 0 deletions src/core/geometry/qgsgeometryutils.cpp
Expand Up @@ -740,6 +740,13 @@ bool QgsGeometryUtils::segmentMidPoint( const QgsPoint &p1, const QgsPoint &p2,
return true;
}

QgsPoint QgsGeometryUtils::segmentMidPointFromCenter( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center )
{
const double midPointAngle = averageAngle( lineAngle( center.x(), center.y(), p1.x(), p1.y() ),
lineAngle( center.x(), center.y(), p2.x(), p2.y() ) );
return center.project( center.distance( p1 ), midPointAngle * 180 / M_PI );
}

double QgsGeometryUtils::circleTangentDirection( const QgsPoint &tangentPoint, const QgsPoint &cp1,
const QgsPoint &cp2, const QgsPoint &cp3 )
{
Expand Down
19 changes: 16 additions & 3 deletions src/core/geometry/qgsgeometryutils.h
Expand Up @@ -277,12 +277,22 @@ class CORE_EXPORT QgsGeometryUtils
static double sweepAngle( double centerX, double centerY, double x1, double y1, double x2, double y2, double x3, double y3 );

/**
* Calculates midpoint on circle passing through p1 and p2, closest to
* given coordinate. Z dimension is supported and is retrieved from the
* Calculates midpoint on circle passing through \a p1 and \a p2, closest to
* the given coordinate \a mousePos. Z dimension is supported and is retrieved from the
* first 3D point amongst \a p1 and \a p2.
* \see segmentMidPointFromCenter()
*/
static bool segmentMidPoint( const QgsPoint &p1, const QgsPoint &p2, QgsPoint &result SIP_OUT, double radius, const QgsPoint &mousePos );

/**
* Calculates the midpoint on the circle passing through \a p1 and \a p2,
* with the specified \a center coordinate.
*
* \since QGIS 3.2
* \see segmentMidPoint()
*/
static QgsPoint segmentMidPointFromCenter( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &center );

//! Calculates the direction angle of a circle tangent (clockwise from north in radians)
static double circleTangentDirection( const QgsPoint &tangentPoint, const QgsPoint &cp1, const QgsPoint &cp2, const QgsPoint &cp3 );

Expand Down Expand Up @@ -386,7 +396,10 @@ class CORE_EXPORT QgsGeometryUtils
*/
static double linePerpendicularAngle( double x1, double y1, double x2, double y2 );

//! Angle between two linear segments
/**
* Calculates the average angle (in radians) between the two linear segments from
* (\a x1, \a y1) to (\a x2, \a y2) and (\a x2, \a y2) to (\a x3, \a y3).
*/
static double averageAngle( double x1, double y1, double x2, double y2, double x3, double y3 );

/**
Expand Down
25 changes: 25 additions & 0 deletions tests/src/core/testqgsgeometryutils.cpp
Expand Up @@ -36,6 +36,7 @@ class TestQgsGeometryUtils: public QObject
void testLeftOfLine();
void testSegmentMidPoint_data();
void testSegmentMidPoint();
void testSegmentMidPointCenter();
void testCircleLength_data();
void testCircleLength();
void testNormalizedAngle_data();
Expand Down Expand Up @@ -214,6 +215,30 @@ void TestQgsGeometryUtils::testSegmentMidPoint()
QGSCOMPARENEAR( midPoint.y(), expectedY, 4 * DBL_EPSILON );
}

void TestQgsGeometryUtils::testSegmentMidPointCenter()
{
QgsPoint mid = QgsGeometryUtils::segmentMidPointFromCenter( QgsPoint( 10, 21 ), QgsPoint( 11, 20 ), QgsPoint( 10, 20 ) );
QGSCOMPARENEAR( mid.x(), 10.7071, 0.0001 );
QGSCOMPARENEAR( mid.y(), 20.7071, 0.0001 );
mid = QgsGeometryUtils::segmentMidPointFromCenter( QgsPoint( 10, 21 ), QgsPoint( 9, 20 ), QgsPoint( 10, 20 ) );
QGSCOMPARENEAR( mid.x(), 9.292893, 0.0001 );
QGSCOMPARENEAR( mid.y(), 20.7071, 0.0001 );
mid = QgsGeometryUtils::segmentMidPointFromCenter( QgsPoint( 10, 21 ), QgsPoint( 10, 19 ), QgsPoint( 10, 20 ) );
QGSCOMPARENEAR( mid.x(), 11.0, 0.0001 );
QGSCOMPARENEAR( mid.y(), 20.0, 0.0001 );
mid = QgsGeometryUtils::segmentMidPointFromCenter( QgsPoint( 10, 21 ), QgsPoint( 10, 21 ), QgsPoint( 10, 20 ) );
QGSCOMPARENEAR( mid.x(), 10.0, 0.0001 );
QGSCOMPARENEAR( mid.y(), 21.0, 0.0001 );
mid = QgsGeometryUtils::segmentMidPointFromCenter( QgsPoint( 10, 21 ), QgsPoint( 10, 21 ), QgsPoint( 10, 21 ) );
QGSCOMPARENEAR( mid.x(), 10.0, 0.0001 );
QGSCOMPARENEAR( mid.y(), 21.0, 0.0001 );
mid = QgsGeometryUtils::segmentMidPointFromCenter( QgsPoint( 10, 21, 3, 4 ), QgsPoint( 11, 20, 5, 6 ), QgsPoint( 10, 20, 7, 8 ) );
QGSCOMPARENEAR( mid.x(), 10.7071, 0.0001 );
QGSCOMPARENEAR( mid.y(), 20.7071, 0.0001 );
QCOMPARE( mid.z(), 7.0 );
QCOMPARE( mid.m(), 8.0 );
}

void TestQgsGeometryUtils::testCircleLength_data()
{
QTest::addColumn<double>( "x1" );
Expand Down

0 comments on commit fbf6b4a

Please sign in to comment.