Skip to content

Commit 17c8a59

Browse files
committedApr 29, 2020
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
1 parent 752d0ad commit 17c8a59

File tree

4 files changed

+140
-16
lines changed

4 files changed

+140
-16
lines changed
 

‎python/core/auto_generated/geometry/qgslinestring.sip.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ segment in the line.
401401
%End
402402

403403

404+
404405
virtual QString geometryType() const;
405406

406407
virtual int dimension() const;

‎src/core/geometry/qgslinestring.cpp

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -890,37 +890,35 @@ QgsLineString *QgsLineString::reversed() const
890890
return copy;
891891
}
892892

893-
QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
893+
void QgsLineString::visitPointsByRegularDistance( const double distance, const std::function<bool ( double, double, double, double, double, double, double, double, double, double, double, double )> &visitPoint ) const
894894
{
895895
if ( distance < 0 )
896-
return nullptr;
896+
return;
897897

898898
double distanceTraversed = 0;
899899
const int totalPoints = numPoints();
900900
if ( totalPoints == 0 )
901-
return nullptr;
901+
return;
902902

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

908-
QgsWkbTypes::Type pointType = QgsWkbTypes::Point;
909-
if ( is3D() )
910-
pointType = QgsWkbTypes::PointZ;
911-
if ( isMeasure() )
912-
pointType = QgsWkbTypes::addM( pointType );
913-
914908
double prevX = *x++;
915909
double prevY = *y++;
916910
double prevZ = z ? *z++ : 0.0;
917911
double prevM = m ? *m++ : 0.0;
918912

919913
if ( qgsDoubleNear( distance, 0.0 ) )
920914
{
921-
return new QgsPoint( pointType, prevX, prevY, prevZ, prevM );
915+
visitPoint( prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM, prevX, prevY, prevZ, prevM );
916+
return;
922917
}
923918

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

931929
const double segmentLength = std::sqrt( ( thisX - prevX ) * ( thisX - prevX ) + ( thisY - prevY ) * ( thisY - prevY ) );
932-
if ( distance < distanceTraversed + segmentLength || qgsDoubleNear( distance, distanceTraversed + segmentLength ) )
930+
while ( nextPointDistance < distanceTraversed + segmentLength || qgsDoubleNear( nextPointDistance, distanceTraversed + segmentLength ) )
933931
{
934932
// point falls on this segment - truncate to segment length if qgsDoubleNear test was actually > segment length
935-
const double distanceToPoint = std::min( distance - distanceTraversed, segmentLength );
933+
const double distanceToPoint = std::min( nextPointDistance - distanceTraversed, segmentLength );
936934
double pX, pY;
937-
double pZ = 0;
938-
double pM = 0;
939935
QgsGeometryUtils::pointOnLineWithDistance( prevX, prevY, thisX, thisY, distanceToPoint, pX, pY,
940936
z ? &prevZ : nullptr, z ? &thisZ : nullptr, z ? &pZ : nullptr,
941937
m ? &prevM : nullptr, m ? &thisM : nullptr, m ? &pM : nullptr );
942-
return new QgsPoint( pointType, pX, pY, pZ, pM );
938+
939+
if ( !visitPoint( pX, pY, pZ, pM, prevX, prevY, prevZ, prevM, thisX, thisY, thisZ, thisM ) )
940+
return;
941+
942+
nextPointDistance += distance;
943943
}
944944

945945
distanceTraversed += segmentLength;
@@ -948,8 +948,26 @@ QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
948948
prevZ = thisZ;
949949
prevM = thisM;
950950
}
951+
}
952+
953+
QgsPoint *QgsLineString::interpolatePoint( const double distance ) const
954+
{
955+
if ( distance < 0 )
956+
return nullptr;
957+
958+
QgsWkbTypes::Type pointType = QgsWkbTypes::Point;
959+
if ( is3D() )
960+
pointType = QgsWkbTypes::PointZ;
961+
if ( isMeasure() )
962+
pointType = QgsWkbTypes::addM( pointType );
951963

952-
return nullptr;
964+
std::unique_ptr< QgsPoint > res;
965+
visitPointsByRegularDistance( distance, [ & ]( double x, double y, double z, double m, double, double, double, double, double, double, double, double )->bool
966+
{
967+
res = qgis::make_unique< QgsPoint >( pointType, x, y, z, m );
968+
return false;
969+
} );
970+
return res.release();
953971
}
954972

955973
QgsLineString *QgsLineString::curveSubstring( double startDistance, double endDistance ) const

‎src/core/geometry/qgslinestring.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,19 @@ class CORE_EXPORT QgsLineString: public QgsCurve
561561
*/
562562
void extend( double startDistance, double endDistance );
563563

564+
#ifndef SIP_RUN
565+
566+
/**
567+
* Visits regular points along the linestring, spaced by \a distance.
568+
*
569+
* The \a visitPoint function should return FALSE to abort further traversal.
570+
*/
571+
void visitPointsByRegularDistance( double distance, const std::function< bool( double x, double y, double z, double m,
572+
double startSegmentX, double startSegmentY, double startSegmentZ, double startSegmentM,
573+
double endSegmentX, double endSegmentY, double endSegmentZ, double endSegmentM
574+
) > &visitPoint ) const;
575+
#endif
576+
564577
//reimplemented methods
565578

566579
QString geometryType() const override;

‎tests/src/core/testqgsgeometry.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4888,6 +4888,98 @@ void TestQgsGeometry::lineString()
48884888
interpolateResult.reset( interpolate.interpolatePoint( 1 ) );
48894889
QCOMPARE( interpolateResult->asWkt( 2 ), QStringLiteral( "Point (11 3)" ) );
48904890

4891+
// visit points
4892+
QgsLineString visitLine;
4893+
visitLine.visitPointsByRegularDistance( 1, [ = ]( double, double, double, double, double, double, double, double, double, double, double, double )->bool
4894+
{
4895+
return true;
4896+
} ); // no crash
4897+
visitLine.setPoints( QgsPointSequence() << QgsPoint( 11, 2, 3, 4, QgsWkbTypes::PointZM ) << QgsPoint( 11, 12, 13, 14, QgsWkbTypes::PointZM ) << QgsPoint( 111, 12, 23, 24, QgsWkbTypes::PointZM ) );
4898+
int visitCount = 0;
4899+
xx.clear();
4900+
yy.clear();
4901+
zz.clear();
4902+
mm.clear();
4903+
QVector<double> pX, pY, pZ, pM, nX, nY, nZ, nM;
4904+
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
4905+
{
4906+
xx << x;
4907+
yy << y;
4908+
zz << z;
4909+
mm << m;
4910+
pX << ppx;
4911+
pY << ppy;
4912+
pZ << ppz;
4913+
pM << ppm;
4914+
nX << nnx;
4915+
nY << nny;
4916+
nZ << nnz;
4917+
nM << nnm;
4918+
visitCount++;
4919+
return true;
4920+
};
4921+
visitLine.visitPointsByRegularDistance( 0, visitor );
4922+
QCOMPARE( visitCount, 1 );
4923+
QCOMPARE( xx.at( 0 ), 11.0 );
4924+
QCOMPARE( yy.at( 0 ), 2.0 );
4925+
QCOMPARE( zz.at( 0 ), 3.0 );
4926+
QCOMPARE( mm.at( 0 ), 4.0 );
4927+
xx.clear();
4928+
yy.clear();
4929+
zz.clear();
4930+
mm.clear();
4931+
pX.clear();
4932+
pY.clear();
4933+
pZ.clear();
4934+
pM.clear();
4935+
nX.clear();
4936+
nY.clear();
4937+
nZ.clear();
4938+
nM.clear();
4939+
visitCount = 0;
4940+
visitLine.visitPointsByRegularDistance( -1, visitor );
4941+
QCOMPARE( visitCount, 0 );
4942+
visitLine.visitPointsByRegularDistance( 10000, visitor );
4943+
QCOMPARE( visitCount, 0 );
4944+
visitLine.visitPointsByRegularDistance( 30, visitor );
4945+
QCOMPARE( visitCount, 3 );
4946+
QCOMPARE( xx.at( 0 ), 31.0 );
4947+
QCOMPARE( yy.at( 0 ), 12.0 );
4948+
QCOMPARE( zz.at( 0 ), 15.0 );
4949+
QCOMPARE( mm.at( 0 ), 16.0 );
4950+
QCOMPARE( pX.at( 0 ), 11.0 );
4951+
QCOMPARE( pY.at( 0 ), 12.0 );
4952+
QCOMPARE( pZ.at( 0 ), 13.0 );
4953+
QCOMPARE( pM.at( 0 ), 14.0 );
4954+
QCOMPARE( nX.at( 0 ), 111.0 );
4955+
QCOMPARE( nY.at( 0 ), 12.0 );
4956+
QCOMPARE( nZ.at( 0 ), 23.0 );
4957+
QCOMPARE( nM.at( 0 ), 24.0 );
4958+
QCOMPARE( xx.at( 1 ), 61.0 );
4959+
QCOMPARE( yy.at( 1 ), 12.0 );
4960+
QCOMPARE( zz.at( 1 ), 18.0 );
4961+
QCOMPARE( mm.at( 1 ), 19.0 );
4962+
QCOMPARE( pX.at( 1 ), 11.0 );
4963+
QCOMPARE( pY.at( 1 ), 12.0 );
4964+
QCOMPARE( pZ.at( 1 ), 13.0 );
4965+
QCOMPARE( pM.at( 1 ), 14.0 );
4966+
QCOMPARE( nX.at( 1 ), 111.0 );
4967+
QCOMPARE( nY.at( 1 ), 12.0 );
4968+
QCOMPARE( nZ.at( 1 ), 23.0 );
4969+
QCOMPARE( nM.at( 1 ), 24.0 );
4970+
QCOMPARE( xx.at( 2 ), 91.0 );
4971+
QCOMPARE( yy.at( 2 ), 12.0 );
4972+
QCOMPARE( zz.at( 2 ), 21.0 );
4973+
QCOMPARE( mm.at( 2 ), 22.0 );
4974+
QCOMPARE( pX.at( 2 ), 11.0 );
4975+
QCOMPARE( pY.at( 2 ), 12.0 );
4976+
QCOMPARE( pZ.at( 2 ), 13.0 );
4977+
QCOMPARE( pM.at( 2 ), 14.0 );
4978+
QCOMPARE( nX.at( 2 ), 111.0 );
4979+
QCOMPARE( nY.at( 2 ), 12.0 );
4980+
QCOMPARE( nZ.at( 2 ), 23.0 );
4981+
QCOMPARE( nM.at( 2 ), 24.0 );
4982+
48914983
// orientation
48924984
QgsLineString orientation;
48934985
( void )orientation.orientation(); // no crash

0 commit comments

Comments
 (0)
Please sign in to comment.