Skip to content

Commit

Permalink
QgsLineStringV2 fixes
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
nyalldawson committed Nov 26, 2015
1 parent da436f9 commit 74ca290
Show file tree
Hide file tree
Showing 11 changed files with 390 additions and 57 deletions.
10 changes: 10 additions & 0 deletions python/core/geometry/qgsabstractgeometryv2.sip
Expand Up @@ -111,6 +111,16 @@ class QgsAbstractGeometryV2
virtual void coordinateSequence( QList< QList< QList< QgsPointV2 > > >& coord /Out/ ) const = 0;
int nCoordinates() const;
virtual QgsPointV2 vertexAt( const QgsVertexId& id ) const = 0;

/** Searches for the closest segment of the geometry to a given point.
* @param pt specifies the point to find closest segment to
* @param segmentPt storage for the closest point within the geometry
* @param vertexAfter storage for the ID of the vertex at the end of the closest segment
* @param leftOf returns whether the point lies on the left side of the nearest segment (true if point is to left of segment,
* false if point is to right of segment)
* @param epsilon epsilon for segment snapping
* @returns squared distance to closest segment
*/
virtual double closestSegment( const QgsPointV2& pt, QgsPointV2& segmentPt, QgsVertexId& vertexAfter, bool* leftOf, double epsilon ) const = 0;

//low-level editing
Expand Down
2 changes: 2 additions & 0 deletions python/core/geometry/qgslinestringv2.sip
Expand Up @@ -152,6 +152,8 @@ class QgsLineStringV2: public QgsCurveV2
double closestSegment( const QgsPointV2& pt, QgsPointV2& segmentPt, QgsVertexId& vertexAfter, bool* leftOf, double epsilon ) const;
bool pointAt( int i, QgsPointV2& vertex, QgsVertexId::VertexType& type ) const;

virtual QgsPointV2 centroid() const;

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

Expand Down
7 changes: 4 additions & 3 deletions src/core/geometry/qgsabstractgeometryv2.h
Expand Up @@ -222,10 +222,11 @@ class CORE_EXPORT QgsAbstractGeometryV2
virtual QgsPointV2 vertexAt( const QgsVertexId& id ) const = 0;

/** Searches for the closest segment of the geometry to a given point.
* @param pt Specifies the point for search
* @param pt specifies the point to find closest segment to
* @param segmentPt storage for the closest point within the geometry
* @param vertexAfter storage for the id of the vertex after the closest segment
* @param leftOf returns if the point lies on the left of right side of the segment ( < 0 means left, > 0 means right )
* @param vertexAfter storage for the ID of the vertex at the end of the closest segment
* @param leftOf returns whether the point lies on the left side of the nearest segment (true if point is to left of segment,
* false if point is to right of segment)
* @param epsilon epsilon for segment snapping
* @returns squared distance to closest segment
*/
Expand Down
8 changes: 4 additions & 4 deletions src/core/geometry/qgscircularstringv2.cpp
Expand Up @@ -810,14 +810,14 @@ double QgsCircularStringV2::closestSegment( const QgsPointV2& pt, QgsPointV2& se
return minDist;
}

bool QgsCircularStringV2::pointAt( int i, QgsPointV2& vertex, QgsVertexId::VertexType& type ) const
bool QgsCircularStringV2::pointAt( int node, QgsPointV2& point, QgsVertexId::VertexType& type ) const
{
if ( i >= numPoints() )
if ( node >= numPoints() )
{
return false;
}
vertex = pointN( i );
type = ( i % 2 == 0 ) ? QgsVertexId::SegmentVertex : QgsVertexId::CurveVertex;
point = pointN( node );
type = ( node % 2 == 0 ) ? QgsVertexId::SegmentVertex : QgsVertexId::CurveVertex;
return true;
}

Expand Down
2 changes: 1 addition & 1 deletion src/core/geometry/qgscircularstringv2.h
Expand Up @@ -108,7 +108,7 @@ class CORE_EXPORT QgsCircularStringV2: public QgsCurveV2
/**
* @copydoc QgsCurveV2::pointAt()
*/
bool pointAt( int i, QgsPointV2& vertex, QgsVertexId::VertexType& type ) const override;
bool pointAt( int node, QgsPointV2& point, QgsVertexId::VertexType& type ) const override;

/**
* @copydoc QgsCurveV2::sumUpArea()
Expand Down
6 changes: 3 additions & 3 deletions src/core/geometry/qgscompoundcurvev2.cpp
Expand Up @@ -557,15 +557,15 @@ double QgsCompoundCurveV2::closestSegment( const QgsPointV2& pt, QgsPointV2& seg
return QgsGeometryUtils::closestSegmentFromComponents( mCurves, QgsGeometryUtils::VERTEX, pt, segmentPt, vertexAfter, leftOf, epsilon );
}

bool QgsCompoundCurveV2::pointAt( int i, QgsPointV2& vertex, QgsVertexId::VertexType& type ) const
bool QgsCompoundCurveV2::pointAt( int node, QgsPointV2& point, QgsVertexId::VertexType& type ) const
{
int currentVertexId = 0;
for ( int j = 0; j < mCurves.size(); ++j )
{
int nCurvePoints = mCurves.at( j )->numPoints();
if (( i - currentVertexId ) < nCurvePoints )
if (( node - currentVertexId ) < nCurvePoints )
{
return ( mCurves.at( j )->pointAt( i - currentVertexId, vertex, type ) );
return ( mCurves.at( j )->pointAt( node - currentVertexId, point, type ) );
}
currentVertexId += ( nCurvePoints - 1 );
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/geometry/qgscompoundcurvev2.h
Expand Up @@ -95,7 +95,7 @@ class CORE_EXPORT QgsCompoundCurveV2: public QgsCurveV2
virtual bool deleteVertex( const QgsVertexId& position ) override;

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

void sumUpArea( double& sum ) const override;

Expand Down
6 changes: 5 additions & 1 deletion src/core/geometry/qgscurvev2.h
Expand Up @@ -84,8 +84,12 @@ class CORE_EXPORT QgsCurveV2: public QgsAbstractGeometryV2
virtual bool nextVertex( QgsVertexId& id, QgsPointV2& vertex ) const override;

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

/** Returns a reversed copy of the curve, where the direction of the curve has been flipped.
* @note added in QGIS 2.14
Expand Down
61 changes: 57 additions & 4 deletions src/core/geometry/qgslinestringv2.cpp
Expand Up @@ -24,6 +24,7 @@
#include <QPainter>
#include <limits>
#include <QDomDocument>
#include <QtCore/qmath.h>

QgsLineStringV2::QgsLineStringV2(): QgsCurveV2()
{
Expand Down Expand Up @@ -573,6 +574,17 @@ double QgsLineStringV2::closestSegment( const QgsPointV2& pt, QgsPointV2& segmen
double segmentPtX, segmentPtY;

int size = mX.size();
if ( size == 0 )
{
vertexAfter = QgsVertexId( 0, 0, 0 );
return sqrDist;
}
else if ( size == 1 )
{
segmentPt = pointN( 0 );
vertexAfter = QgsVertexId( 0, 0, 1 );
return QgsGeometryUtils::sqrDistance2D( pt, segmentPt );
}
for ( int i = 1; i < size; ++i )
{
double prevX = mX.at( i - 1 );
Expand All @@ -587,28 +599,69 @@ double QgsLineStringV2::closestSegment( const QgsPointV2& pt, QgsPointV2& segmen
segmentPt.setY( segmentPtY );
if ( leftOf )
{
*leftOf = ( QgsGeometryUtils::leftOfLine( segmentPtX, segmentPtY, prevX, prevY, pt.x(), pt.y() ) < 0 );
*leftOf = ( QgsGeometryUtils::leftOfLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY ) < 0 );
}
vertexAfter.part = 0; vertexAfter.ring = 0; vertexAfter.vertex = i;
}
}
return sqrDist;
}

bool QgsLineStringV2::pointAt( int i, QgsPointV2& vertex, QgsVertexId::VertexType& type ) const
bool QgsLineStringV2::pointAt( int node, QgsPointV2& point, QgsVertexId::VertexType& type ) const
{
if ( i >= numPoints() )
if ( node < 0 || node >= numPoints() )
{
return false;
}
vertex = pointN( i );
point = pointN( node );
type = QgsVertexId::SegmentVertex;
return true;
}

QgsPointV2 QgsLineStringV2::centroid() const
{
if ( mX.isEmpty() )
return QgsPointV2();

int numPoints = mX.count();
if ( numPoints == 1 )
return QgsPointV2( mX.at( 0 ), mY.at( 0 ) );

double totalLineLength = 0.0;
double prevX = mX.at( 0 );
double prevY = mY.at( 0 );
double sumX = 0.0;
double sumY = 0.0;

for ( int i = 1; i < numPoints ; ++i )
{
double currentX = mX.at( i );
double currentY = mY.at( i );
double segmentLength = sqrt( qPow( currentX - prevX, 2.0 ) +
qPow( currentY - prevY, 2.0 ) );
if ( qgsDoubleNear( segmentLength, 0.0 ) )
continue;

totalLineLength += segmentLength;
sumX += segmentLength * 0.5 * ( currentX + prevX );
sumY += segmentLength * 0.5 * ( currentY + prevY );
prevX = currentX;
prevY = currentY;
}

if ( qgsDoubleNear( totalLineLength, 0.0 ) )
return QgsPointV2( mX.at( 0 ), mY.at( 0 ) );
else
return QgsPointV2( sumX / totalLineLength, sumY / totalLineLength );

}

void QgsLineStringV2::sumUpArea( double& sum ) const
{
int maxIndex = numPoints() - 1;
if ( maxIndex == 1 )
return; //no area, just a single line

for ( int i = 0; i < maxIndex; ++i )
{
sum += 0.5 * ( mX.at( i ) * mY.at( i + 1 ) - mY.at( i ) * mX.at( i + 1 ) );
Expand Down
4 changes: 3 additions & 1 deletion src/core/geometry/qgslinestringv2.h
Expand Up @@ -171,7 +171,9 @@ class CORE_EXPORT QgsLineStringV2: public QgsCurveV2
virtual QgsLineStringV2* reversed() const override;

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

virtual QgsPointV2 centroid() const override;

void sumUpArea( double& sum ) const override;
double vertexAngle( const QgsVertexId& vertex ) const override;
Expand Down

0 comments on commit 74ca290

Please sign in to comment.