Skip to content

Commit

Permalink
Add QgsGeometryUtils method for interpolating point between two point…
Browse files Browse the repository at this point in the history
…s, with z/m handling
  • Loading branch information
nyalldawson committed Aug 15, 2018
1 parent 0faa7ec commit 56fd4e3
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 9 deletions.
3 changes: 2 additions & 1 deletion python/core/auto_generated/geometry/qgsgeometryutils.sip.in
Expand Up @@ -228,9 +228,10 @@ on the line, or exactly in line with the segment) and the result is undefined.

static QgsPoint pointOnLineWithDistance( const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance );
%Docstring
Returns a point a specified distance toward a second point.
Returns a point a specified ``distance`` toward a second point.
%End


static double ccwAngle( double dy, double dx );
%Docstring
Returns the counter clockwise angle between a line with components dx, dy and the line with dx > 0 and dy = 0
Expand Down
33 changes: 26 additions & 7 deletions src/core/geometry/qgsgeometryutils.cpp
Expand Up @@ -542,17 +542,36 @@ int QgsGeometryUtils::leftOfLine( double x, double y, double x1, double y1, doub

QgsPoint QgsGeometryUtils::pointOnLineWithDistance( const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance )
{
double dx = directionPoint.x() - startPoint.x();
double dy = directionPoint.y() - startPoint.y();
double length = std::sqrt( dx * dx + dy * dy );
double x, y;
pointOnLineWithDistance( startPoint.x(), startPoint.y(), directionPoint.x(), directionPoint.y(), distance, x, y );
return QgsPoint( x, y );
}

void QgsGeometryUtils::pointOnLineWithDistance( double x1, double y1, double x2, double y2, double distance, double &x, double &y, double *z1, double *z2, double *z, double *m1, double *m2, double *m )
{
const double dx = x2 - x1;
const double dy = y2 - y1;
const double length = std::sqrt( dx * dx + dy * dy );

if ( qgsDoubleNear( length, 0.0 ) )
{
return startPoint;
x = x1;
y = y1;
if ( z && z1 )
*z = *z1;
if ( m && m1 )
*m = *m1;
}
else
{
const double scaleFactor = distance / length;
x = x1 + dx * scaleFactor;
y = y1 + dy * scaleFactor;
if ( z && z1 && z2 )
*z = *z1 + ( *z2 - *z1 ) * scaleFactor;
if ( m && m1 && m2 )
*m = *m1 + ( *m2 - *m1 ) * scaleFactor;
}

double scaleFactor = distance / length;
return QgsPoint( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
}

double QgsGeometryUtils::ccwAngle( double dy, double dx )
Expand Down
15 changes: 14 additions & 1 deletion src/core/geometry/qgsgeometryutils.h
Expand Up @@ -247,10 +247,23 @@ class CORE_EXPORT QgsGeometryUtils
static int leftOfLine( double x, double y, double x1, double y1, double x2, double y2 );

/**
* Returns a point a specified distance toward a second point.
* Returns a point a specified \a distance toward a second point.
*/
static QgsPoint pointOnLineWithDistance( const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance );

/**
* Calculates the point a specified \a distance from (\a x1, \a y1) toward a second point (\a x2, \a y2).
*
* Optionally, interpolated z and m values can be obtained by specifying the \a z1, \a z2 and \a z arguments
* and/or the \a m1, \a m2, \a m arguments.
*
* \note Not available in Python bindings
* \since QGIS 3.4
*/
static void pointOnLineWithDistance( double x1, double y1, double x2, double y2, double distance, double &x, double &y,
double *z1 = nullptr, double *z2 = nullptr, double *z = nullptr,
double *m1 = nullptr, double *m2 = nullptr, double *m = nullptr ) SIP_SKIP;

//! Returns the counter clockwise angle between a line with components dx, dy and the line with dx > 0 and dy = 0
static double ccwAngle( double dy, double dx );

Expand Down
84 changes: 84 additions & 0 deletions tests/src/core/testqgsgeometryutils.cpp
Expand Up @@ -68,6 +68,7 @@ class TestQgsGeometryUtils: public QObject
void testInterpolatePointOnLineQgsPoint();
void testInterpolatePointOnLine();
void testInterpolatePointOnLineByValue();
void testPointOnLineWithDistance();
};


Expand Down Expand Up @@ -1095,5 +1096,88 @@ void TestQgsGeometryUtils::testInterpolatePointOnLineByValue()
QCOMPARE( p.y(), 0.0 );
}

void TestQgsGeometryUtils::testPointOnLineWithDistance()
{
QgsPoint p = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( 0, 0 ), QgsPoint( 10, 0 ), 0 );
QCOMPARE( p.x(), 0.0 );
QCOMPARE( p.y(), 0.0 );
double x;
double y;
QgsGeometryUtils::pointOnLineWithDistance( 0, 0, 10, 0, 0, x, y );
QCOMPARE( x, 0.0 );
QCOMPARE( y, 0.0 );

p = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( 2, 3 ), QgsPoint( 12, 3 ), 10 );
QCOMPARE( p.x(), 12.0 );
QCOMPARE( p.y(), 3.0 );
QgsGeometryUtils::pointOnLineWithDistance( 2, 3, 12, 3, 10, x, y );
QCOMPARE( x, 12.0 );
QCOMPARE( y, 3.0 );

p = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( 0, 0 ), QgsPoint( 0, 10 ), 0 );
QCOMPARE( p.x(), 0.0 );
QCOMPARE( p.y(), 0.0 );
QgsGeometryUtils::pointOnLineWithDistance( 0, 0, 0, 10, 0, x, y );
QCOMPARE( x, 0.0 );
QCOMPARE( y, 0.0 );

p = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( 0, 0 ), QgsPoint( 0, 10 ), 10 );
QCOMPARE( p.x(), 0.0 );
QCOMPARE( p.y(), 10.0 );
QgsGeometryUtils::pointOnLineWithDistance( 0, 0, 0, 10, 10, x, y );
QCOMPARE( x, 0.0 );
QCOMPARE( y, 10.0 );

p = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( 2, 1 ), QgsPoint( -8, -5 ), 5 );
QGSCOMPARENEAR( p.x(), -2.28746, 0.0001 );
QGSCOMPARENEAR( p.y(), -1.57248, 0.0001 );
QgsGeometryUtils::pointOnLineWithDistance( 2, 1, -8, -5, 5, x, y );
QGSCOMPARENEAR( x, -2.28746, 0.0001 );
QGSCOMPARENEAR( y, -1.57248, 0.0001 );
double z, z1, z2;
double m, m1, m2;
z1 = 12;
z2 = 2;
m1 = 11;
m2 = 15;
QgsGeometryUtils::pointOnLineWithDistance( 2, 1, -8, -5, 5, x, y, &z1, &z2, &z );
QGSCOMPARENEAR( x, -2.28746, 0.0001 );
QGSCOMPARENEAR( y, -1.57248, 0.0001 );
QGSCOMPARENEAR( z, 7.712535, 0.0001 );
QgsGeometryUtils::pointOnLineWithDistance( 2, 1, -8, -5, 5, x, y, nullptr, nullptr, nullptr, &m1, &m2, &m );
QGSCOMPARENEAR( x, -2.28746, 0.0001 );
QGSCOMPARENEAR( y, -1.57248, 0.0001 );
QGSCOMPARENEAR( m, 12.714986, 0.0001 );
z = 0;
m = 0;
QgsGeometryUtils::pointOnLineWithDistance( 2, 1, -8, -5, 5, x, y, &z1, &z2, &z, &m1, &m2, &m );
QGSCOMPARENEAR( x, -2.28746, 0.0001 );
QGSCOMPARENEAR( y, -1.57248, 0.0001 );
QGSCOMPARENEAR( z, 7.712535, 0.0001 );
QGSCOMPARENEAR( m, 12.714986, 0.0001 );

p = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( 0, 0 ), QgsPoint( -10, -6 ), 2 );
QGSCOMPARENEAR( p.x(), -1.71499, 0.0001 );
QGSCOMPARENEAR( p.y(), -1.02899, 0.0001 );
QgsGeometryUtils::pointOnLineWithDistance( 0, 0, -10, -6, 2, x, y );
QGSCOMPARENEAR( x, -1.71499, 0.0001 );
QGSCOMPARENEAR( y, -1.02899, 0.0001 );

p = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( 0, 0 ), QgsPoint( -10, -6 ), 20 );
QGSCOMPARENEAR( p.x(), -17.1499, 0.0001 );
QGSCOMPARENEAR( p.y(), -10.2899, 0.0001 );
QgsGeometryUtils::pointOnLineWithDistance( 0, 0, -10, -6, 20, x, y );
QGSCOMPARENEAR( x, -17.1499, 0.0001 );
QGSCOMPARENEAR( y, -10.2899, 0.0001 );

p = QgsGeometryUtils::pointOnLineWithDistance( QgsPoint( 0, 0 ), QgsPoint( -10, -6 ), -10 );
QGSCOMPARENEAR( p.x(), 8.57493, 0.0001 );
QGSCOMPARENEAR( p.y(), 5.14496, 0.0001 );
QgsGeometryUtils::pointOnLineWithDistance( 0, 0, -10, -6, -10, x, y );
QGSCOMPARENEAR( x, 8.57493, 0.0001 );
QGSCOMPARENEAR( y, 5.14496, 0.0001 );

}

QGSTEST_MAIN( TestQgsGeometryUtils )
#include "testqgsgeometryutils.moc"

0 comments on commit 56fd4e3

Please sign in to comment.