Skip to content

Commit e4ce623

Browse files
authoredNov 29, 2017
Merge pull request #5758 from nyalldawson/leftof
Geometry "leftOf" improvements
2 parents 62dbf4c + b8a62f4 commit e4ce623

32 files changed

+168
-144
lines changed
 

‎doc/api_break.dox

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,9 @@ QgsAbstractGeometry {#qgis_api_break_3_0_QgsAbstractGeometry}
456456
- asGML2() was renamed to asGml2()
457457
- asGML3() was renamed to asGml3()
458458
- asJSON() was renamed to asJson()
459+
- closestSegment() now returns an integer value for the leftOf test (-1 if point is to the left
460+
of the geometry, +1 if the point is to the right of the geometry, or 0 for cases where left/right could not
461+
be determined, e.g. point exactly on a line)
459462

460463

461464
QgsActionManager {#qgis_api_break_3_0_QgsActionManager}
@@ -1338,6 +1341,7 @@ maintains Z or M dimensions from the input points and is more efficient.
13381341
- fromMultiPolygon() was renamed to fromMultiPolygonXY()
13391342
- exportToWkt() was renamed to asWkt()
13401343
- exportToGeoJSON() was renamed to asJson()
1344+
- closestSegmentWithContext() now returns an extra value, indicating whether the point is to the left of the geometry
13411345

13421346

13431347
QgsGeometryAnalyzer {#qgis_api_break_3_0_QgsGeometryAnalyzer}

‎python/core/geometry/qgsabstractgeometry.sip

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,13 +299,15 @@ class QgsAbstractGeometry
299299

300300
virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt /Out/,
301301
QgsVertexId &vertexAfter /Out/,
302-
bool *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const = 0;
302+
int *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const = 0;
303303
%Docstring
304304
Searches for the closest segment of the geometry to a given point.
305305
\param pt specifies the point to find closest segment to
306306
\param segmentPt storage for the closest point within the geometry
307307
\param vertexAfter storage for the ID of the vertex at the end of the closest segment
308-
\param leftOf returns whether the point lies on the left side of the nearest segment (true if point is to left of segment,
308+
\param leftOf indicates whether the point lies on the left side of the geometry (-1 if point is to the left
309+
of the geometry, +1 if the point is to the right of the geometry, or 0 for cases where left/right could not
310+
be determined, e.g. point exactly on a line)
309311
false if point is to right of segment)
310312
\param epsilon epsilon for segment snapping
311313
:return: squared distance to closest segment or negative value on error

‎python/core/geometry/qgscircularstring.sip

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ class QgsCircularString: QgsCurve
9999

100100
virtual bool deleteVertex( QgsVertexId position );
101101

102-
virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt /Out/, QgsVertexId &vertexAfter /Out/, bool *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const;
102+
virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt /Out/, QgsVertexId &vertexAfter /Out/, int *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const;
103103

104104
virtual bool pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const;
105105

‎python/core/geometry/qgscompoundcurve.sip

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ class QgsCompoundCurve: QgsCurve
124124

125125
virtual bool deleteVertex( QgsVertexId position );
126126

127-
virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt /Out/, QgsVertexId &vertexAfter /Out/, bool *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const;
127+
virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt /Out/, QgsVertexId &vertexAfter /Out/, int *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const;
128128

129129
virtual bool pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const;
130130

‎python/core/geometry/qgscurvepolygon.sip

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ Adds an interior ring to the geometry (takes ownership)
149149

150150
virtual bool isEmpty() const;
151151

152-
virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt /Out/, QgsVertexId &vertexAfter /Out/, bool *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const;
152+
virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt /Out/, QgsVertexId &vertexAfter /Out/, int *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const;
153153

154154

155155
virtual bool nextVertex( QgsVertexId &id, QgsPoint &vertex /Out/ ) const;

‎python/core/geometry/qgsgeometry.sip

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -493,14 +493,15 @@ Returns true if WKB of the geometry is of WKBMulti* type
493493
:rtype: float
494494
%End
495495

496-
double closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint /Out/, int &afterVertex /Out/ ) const;
496+
double closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint /Out/, int &afterVertex /Out/, int *leftOf /Out/ = 0, double epsilon = DEFAULT_SEGMENT_EPSILON ) const;
497497
%Docstring
498498
Searches for the closest segment of geometry to the given point
499499
\param point Specifies the point for search
500500
\param minDistPoint Receives the nearest point on the segment
501501
\param afterVertex Receives index of the vertex after the closest segment. The vertex
502502
before the closest segment is always afterVertex - 1
503-
\param leftOf Out: Returns if the point lies on the left of right side of the segment ( < 0 means left, > 0 means right )
503+
\param leftOf Out: Returns if the point lies on the left of left side of the geometry ( < 0 means left, > 0 means right, 0 indicates
504+
that the test was unsuccesful, e.g. for a point exactly on the line)
504505
\param epsilon epsilon for segment snapping
505506
:return: The squared Cartesian distance is also returned in sqrDist, negative number on error
506507
:rtype: float

‎python/core/geometry/qgsgeometrycollection.sip

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ Adds a geometry and takes ownership. Returns true in case of success.
112112
virtual int nCoordinates() const;
113113

114114

115-
virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt /Out/, QgsVertexId &vertexAfter /Out/, bool *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const;
115+
virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt /Out/, QgsVertexId &vertexAfter /Out/, int *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const;
116116

117117
virtual bool nextVertex( QgsVertexId &id, QgsPoint &vertex /Out/ ) const;
118118

‎python/core/geometry/qgsgeometryutils.sip

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,14 @@ class QgsGeometryUtils
123123

124124

125125

126-
static double leftOfLine( double x, double y, double x1, double y1, double x2, double y2 );
126+
static int leftOfLine( double x, double y, double x1, double y1, double x2, double y2 );
127127
%Docstring
128128
Returns a value < 0 if the point (``x``, ``y``) is left of the line from (``x1``, ``y1``) -> ( ``x2``, ``y2``).
129129
A positive return value indicates the point is to the right of the line.
130130

131131
If the return value is 0, then the test was unsuccessful (e.g. due to testing a point exactly
132132
on the line, or exactly in line with the segment) and the result is undefined.
133-
:rtype: float
133+
:rtype: int
134134
%End
135135

136136
static QgsPoint pointOnLineWithDistance( const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance );

‎python/core/geometry/qgslinestring.sip

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ Closes the line string by appending the first point to the end of the line, if i
243243
virtual QgsLineString *reversed() const /Factory/;
244244

245245

246-
virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt /Out/, QgsVertexId &vertexAfter /Out/, bool *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const;
246+
virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt /Out/, QgsVertexId &vertexAfter /Out/, int *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const;
247247

248248
virtual bool pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const;
249249

‎python/core/geometry/qgspoint.sip

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ class QgsPoint: QgsAbstractGeometry
377377
virtual bool deleteVertex( QgsVertexId position );
378378

379379

380-
virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt /Out/, QgsVertexId &vertexAfter /Out/, bool *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const;
380+
virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt /Out/, QgsVertexId &vertexAfter /Out/, int *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const;
381381

382382
virtual bool nextVertex( QgsVertexId &id, QgsPoint &vertex /Out/ ) const;
383383

‎src/app/qgsmaptooloffsetcurve.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ void QgsMapToolOffsetCurve::canvasMoveEvent( QgsMapMouseEvent *e )
242242

243243
QgsPointXY minDistPoint;
244244
int beforeVertex;
245-
double leftOf;
245+
int leftOf = 0;
246246
double offset = std::sqrt( mOriginalGeometry.closestSegmentWithContext( layerCoords, minDistPoint, beforeVertex, &leftOf ) );
247247
if ( offset == 0.0 )
248248
{
@@ -254,13 +254,13 @@ void QgsMapToolOffsetCurve::canvasMoveEvent( QgsMapMouseEvent *e )
254254
if ( mDistanceWidget )
255255
{
256256
// this will also set the rubber band
257-
mDistanceWidget->setValue( leftOf < 0 ? -offset : offset );
257+
mDistanceWidget->setValue( leftOf < 0 ? offset : -offset );
258258
mDistanceWidget->setFocus( Qt::TabFocusReason );
259259
}
260260
else
261261
{
262262
//create offset geometry using geos
263-
setOffsetForRubberBand( leftOf < 0 ? -offset : offset );
263+
setOffsetForRubberBand( leftOf < 0 ? offset : -offset );
264264
}
265265
}
266266

‎src/app/qgsmaptoolrectangle3points.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ void QgsMapToolRectangle3Points::cadCanvasMoveEvent( QgsMapMouseEvent *e )
7676
{
7777

7878
setDistance2( mPoints.at( 1 ).distance( mapPoint ) );
79-
double side = QgsGeometryUtils::leftOfLine( mapPoint.x(), mapPoint.y(),
80-
mPoints.at( 0 ).x(), mPoints.at( 0 ).y(),
81-
mPoints.at( 1 ).x(), mPoints.at( 1 ).y() );
79+
int side = QgsGeometryUtils::leftOfLine( mapPoint.x(), mapPoint.y(),
80+
mPoints.at( 0 ).x(), mPoints.at( 0 ).y(),
81+
mPoints.at( 1 ).x(), mPoints.at( 1 ).y() );
8282

8383
setSide( side < 0 ? -1 : 1 );
8484

‎src/core/geometry/qgsabstractgeometry.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,14 +324,16 @@ class CORE_EXPORT QgsAbstractGeometry
324324
* \param pt specifies the point to find closest segment to
325325
* \param segmentPt storage for the closest point within the geometry
326326
* \param vertexAfter storage for the ID of the vertex at the end of the closest segment
327-
* \param leftOf returns whether the point lies on the left side of the nearest segment (true if point is to left of segment,
327+
* \param leftOf indicates whether the point lies on the left side of the geometry (-1 if point is to the left
328+
* of the geometry, +1 if the point is to the right of the geometry, or 0 for cases where left/right could not
329+
* be determined, e.g. point exactly on a line)
328330
* false if point is to right of segment)
329331
* \param epsilon epsilon for segment snapping
330332
* \returns squared distance to closest segment or negative value on error
331333
*/
332334
virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT,
333335
QgsVertexId &vertexAfter SIP_OUT,
334-
bool *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const = 0;
336+
int *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const = 0;
335337

336338
//low-level editing
337339

‎src/core/geometry/qgscircularstring.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -732,12 +732,12 @@ void QgsCircularString::deleteVertex( int i )
732732
clearCache();
733733
}
734734

735-
double QgsCircularString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, bool *leftOf, double epsilon ) const
735+
double QgsCircularString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
736736
{
737737
double minDist = std::numeric_limits<double>::max();
738738
QgsPoint minDistSegmentPoint;
739739
QgsVertexId minDistVertexAfter;
740-
bool minDistLeftOf = false;
740+
int minDistLeftOf = 0;
741741

742742
double currentDist = 0.0;
743743

@@ -766,7 +766,7 @@ double QgsCircularString::closestSegment( const QgsPoint &pt, QgsPoint &segmentP
766766
vertexAfter.ring = 0;
767767
if ( leftOf )
768768
{
769-
*leftOf = minDistLeftOf;
769+
*leftOf = qgsDoubleNear( minDist, 0.0 ) ? 0 : minDistLeftOf;
770770
}
771771
return minDist;
772772
}
@@ -849,7 +849,7 @@ bool QgsCircularString::hasCurvedSegments() const
849849
}
850850

851851
double QgsCircularString::closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
852-
const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, bool *leftOf, double epsilon )
852+
const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon )
853853
{
854854
double radius, centerX, centerY;
855855
QgsPoint pt1( x1, y1 );
@@ -892,7 +892,8 @@ double QgsCircularString::closestPointOnArc( double x1, double y1, double x2, do
892892
if ( leftOf )
893893
{
894894
double sqrDistancePointToCenter = ( pt.x() - centerX ) * ( pt.x() - centerX ) + ( pt.y() - centerY ) * ( pt.y() - centerY );
895-
*leftOf = clockwise ? sqrDistancePointToCenter > radius * radius : sqrDistancePointToCenter < radius * radius;
895+
*leftOf = clockwise ? ( sqrDistancePointToCenter > radius * radius ? -1 : 1 )
896+
: ( sqrDistancePointToCenter < radius * radius ? -1 : 1 );
896897
}
897898

898899
return sqrDistance;

‎src/core/geometry/qgscircularstring.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class CORE_EXPORT QgsCircularString: public QgsCurve
8282
bool insertVertex( QgsVertexId position, const QgsPoint &vertex ) override;
8383
bool moveVertex( QgsVertexId position, const QgsPoint &newPos ) override;
8484
bool deleteVertex( QgsVertexId position ) override;
85-
double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT, QgsVertexId &vertexAfter SIP_OUT, bool *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const override;
85+
double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT, QgsVertexId &vertexAfter SIP_OUT, int *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const override;
8686
bool pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const override;
8787
void sumUpArea( double &sum SIP_OUT ) const override;
8888
bool hasCurvedSegments() const override;
@@ -129,7 +129,7 @@ class CORE_EXPORT QgsCircularString: public QgsCurve
129129
static QgsRectangle segmentBoundingBox( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3 );
130130
static QgsPointSequence compassPointsOnSegment( double p1Angle, double p2Angle, double p3Angle, double centerX, double centerY, double radius );
131131
static double closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
132-
const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, bool *leftOf, double epsilon );
132+
const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon );
133133
void insertVertexBetween( int after, int before, int pointOnCircle );
134134
void deleteVertex( int i );
135135

‎src/core/geometry/qgscompoundcurve.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ QVector< QPair<int, QgsVertexId> > QgsCompoundCurve::curveVertexId( QgsVertexId
699699
return curveIds;
700700
}
701701

702-
double QgsCompoundCurve::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, bool *leftOf, double epsilon ) const
702+
double QgsCompoundCurve::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
703703
{
704704
return QgsGeometryUtils::closestSegmentFromComponents( mCurves, QgsGeometryUtils::Vertex, pt, segmentPt, vertexAfter, leftOf, epsilon );
705705
}

‎src/core/geometry/qgscompoundcurve.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ class CORE_EXPORT QgsCompoundCurve: public QgsCurve
105105
bool insertVertex( QgsVertexId position, const QgsPoint &vertex ) override;
106106
bool moveVertex( QgsVertexId position, const QgsPoint &newPos ) override;
107107
bool deleteVertex( QgsVertexId position ) override;
108-
double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT, QgsVertexId &vertexAfter SIP_OUT, bool *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const override;
108+
double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT, QgsVertexId &vertexAfter SIP_OUT, int *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const override;
109109
bool pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const override;
110110
void sumUpArea( double &sum SIP_OUT ) const override;
111111

‎src/core/geometry/qgscurvepolygon.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ bool QgsCurvePolygon::isEmpty() const
798798
return mExteriorRing->isEmpty();
799799
}
800800

801-
double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, bool *leftOf, double epsilon ) const
801+
double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
802802
{
803803
if ( !mExteriorRing )
804804
{

‎src/core/geometry/qgscurvepolygon.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
121121
int nCoordinates() const override;
122122
int vertexNumberFromVertexId( QgsVertexId id ) const override;
123123
bool isEmpty() const override;
124-
double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT, QgsVertexId &vertexAfter SIP_OUT, bool *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const override;
124+
double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT, QgsVertexId &vertexAfter SIP_OUT, int *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const override;
125125

126126
bool nextVertex( QgsVertexId &id, QgsPoint &vertex SIP_OUT ) const override;
127127
void adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex SIP_OUT, QgsVertexId &nextVertex SIP_OUT ) const override;

‎src/core/geometry/qgsgeometry.cpp

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -584,12 +584,11 @@ double QgsGeometry::closestVertexWithContext( const QgsPointXY &point, int &atVe
584584
return QgsGeometryUtils::sqrDistance2D( closestPoint, pt );
585585
}
586586

587-
double QgsGeometry::closestSegmentWithContext(
588-
const QgsPointXY &point,
589-
QgsPointXY &minDistPoint,
590-
int &afterVertex,
591-
double *leftOf,
592-
double epsilon ) const
587+
double QgsGeometry::closestSegmentWithContext( const QgsPointXY &point,
588+
QgsPointXY &minDistPoint,
589+
int &afterVertex,
590+
int *leftOf,
591+
double epsilon ) const
593592
{
594593
if ( !d->geometry )
595594
{
@@ -598,19 +597,14 @@ double QgsGeometry::closestSegmentWithContext(
598597

599598
QgsPoint segmentPt;
600599
QgsVertexId vertexAfter;
601-
bool leftOfBool;
602600

603-
double sqrDist = d->geometry->closestSegment( QgsPoint( point.x(), point.y() ), segmentPt, vertexAfter, &leftOfBool, epsilon );
601+
double sqrDist = d->geometry->closestSegment( QgsPoint( point.x(), point.y() ), segmentPt, vertexAfter, leftOf, epsilon );
604602
if ( sqrDist < 0 )
605603
return -1;
606604

607605
minDistPoint.setX( segmentPt.x() );
608606
minDistPoint.setY( segmentPt.y() );
609607
afterVertex = vertexNrFromVertexId( vertexAfter );
610-
if ( leftOf )
611-
{
612-
*leftOf = leftOfBool ? 1.0 : -1.0;
613-
}
614608
return sqrDist;
615609
}
616610

‎src/core/geometry/qgsgeometry.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -555,15 +555,12 @@ class CORE_EXPORT QgsGeometry
555555
* \param minDistPoint Receives the nearest point on the segment
556556
* \param afterVertex Receives index of the vertex after the closest segment. The vertex
557557
* before the closest segment is always afterVertex - 1
558-
* \param leftOf Out: Returns if the point lies on the left of right side of the segment ( < 0 means left, > 0 means right )
558+
* \param leftOf Out: Returns if the point lies on the left of left side of the geometry ( < 0 means left, > 0 means right, 0 indicates
559+
* that the test was unsuccesful, e.g. for a point exactly on the line)
559560
* \param epsilon epsilon for segment snapping
560561
* \returns The squared Cartesian distance is also returned in sqrDist, negative number on error
561562
*/
562-
#ifndef SIP_RUN
563-
double closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint, int &afterVertex, double *leftOf = nullptr, double epsilon = DEFAULT_SEGMENT_EPSILON ) const;
564-
#else
565-
double closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint SIP_OUT, int &afterVertex SIP_OUT ) const;
566-
#endif
563+
double closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint SIP_OUT, int &afterVertex SIP_OUT, int *leftOf SIP_OUT = nullptr, double epsilon = DEFAULT_SEGMENT_EPSILON ) const;
567564

568565
/**
569566
* Adds a new ring to this geometry. This makes only sense for polygon and multipolygons.

‎src/core/geometry/qgsgeometrycollection.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ int QgsGeometryCollection::nCoordinates() const
455455
return count;
456456
}
457457

458-
double QgsGeometryCollection::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, bool *leftOf, double epsilon ) const
458+
double QgsGeometryCollection::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
459459
{
460460
return QgsGeometryUtils::closestSegmentFromComponents( mGeometries, QgsGeometryUtils::Part, pt, segmentPt, vertexAfter, leftOf, epsilon );
461461
}

‎src/core/geometry/qgsgeometrycollection.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
105105
QgsCoordinateSequence coordinateSequence() const override;
106106
int nCoordinates() const override;
107107

108-
double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT, QgsVertexId &vertexAfter SIP_OUT, bool *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const override;
108+
double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT, QgsVertexId &vertexAfter SIP_OUT, int *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const override;
109109
bool nextVertex( QgsVertexId &id, QgsPoint &vertex SIP_OUT ) const override;
110110

111111
//low-level editing

‎src/core/geometry/qgsgeometryutils.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,7 @@ QgsPoint QgsGeometryUtils::closestPoint( const QgsAbstractGeometry &geometry, co
9898
{
9999
QgsPoint closestPoint;
100100
QgsVertexId vertexAfter;
101-
bool leftOf;
102-
geometry.closestSegment( point, closestPoint, vertexAfter, &leftOf, DEFAULT_SEGMENT_EPSILON );
101+
geometry.closestSegment( point, closestPoint, vertexAfter, nullptr, DEFAULT_SEGMENT_EPSILON );
103102
if ( vertexAfter.isValid() )
104103
{
105104
QgsPoint pointAfter = geometry.vertexAt( vertexAfter );
@@ -316,13 +315,15 @@ QVector<QgsGeometryUtils::SelfIntersection> QgsGeometryUtils::getSelfIntersectio
316315
return intersections;
317316
}
318317

319-
double QgsGeometryUtils::leftOfLine( double x, double y, double x1, double y1, double x2, double y2 )
318+
int QgsGeometryUtils::leftOfLine( double x, double y, double x1, double y1, double x2, double y2 )
320319
{
321320
double f1 = x - x1;
322321
double f2 = y2 - y1;
323322
double f3 = y - y1;
324323
double f4 = x2 - x1;
325-
return f1 * f2 - f3 * f4;
324+
double test = ( f1 * f2 - f3 * f4 );
325+
// return -1, 0, or 1
326+
return qgsDoubleNear( test, 0.0 ) ? 0 : ( test < 0 ? -1 : 1 );
326327
}
327328

328329
QgsPoint QgsGeometryUtils::pointOnLineWithDistance( const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance )

‎src/core/geometry/qgsgeometryutils.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ class CORE_EXPORT QgsGeometryUtils
154154
* If the return value is 0, then the test was unsuccessful (e.g. due to testing a point exactly
155155
* on the line, or exactly in line with the segment) and the result is undefined.
156156
*/
157-
static double leftOfLine( double x, double y, double x1, double y1, double x2, double y2 );
157+
static int leftOfLine( double x, double y, double x1, double y1, double x2, double y2 );
158158

159159
/**
160160
* Returns a point a specified distance toward a second point.
@@ -380,12 +380,12 @@ class CORE_EXPORT QgsGeometryUtils
380380
};
381381

382382
//! \note not available in Python bindings
383-
template<class T> static double closestSegmentFromComponents( T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, bool *leftOf, double epsilon ) SIP_SKIP
383+
template<class T> static double closestSegmentFromComponents( T &container, ComponentType ctype, const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) SIP_SKIP
384384
{
385385
double minDist = std::numeric_limits<double>::max();
386386
double minDistSegmentX = 0.0, minDistSegmentY = 0.0;
387387
QgsVertexId minDistVertexAfter;
388-
bool minDistLeftOf = false;
388+
int minDistLeftOf = 0;
389389
double sqrDist = 0.0;
390390
int vertexOffset = 0;
391391
int ringOffset = 0;

‎src/core/geometry/qgslinestring.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -900,18 +900,18 @@ void QgsLineString::addVertex( const QgsPoint &pt )
900900
clearCache(); //set bounding box invalid
901901
}
902902

903-
double QgsLineString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, bool *leftOf, double epsilon ) const
903+
double QgsLineString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
904904
{
905905
double sqrDist = std::numeric_limits<double>::max();
906906
double leftOfDist = std::numeric_limits<double>::max();
907-
bool prevLeftOf = false;
907+
int prevLeftOf = 0;
908908
double prevLeftOfX;
909909
double prevLeftOfY;
910910
double testDist = 0;
911911
double segmentPtX, segmentPtY;
912912

913913
if ( leftOf )
914-
*leftOf = false;
914+
*leftOf = 0;
915915

916916
int size = mX.size();
917917
if ( size == 0 || size == 1 )
@@ -937,29 +937,35 @@ double QgsLineString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt,
937937
}
938938
if ( leftOf && qgsDoubleNear( testDist, sqrDist ) )
939939
{
940-
double left = QgsGeometryUtils::leftOfLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY );
940+
int left = QgsGeometryUtils::leftOfLine( pt.x(), pt.y(), prevX, prevY, currentX, currentY );
941941
// if left equals 0, the test could not be performed (e.g. point in line with segment or on segment)
942942
// so don't set leftOf in this case, and hope that there's another segment that's the same distance
943943
// where we can perform the check
944-
if ( !qgsDoubleNear( left, 0 ) )
944+
if ( left != 0 )
945945
{
946-
if ( qgsDoubleNear( testDist, leftOfDist ) && ( left < 0 ) != prevLeftOf )
946+
if ( qgsDoubleNear( testDist, leftOfDist ) && left != prevLeftOf && prevLeftOf != 0 )
947947
{
948948
// we have two possible segments each with equal distance to point, but they disagree
949949
// on whether or not the point is to the left of them.
950950
// so we test the segments themselves and flip the result.
951951
// see https://stackoverflow.com/questions/10583212/elegant-left-of-test-for-polyline
952-
*leftOf = QgsGeometryUtils::leftOfLine( currentX, currentY, prevLeftOfX, prevLeftOfY, prevX, prevY ) > 0;
952+
*leftOf = -QgsGeometryUtils::leftOfLine( currentX, currentY, prevLeftOfX, prevLeftOfY, prevX, prevY );
953953
}
954954
else
955955
{
956-
*leftOf = left < 0;
956+
*leftOf = left;
957957
}
958958
prevLeftOf = *leftOf;
959959
leftOfDist = testDist;
960960
prevLeftOfX = prevX;
961961
prevLeftOfY = prevY;
962962
}
963+
else if ( testDist < leftOfDist )
964+
{
965+
*leftOf = left;
966+
leftOfDist = testDist;
967+
prevLeftOf = 0;
968+
}
963969
}
964970
}
965971
return sqrDist;

‎src/core/geometry/qgslinestring.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ class CORE_EXPORT QgsLineString: public QgsCurve
221221

222222
QgsLineString *reversed() const override SIP_FACTORY;
223223

224-
double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT, QgsVertexId &vertexAfter SIP_OUT, bool *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const override;
224+
double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT, QgsVertexId &vertexAfter SIP_OUT, int *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const override;
225225
bool pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const override;
226226

227227
QgsPoint centroid() const override;

‎src/core/geometry/qgspoint.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,12 +392,13 @@ bool QgsPoint::deleteVertex( QgsVertexId position )
392392
return false;
393393
}
394394

395-
double QgsPoint::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, bool *leftOf, double epsilon ) const
395+
double QgsPoint::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
396396
{
397397
Q_UNUSED( pt );
398398
Q_UNUSED( segmentPt );
399399
Q_UNUSED( vertexAfter );
400-
Q_UNUSED( leftOf );
400+
if ( leftOf )
401+
*leftOf = 0;
401402
Q_UNUSED( epsilon );
402403
return -1; // no segments - return error
403404
}

‎src/core/geometry/qgspoint.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ class CORE_EXPORT QgsPoint: public QgsAbstractGeometry
414414
bool moveVertex( QgsVertexId position, const QgsPoint &newPos ) override;
415415
bool deleteVertex( QgsVertexId position ) override;
416416

417-
double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT, QgsVertexId &vertexAfter SIP_OUT, bool *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const override;
417+
double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT, QgsVertexId &vertexAfter SIP_OUT, int *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const override;
418418
bool nextVertex( QgsVertexId &id, QgsPoint &vertex SIP_OUT ) const override;
419419
void adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex SIP_OUT, QgsVertexId &nextVertex SIP_OUT ) const override;
420420

‎src/core/geometry/qgstriangle.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ bool QgsTriangle::isDegenerate()
403403
QgsPoint p1( vertexAt( 0 ) );
404404
QgsPoint p2( vertexAt( 1 ) );
405405
QgsPoint p3( vertexAt( 2 ) );
406-
return ( ( ( p1 == p2 ) || ( p1 == p3 ) || ( p2 == p3 ) ) || qgsDoubleNear( QgsGeometryUtils::leftOfLine( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() ), 0.0 ) );
406+
return ( ( ( p1 == p2 ) || ( p1 == p3 ) || ( p2 == p3 ) ) || QgsGeometryUtils::leftOfLine( p1.x(), p1.y(), p2.x(), p2.y(), p3.x(), p3.y() ) == 0 );
407407
}
408408

409409
bool QgsTriangle::isIsocele( double lengthTolerance ) const

‎tests/src/core/testqgsgeometry.cpp

Lines changed: 74 additions & 66 deletions
Large diffs are not rendered by default.

‎tests/src/python/test_qgsgeometry.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -499,10 +499,11 @@ def testClosestVertex(self):
499499
self.assertEqual(afterVertex, 5)
500500
self.assertEqual(dist, 1)
501501

502-
(dist, minDistPoint, afterVertex) = polyline.closestSegmentWithContext(QgsPointXY(6, 2))
502+
(dist, minDistPoint, afterVertex, leftOf) = polyline.closestSegmentWithContext(QgsPointXY(6, 2))
503503
self.assertEqual(dist, 1)
504504
self.assertEqual(minDistPoint, QgsPointXY(5, 2))
505505
self.assertEqual(afterVertex, 4)
506+
self.assertEqual(leftOf, -1)
506507

507508
(point, atVertex, beforeVertex, afterVertex, dist) = polyline.closestVertex(QgsPointXY(6, 0))
508509
self.assertEqual(point, QgsPointXY(5, 0))
@@ -511,10 +512,11 @@ def testClosestVertex(self):
511512
self.assertEqual(afterVertex, 1)
512513
self.assertEqual(dist, 1)
513514

514-
(dist, minDistPoint, afterVertex) = polyline.closestSegmentWithContext(QgsPointXY(6, 0))
515+
(dist, minDistPoint, afterVertex, leftOf) = polyline.closestSegmentWithContext(QgsPointXY(6, 0))
515516
self.assertEqual(dist, 1)
516517
self.assertEqual(minDistPoint, QgsPointXY(5, 0))
517518
self.assertEqual(afterVertex, 1)
519+
self.assertEqual(leftOf, 0)
518520

519521
(point, atVertex, beforeVertex, afterVertex, dist) = polyline.closestVertex(QgsPointXY(0, -1))
520522
self.assertEqual(point, QgsPointXY(0, 0))
@@ -523,10 +525,11 @@ def testClosestVertex(self):
523525
self.assertEqual(afterVertex, 2)
524526
self.assertEqual(dist, 1)
525527

526-
(dist, minDistPoint, afterVertex) = polyline.closestSegmentWithContext(QgsPointXY(0, 1))
528+
(dist, minDistPoint, afterVertex, leftOf) = polyline.closestSegmentWithContext(QgsPointXY(0, 1))
527529
self.assertEqual(dist, 0)
528530
self.assertEqual(minDistPoint, QgsPointXY(0, 1))
529531
self.assertEqual(afterVertex, 2)
532+
self.assertEqual(leftOf, 0)
530533

531534
# 2-3 6-+-7 !
532535
# | | | |
@@ -544,10 +547,11 @@ def testClosestVertex(self):
544547
self.assertEqual(afterVertex, 8)
545548
self.assertEqual(dist, 1)
546549

547-
(dist, minDistPoint, afterVertex) = polyline.closestSegmentWithContext(QgsPointXY(7, 0))
550+
(dist, minDistPoint, afterVertex, leftOf) = polyline.closestSegmentWithContext(QgsPointXY(7, 0))
548551
self.assertEqual(dist, 1)
549552
self.assertEqual(minDistPoint, QgsPointXY(6, 0))
550553
self.assertEqual(afterVertex, 9)
554+
self.assertEqual(leftOf, 0)
551555

552556
# 5---4
553557
# |! |
@@ -566,11 +570,12 @@ def testClosestVertex(self):
566570
self.assertEqual(afterVertex, 3)
567571
assert abs(dist - 0.1) < 0.00001, "Expected: %f; Got:%f" % (dist, 0.1)
568572

569-
(dist, minDistPoint, afterVertex) = polygon.closestSegmentWithContext(QgsPointXY(0.7, 1.1))
573+
(dist, minDistPoint, afterVertex, leftOf) = polygon.closestSegmentWithContext(QgsPointXY(0.7, 1.1))
570574
self.assertEqual(afterVertex, 2)
571575
self.assertEqual(minDistPoint, QgsPointXY(1, 1))
572576
exp = 0.3 ** 2 + 0.1 ** 2
573577
assert abs(dist - exp) < 0.00001, "Expected: %f; Got:%f" % (exp, dist)
578+
self.assertEqual(leftOf, -1)
574579

575580
# 3-+-+-2
576581
# | |
@@ -592,11 +597,12 @@ def testClosestVertex(self):
592597
self.assertEqual(afterVertex, 9)
593598
assert abs(dist - 0.02) < 0.00001, "Expected: %f; Got:%f" % (dist, 0.02)
594599

595-
(dist, minDistPoint, afterVertex) = polygon.closestSegmentWithContext(QgsPointXY(1.2, 1.9))
600+
(dist, minDistPoint, afterVertex, leftOf) = polygon.closestSegmentWithContext(QgsPointXY(1.2, 1.9))
596601
self.assertEqual(afterVertex, 8)
597602
self.assertEqual(minDistPoint, QgsPointXY(1.2, 2))
598603
exp = 0.01
599604
assert abs(dist - exp) < 0.00001, "Expected: %f; Got:%f" % (exp, dist)
605+
self.assertEqual(leftOf, -1)
600606

601607
# 5-+-4 0-+-9
602608
# | | | |
@@ -616,11 +622,12 @@ def testClosestVertex(self):
616622
self.assertEqual(afterVertex, 13)
617623
assert abs(dist - 0.02) < 0.00001, "Expected: %f; Got:%f" % (dist, 0.02)
618624

619-
(dist, minDistPoint, afterVertex) = polygon.closestSegmentWithContext(QgsPointXY(4.1, 1.1))
625+
(dist, minDistPoint, afterVertex, leftOf) = polygon.closestSegmentWithContext(QgsPointXY(4.1, 1.1))
620626
self.assertEqual(afterVertex, 12)
621627
self.assertEqual(minDistPoint, QgsPointXY(4, 1))
622628
exp = 0.02
623629
assert abs(dist - exp) < 0.00001, "Expected: %f; Got:%f" % (exp, dist)
630+
self.assertEqual(leftOf, -1)
624631

625632
def testAdjacentVertex(self):
626633
# 2-+-+-+-+-3

0 commit comments

Comments
 (0)
Please sign in to comment.