Skip to content

Commit 74ca290

Browse files
committed
QgsLineStringV2 fixes
- fix incorrect centroid calculation (was always returning 0,0) - fix closestSegment when numPoints < 2 - fix broken leftOf calculation for closestSegment - area calculation when numPoints < 2 Plus add more unit tests
1 parent da436f9 commit 74ca290

File tree

11 files changed

+390
-57
lines changed

11 files changed

+390
-57
lines changed

python/core/geometry/qgsabstractgeometryv2.sip

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,16 @@ class QgsAbstractGeometryV2
111111
virtual void coordinateSequence( QList< QList< QList< QgsPointV2 > > >& coord /Out/ ) const = 0;
112112
int nCoordinates() const;
113113
virtual QgsPointV2 vertexAt( const QgsVertexId& id ) const = 0;
114+
115+
/** Searches for the closest segment of the geometry to a given point.
116+
* @param pt specifies the point to find closest segment to
117+
* @param segmentPt storage for the closest point within the geometry
118+
* @param vertexAfter storage for the ID of the vertex at the end of the closest segment
119+
* @param leftOf returns whether the point lies on the left side of the nearest segment (true if point is to left of segment,
120+
* false if point is to right of segment)
121+
* @param epsilon epsilon for segment snapping
122+
* @returns squared distance to closest segment
123+
*/
114124
virtual double closestSegment( const QgsPointV2& pt, QgsPointV2& segmentPt, QgsVertexId& vertexAfter, bool* leftOf, double epsilon ) const = 0;
115125

116126
//low-level editing

python/core/geometry/qgslinestringv2.sip

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ class QgsLineStringV2: public QgsCurveV2
152152
double closestSegment( const QgsPointV2& pt, QgsPointV2& segmentPt, QgsVertexId& vertexAfter, bool* leftOf, double epsilon ) const;
153153
bool pointAt( int i, QgsPointV2& vertex, QgsVertexId::VertexType& type ) const;
154154

155+
virtual QgsPointV2 centroid() const;
156+
155157
void sumUpArea( double& sum ) const;
156158
double vertexAngle( const QgsVertexId& vertex ) const;
157159

src/core/geometry/qgsabstractgeometryv2.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,11 @@ class CORE_EXPORT QgsAbstractGeometryV2
222222
virtual QgsPointV2 vertexAt( const QgsVertexId& id ) const = 0;
223223

224224
/** Searches for the closest segment of the geometry to a given point.
225-
* @param pt Specifies the point for search
225+
* @param pt specifies the point to find closest segment to
226226
* @param segmentPt storage for the closest point within the geometry
227-
* @param vertexAfter storage for the id of the vertex after the closest segment
228-
* @param leftOf returns if the point lies on the left of right side of the segment ( < 0 means left, > 0 means right )
227+
* @param vertexAfter storage for the ID of the vertex at the end of the closest segment
228+
* @param leftOf returns whether the point lies on the left side of the nearest segment (true if point is to left of segment,
229+
* false if point is to right of segment)
229230
* @param epsilon epsilon for segment snapping
230231
* @returns squared distance to closest segment
231232
*/

src/core/geometry/qgscircularstringv2.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -810,14 +810,14 @@ double QgsCircularStringV2::closestSegment( const QgsPointV2& pt, QgsPointV2& se
810810
return minDist;
811811
}
812812

813-
bool QgsCircularStringV2::pointAt( int i, QgsPointV2& vertex, QgsVertexId::VertexType& type ) const
813+
bool QgsCircularStringV2::pointAt( int node, QgsPointV2& point, QgsVertexId::VertexType& type ) const
814814
{
815-
if ( i >= numPoints() )
815+
if ( node >= numPoints() )
816816
{
817817
return false;
818818
}
819-
vertex = pointN( i );
820-
type = ( i % 2 == 0 ) ? QgsVertexId::SegmentVertex : QgsVertexId::CurveVertex;
819+
point = pointN( node );
820+
type = ( node % 2 == 0 ) ? QgsVertexId::SegmentVertex : QgsVertexId::CurveVertex;
821821
return true;
822822
}
823823

src/core/geometry/qgscircularstringv2.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class CORE_EXPORT QgsCircularStringV2: public QgsCurveV2
108108
/**
109109
* @copydoc QgsCurveV2::pointAt()
110110
*/
111-
bool pointAt( int i, QgsPointV2& vertex, QgsVertexId::VertexType& type ) const override;
111+
bool pointAt( int node, QgsPointV2& point, QgsVertexId::VertexType& type ) const override;
112112

113113
/**
114114
* @copydoc QgsCurveV2::sumUpArea()

src/core/geometry/qgscompoundcurvev2.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -557,15 +557,15 @@ double QgsCompoundCurveV2::closestSegment( const QgsPointV2& pt, QgsPointV2& seg
557557
return QgsGeometryUtils::closestSegmentFromComponents( mCurves, QgsGeometryUtils::VERTEX, pt, segmentPt, vertexAfter, leftOf, epsilon );
558558
}
559559

560-
bool QgsCompoundCurveV2::pointAt( int i, QgsPointV2& vertex, QgsVertexId::VertexType& type ) const
560+
bool QgsCompoundCurveV2::pointAt( int node, QgsPointV2& point, QgsVertexId::VertexType& type ) const
561561
{
562562
int currentVertexId = 0;
563563
for ( int j = 0; j < mCurves.size(); ++j )
564564
{
565565
int nCurvePoints = mCurves.at( j )->numPoints();
566-
if (( i - currentVertexId ) < nCurvePoints )
566+
if (( node - currentVertexId ) < nCurvePoints )
567567
{
568-
return ( mCurves.at( j )->pointAt( i - currentVertexId, vertex, type ) );
568+
return ( mCurves.at( j )->pointAt( node - currentVertexId, point, type ) );
569569
}
570570
currentVertexId += ( nCurvePoints - 1 );
571571
}

src/core/geometry/qgscompoundcurvev2.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class CORE_EXPORT QgsCompoundCurveV2: public QgsCurveV2
9595
virtual bool deleteVertex( const QgsVertexId& position ) override;
9696

9797
virtual double closestSegment( const QgsPointV2& pt, QgsPointV2& segmentPt, QgsVertexId& vertexAfter, bool* leftOf, double epsilon ) const override;
98-
bool pointAt( int i, QgsPointV2& vertex, QgsVertexId::VertexType& type ) const override;
98+
bool pointAt( int node, QgsPointV2& point, QgsVertexId::VertexType& type ) const override;
9999

100100
void sumUpArea( double& sum ) const override;
101101

src/core/geometry/qgscurvev2.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,12 @@ class CORE_EXPORT QgsCurveV2: public QgsAbstractGeometryV2
8484
virtual bool nextVertex( QgsVertexId& id, QgsPointV2& vertex ) const override;
8585

8686
/** Returns the point and vertex id of a point within the curve.
87+
* @param node node number, where the first node is 0
88+
* @param point will be set to point at corresponding node in the curve
89+
* @param type will be set to the vertex type of the node
90+
* @returns true if node exists within the curve
8791
*/
88-
virtual bool pointAt( int i, QgsPointV2& vertex, QgsVertexId::VertexType& type ) const = 0;
92+
virtual bool pointAt( int node, QgsPointV2& point, QgsVertexId::VertexType& type ) const = 0;
8993

9094
/** Returns a reversed copy of the curve, where the direction of the curve has been flipped.
9195
* @note added in QGIS 2.14

src/core/geometry/qgslinestringv2.cpp

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <QPainter>
2525
#include <limits>
2626
#include <QDomDocument>
27+
#include <QtCore/qmath.h>
2728

2829
QgsLineStringV2::QgsLineStringV2(): QgsCurveV2()
2930
{
@@ -573,6 +574,17 @@ double QgsLineStringV2::closestSegment( const QgsPointV2& pt, QgsPointV2& segmen
573574
double segmentPtX, segmentPtY;
574575

575576
int size = mX.size();
577+
if ( size == 0 )
578+
{
579+
vertexAfter = QgsVertexId( 0, 0, 0 );
580+
return sqrDist;
581+
}
582+
else if ( size == 1 )
583+
{
584+
segmentPt = pointN( 0 );
585+
vertexAfter = QgsVertexId( 0, 0, 1 );
586+
return QgsGeometryUtils::sqrDistance2D( pt, segmentPt );
587+
}
576588
for ( int i = 1; i < size; ++i )
577589
{
578590
double prevX = mX.at( i - 1 );
@@ -587,28 +599,69 @@ double QgsLineStringV2::closestSegment( const QgsPointV2& pt, QgsPointV2& segmen
587599
segmentPt.setY( segmentPtY );
588600
if ( leftOf )
589601
{
590-
*leftOf = ( QgsGeometryUtils::leftOfLine( segmentPtX, segmentPtY, prevX, prevY, pt.x(), pt.y() ) < 0 );
602+
*leftOf = ( QgsGeometryUtils::leftOfLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY ) < 0 );
591603
}
592604
vertexAfter.part = 0; vertexAfter.ring = 0; vertexAfter.vertex = i;
593605
}
594606
}
595607
return sqrDist;
596608
}
597609

598-
bool QgsLineStringV2::pointAt( int i, QgsPointV2& vertex, QgsVertexId::VertexType& type ) const
610+
bool QgsLineStringV2::pointAt( int node, QgsPointV2& point, QgsVertexId::VertexType& type ) const
599611
{
600-
if ( i >= numPoints() )
612+
if ( node < 0 || node >= numPoints() )
601613
{
602614
return false;
603615
}
604-
vertex = pointN( i );
616+
point = pointN( node );
605617
type = QgsVertexId::SegmentVertex;
606618
return true;
607619
}
608620

621+
QgsPointV2 QgsLineStringV2::centroid() const
622+
{
623+
if ( mX.isEmpty() )
624+
return QgsPointV2();
625+
626+
int numPoints = mX.count();
627+
if ( numPoints == 1 )
628+
return QgsPointV2( mX.at( 0 ), mY.at( 0 ) );
629+
630+
double totalLineLength = 0.0;
631+
double prevX = mX.at( 0 );
632+
double prevY = mY.at( 0 );
633+
double sumX = 0.0;
634+
double sumY = 0.0;
635+
636+
for ( int i = 1; i < numPoints ; ++i )
637+
{
638+
double currentX = mX.at( i );
639+
double currentY = mY.at( i );
640+
double segmentLength = sqrt( qPow( currentX - prevX, 2.0 ) +
641+
qPow( currentY - prevY, 2.0 ) );
642+
if ( qgsDoubleNear( segmentLength, 0.0 ) )
643+
continue;
644+
645+
totalLineLength += segmentLength;
646+
sumX += segmentLength * 0.5 * ( currentX + prevX );
647+
sumY += segmentLength * 0.5 * ( currentY + prevY );
648+
prevX = currentX;
649+
prevY = currentY;
650+
}
651+
652+
if ( qgsDoubleNear( totalLineLength, 0.0 ) )
653+
return QgsPointV2( mX.at( 0 ), mY.at( 0 ) );
654+
else
655+
return QgsPointV2( sumX / totalLineLength, sumY / totalLineLength );
656+
657+
}
658+
609659
void QgsLineStringV2::sumUpArea( double& sum ) const
610660
{
611661
int maxIndex = numPoints() - 1;
662+
if ( maxIndex == 1 )
663+
return; //no area, just a single line
664+
612665
for ( int i = 0; i < maxIndex; ++i )
613666
{
614667
sum += 0.5 * ( mX.at( i ) * mY.at( i + 1 ) - mY.at( i ) * mX.at( i + 1 ) );

src/core/geometry/qgslinestringv2.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,9 @@ class CORE_EXPORT QgsLineStringV2: public QgsCurveV2
171171
virtual QgsLineStringV2* reversed() const override;
172172

173173
double closestSegment( const QgsPointV2& pt, QgsPointV2& segmentPt, QgsVertexId& vertexAfter, bool* leftOf, double epsilon ) const override;
174-
bool pointAt( int i, QgsPointV2& vertex, QgsVertexId::VertexType& type ) const override;
174+
bool pointAt( int node, QgsPointV2& point, QgsVertexId::VertexType& type ) const override;
175+
176+
virtual QgsPointV2 centroid() const override;
175177

176178
void sumUpArea( double& sum ) const override;
177179
double vertexAngle( const QgsVertexId& vertex ) const override;

0 commit comments

Comments
 (0)