Skip to content
Permalink
Browse files

Refactor QgsLineString::interpolatePoint to extract segment traversal

to a separate visitor function

This allows other code paths to utilise this same logic in an efficient
way
  • Loading branch information
nyalldawson committed Apr 29, 2020
1 parent 752d0ad commit 17c8a59e4eb13d454d0f7ff72ed042e385f8cfb8
@@ -401,6 +401,7 @@ segment in the line.
%End



virtual QString geometryType() const;

virtual int dimension() const;
@@ -890,37 +890,35 @@ QgsLineString *QgsLineString::reversed() const
return copy;
}

QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
void QgsLineString::visitPointsByRegularDistance( const double distance, const std::function<bool ( double, double, double, double, double, double, double, double, double, double, double, double )> &visitPoint ) const
{
if ( distance < 0 )
return nullptr;
return;

double distanceTraversed = 0;
const int totalPoints = numPoints();
if ( totalPoints == 0 )
return nullptr;
return;

const double *x = mX.constData();
const double *y = mY.constData();
const double *z = is3D() ? mZ.constData() : nullptr;
const double *m = isMeasure() ? mM.constData() : nullptr;

QgsWkbTypes::Type pointType = QgsWkbTypes::Point;
if ( is3D() )
pointType = QgsWkbTypes::PointZ;
if ( isMeasure() )
pointType = QgsWkbTypes::addM( pointType );

double prevX = *x++;
double prevY = *y++;
double prevZ = z ? *z++ : 0.0;
double prevM = m ? *m++ : 0.0;

if ( qgsDoubleNear( distance, 0.0 ) )
{
return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
visitPoint( prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM );
return;
}

double pZ = std::numeric_limits<double>::quiet_NaN();
double pM = std::numeric_limits<double>::quiet_NaN();
double nextPointDistance = distance;
for ( int i = 1; i < totalPoints; ++i )
{
double thisX = *x++;
@@ -929,17 +927,19 @@ QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
double thisM = m ? *m++ : 0.0;

const double segmentLength = std::sqrt( ( thisX - prevX ) * ( thisX - prevX ) + ( thisY - prevY ) * ( thisY - prevY ) );
if ( distance < distanceTraversed + segmentLength || qgsDoubleNear( distance, distanceTraversed + segmentLength ) )
while ( nextPointDistance < distanceTraversed + segmentLength || qgsDoubleNear( nextPointDistance, distanceTraversed + segmentLength ) )
{
// point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
const double distanceToPoint = std::min( distance - distanceTraversed, segmentLength );
const double distanceToPoint = std::min( nextPointDistance - distanceTraversed, segmentLength );
double pX, pY;
double pZ = 0;
double pM = 0;
QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY,
z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &pZ : nullptr,
m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &pM : nullptr );
return new QgsPoint( pointType, pX, pY, pZ, pM );

if ( !visitPoint( pX, pY, pZ, pM, prevX, prevY, prevZ, prevM, thisX, thisY, thisZ, thisM ) )
return;

nextPointDistance += distance;
}

distanceTraversed += segmentLength;
@@ -948,8 +948,26 @@ QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
prevZ = thisZ;
prevM = thisM;
}
}

QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
{
if ( distance < 0 )
return nullptr;

QgsWkbTypes::Type pointType = QgsWkbTypes::Point;
if ( is3D() )
pointType = QgsWkbTypes::PointZ;
if ( isMeasure() )
pointType = QgsWkbTypes::addM( pointType );

return nullptr;
std::unique_ptr< QgsPoint > res;
visitPointsByRegularDistance( distance, [ & ]( double x, double y, double z, double m, double, double, double, double, double, double, double, double )->bool
{
res = qgis::make_unique< QgsPoint >( pointType, x, y, z, m );
return false;
} );
return res.release();
}

QgsLineString *QgsLineString::curveSubstring( double startDistance, double endDistance ) const
@@ -561,6 +561,19 @@ class CORE_EXPORT QgsLineString: public QgsCurve
*/
void extend( double startDistance, double endDistance );

#ifndef SIP_RUN

/**
* Visits regular points along the linestring, spaced by \a distance.
*
* The \a visitPoint function should return FALSE to abort further traversal.
*/
void visitPointsByRegularDistance( double distance, const std::function< bool( double x, double y, double z, double m,
double startSegmentX, double startSegmentY, double startSegmentZ, double startSegmentM,
double endSegmentX, double endSegmentY, double endSegmentZ, double endSegmentM
) > &visitPoint ) const;
#endif

//reimplemented methods

QString geometryType() const override;
@@ -4888,6 +4888,98 @@ void TestQgsGeometry::lineString()
interpolateResult.reset( interpolate.interpolatePoint( 1 ) );
QCOMPARE( interpolateResult->asWkt( 2 ), QStringLiteral( "Point (11 3)" ) );

// visit points
QgsLineString visitLine;
visitLine.visitPointsByRegularDistance( 1, [ = ]( double, double, double, double, double, double, double, double, double, double, double, double )->bool
{
return true;
} ); // no crash
visitLine.setPoints( QgsPointSequence() << QgsPoint( 11, 2, 3, 4, QgsWkbTypes::PointZM ) << QgsPoint( 11, 12, 13, 14, QgsWkbTypes::PointZM ) << QgsPoint( 111, 12, 23, 24, QgsWkbTypes::PointZM ) );
int visitCount = 0;
xx.clear();
yy.clear();
zz.clear();
mm.clear();
QVector<double> pX, pY, pZ, pM, nX, nY, nZ, nM;
auto visitor = [ & ]( double x, double y, double z, double m, double ppx, double ppy, double ppz, double ppm, double nnx, double nny, double nnz, double nnm )->bool
{
xx << x;
yy << y;
zz << z;
mm << m;
pX << ppx;
pY << ppy;
pZ << ppz;
pM << ppm;
nX << nnx;
nY << nny;
nZ << nnz;
nM << nnm;
visitCount++;
return true;
};
visitLine.visitPointsByRegularDistance( 0, visitor );
QCOMPARE( visitCount, 1 );
QCOMPARE( xx.at( 0 ), 11.0 );
QCOMPARE( yy.at( 0 ), 2.0 );
QCOMPARE( zz.at( 0 ), 3.0 );
QCOMPARE( mm.at( 0 ), 4.0 );
xx.clear();
yy.clear();
zz.clear();
mm.clear();
pX.clear();
pY.clear();
pZ.clear();
pM.clear();
nX.clear();
nY.clear();
nZ.clear();
nM.clear();
visitCount = 0;
visitLine.visitPointsByRegularDistance( -1, visitor );
QCOMPARE( visitCount, 0 );
visitLine.visitPointsByRegularDistance( 10000, visitor );
QCOMPARE( visitCount, 0 );
visitLine.visitPointsByRegularDistance( 30, visitor );
QCOMPARE( visitCount, 3 );
QCOMPARE( xx.at( 0 ), 31.0 );
QCOMPARE( yy.at( 0 ), 12.0 );
QCOMPARE( zz.at( 0 ), 15.0 );
QCOMPARE( mm.at( 0 ), 16.0 );
QCOMPARE( pX.at( 0 ), 11.0 );
QCOMPARE( pY.at( 0 ), 12.0 );
QCOMPARE( pZ.at( 0 ), 13.0 );
QCOMPARE( pM.at( 0 ), 14.0 );
QCOMPARE( nX.at( 0 ), 111.0 );
QCOMPARE( nY.at( 0 ), 12.0 );
QCOMPARE( nZ.at( 0 ), 23.0 );
QCOMPARE( nM.at( 0 ), 24.0 );
QCOMPARE( xx.at( 1 ), 61.0 );
QCOMPARE( yy.at( 1 ), 12.0 );
QCOMPARE( zz.at( 1 ), 18.0 );
QCOMPARE( mm.at( 1 ), 19.0 );
QCOMPARE( pX.at( 1 ), 11.0 );
QCOMPARE( pY.at( 1 ), 12.0 );
QCOMPARE( pZ.at( 1 ), 13.0 );
QCOMPARE( pM.at( 1 ), 14.0 );
QCOMPARE( nX.at( 1 ), 111.0 );
QCOMPARE( nY.at( 1 ), 12.0 );
QCOMPARE( nZ.at( 1 ), 23.0 );
QCOMPARE( nM.at( 1 ), 24.0 );
QCOMPARE( xx.at( 2 ), 91.0 );
QCOMPARE( yy.at( 2 ), 12.0 );
QCOMPARE( zz.at( 2 ), 21.0 );
QCOMPARE( mm.at( 2 ), 22.0 );
QCOMPARE( pX.at( 2 ), 11.0 );
QCOMPARE( pY.at( 2 ), 12.0 );
QCOMPARE( pZ.at( 2 ), 13.0 );
QCOMPARE( pM.at( 2 ), 14.0 );
QCOMPARE( nX.at( 2 ), 111.0 );
QCOMPARE( nY.at( 2 ), 12.0 );
QCOMPARE( nZ.at( 2 ), 23.0 );
QCOMPARE( nM.at( 2 ), 24.0 );

// orientation
QgsLineString orientation;
( void )orientation.orientation(); // no crash

0 comments on commit 17c8a59

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