Skip to content
Permalink
Browse files

Unify behavior of various geometry API leftOf tests

Now instead of mixing bools/numeric returns, we always use
ints, where:
-1 = left
0 = test failed, e.g. point on line
1 = right

Also fix a bunch of extra issues identified with left of tests
as a result of these changes
  • Loading branch information
nyalldawson committed Nov 29, 2017
1 parent 2f43deb commit 3341a3e91e8d78e486d36193bcf97d39b4bfdcd9
Showing with 155 additions and 136 deletions.
  1. +4 −2 python/core/geometry/qgsabstractgeometry.sip
  2. +1 −1 python/core/geometry/qgscircularstring.sip
  3. +1 −1 python/core/geometry/qgscompoundcurve.sip
  4. +1 −1 python/core/geometry/qgscurvepolygon.sip
  5. +3 −2 python/core/geometry/qgsgeometry.sip
  6. +1 −1 python/core/geometry/qgsgeometrycollection.sip
  7. +2 −2 python/core/geometry/qgsgeometryutils.sip
  8. +1 −1 python/core/geometry/qgslinestring.sip
  9. +1 −1 python/core/geometry/qgspoint.sip
  10. +3 −3 src/app/qgsmaptooloffsetcurve.cpp
  11. +3 −3 src/app/qgsmaptoolrectangle3points.cpp
  12. +4 −2 src/core/geometry/qgsabstractgeometry.h
  13. +6 −5 src/core/geometry/qgscircularstring.cpp
  14. +2 −2 src/core/geometry/qgscircularstring.h
  15. +1 −1 src/core/geometry/qgscompoundcurve.cpp
  16. +1 −1 src/core/geometry/qgscompoundcurve.h
  17. +1 −1 src/core/geometry/qgscurvepolygon.cpp
  18. +1 −1 src/core/geometry/qgscurvepolygon.h
  19. +6 −12 src/core/geometry/qgsgeometry.cpp
  20. +3 −2 src/core/geometry/qgsgeometry.h
  21. +1 −1 src/core/geometry/qgsgeometrycollection.cpp
  22. +1 −1 src/core/geometry/qgsgeometrycollection.h
  23. +5 −4 src/core/geometry/qgsgeometryutils.cpp
  24. +3 −3 src/core/geometry/qgsgeometryutils.h
  25. +12 −7 src/core/geometry/qgslinestring.cpp
  26. +1 −1 src/core/geometry/qgslinestring.h
  27. +3 −2 src/core/geometry/qgspoint.cpp
  28. +1 −1 src/core/geometry/qgspoint.h
  29. +1 −1 src/core/geometry/qgstriangle.cpp
  30. +74 −66 tests/src/core/testqgsgeometry.cpp
  31. +7 −4 tests/src/python/test_qgsgeometry.py
@@ -299,13 +299,15 @@ class QgsAbstractGeometry

virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt /Out/,
QgsVertexId &vertexAfter /Out/,
bool *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const = 0;
int *leftOf /Out/ = 0, double epsilon = 4 * DBL_EPSILON ) const = 0;
%Docstring
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,
\param leftOf indicates whether the point lies on the left side of the geometry (-1 if point is to the left
of the geometry, +1 if the point is to the right of the geometry, or 0 for cases where left/right could not
be determined, e.g. point exactly on a line)
false if point is to right of segment)
\param epsilon epsilon for segment snapping
:return: squared distance to closest segment or negative value on error
@@ -99,7 +99,7 @@ class QgsCircularString: QgsCurve

virtual bool deleteVertex( QgsVertexId position );

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

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

@@ -124,7 +124,7 @@ class QgsCompoundCurve: QgsCurve

virtual bool deleteVertex( QgsVertexId position );

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

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

@@ -149,7 +149,7 @@ Adds an interior ring to the geometry (takes ownership)

virtual bool isEmpty() const;

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


virtual bool nextVertex( QgsVertexId &id, QgsPoint &vertex /Out/ ) const;
@@ -493,14 +493,15 @@ Returns true if WKB of the geometry is of WKBMulti* type
:rtype: float
%End

double closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint /Out/, int &afterVertex /Out/, double *leftOf /Out/ = 0, double epsilon = DEFAULT_SEGMENT_EPSILON ) const;
double closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint /Out/, int &afterVertex /Out/, int *leftOf /Out/ = 0, double epsilon = DEFAULT_SEGMENT_EPSILON ) const;
%Docstring
Searches for the closest segment of geometry to the given point
\param point Specifies the point for search
\param minDistPoint Receives the nearest point on the segment
\param afterVertex Receives index of the vertex after the closest segment. The vertex
before the closest segment is always afterVertex - 1
\param leftOf Out: Returns if the point lies on the left of right side of the segment ( > 0 means left, < 0 means right )
\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
that the test was unsuccesful, e.g. for a point exactly on the line)
\param epsilon epsilon for segment snapping
:return: The squared Cartesian distance is also returned in sqrDist, negative number on error
:rtype: float
@@ -112,7 +112,7 @@ Adds a geometry and takes ownership. Returns true in case of success.
virtual int nCoordinates() const;


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

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

@@ -123,14 +123,14 @@ class QgsGeometryUtils



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

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

static QgsPoint pointOnLineWithDistance( const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance );
@@ -243,7 +243,7 @@ Closes the line string by appending the first point to the end of the line, if i
virtual QgsLineString *reversed() const /Factory/;


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

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

@@ -377,7 +377,7 @@ class QgsPoint: QgsAbstractGeometry
virtual bool deleteVertex( QgsVertexId position );


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

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

@@ -242,7 +242,7 @@ void QgsMapToolOffsetCurve::canvasMoveEvent( QgsMapMouseEvent *e )

QgsPointXY minDistPoint;
int beforeVertex;
double leftOf;
int leftOf = 0;
double offset = std::sqrt( mOriginalGeometry.closestSegmentWithContext( layerCoords, minDistPoint, beforeVertex, &leftOf ) );
if ( offset == 0.0 )
{
@@ -254,13 +254,13 @@ void QgsMapToolOffsetCurve::canvasMoveEvent( QgsMapMouseEvent *e )
if ( mDistanceWidget )
{
// this will also set the rubber band
mDistanceWidget->setValue( leftOf < 0 ? -offset : offset );
mDistanceWidget->setValue( leftOf < 0 ? offset : -offset );
mDistanceWidget->setFocus( Qt::TabFocusReason );
}
else
{
//create offset geometry using geos
setOffsetForRubberBand( leftOf < 0 ? -offset : offset );
setOffsetForRubberBand( leftOf < 0 ? offset : -offset );
}
}

@@ -76,9 +76,9 @@ void QgsMapToolRectangle3Points::cadCanvasMoveEvent( QgsMapMouseEvent *e )
{

setDistance2( mPoints.at( 1 ).distance( mapPoint ) );
double side = QgsGeometryUtils::leftOfLine( mapPoint.x(), mapPoint.y(),
mPoints.at( 0 ).x(), mPoints.at( 0 ).y(),
mPoints.at( 1 ).x(), mPoints.at( 1 ).y() );
int side = QgsGeometryUtils::leftOfLine( mapPoint.x(), mapPoint.y(),
mPoints.at( 0 ).x(), mPoints.at( 0 ).y(),
mPoints.at( 1 ).x(), mPoints.at( 1 ).y() );

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

@@ -324,14 +324,16 @@ class CORE_EXPORT QgsAbstractGeometry
* \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,
* \param leftOf indicates whether the point lies on the left side of the geometry (-1 if point is to the left
* of the geometry, +1 if the point is to the right of the geometry, or 0 for cases where left/right could not
* be determined, e.g. point exactly on a line)
* false if point is to right of segment)
* \param epsilon epsilon for segment snapping
* \returns squared distance to closest segment or negative value on error
*/
virtual double closestSegment( const QgsPoint &pt, QgsPoint &segmentPt SIP_OUT,
QgsVertexId &vertexAfter SIP_OUT,
bool *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const = 0;
int *leftOf SIP_OUT = nullptr, double epsilon = 4 * DBL_EPSILON ) const = 0;

//low-level editing

@@ -732,12 +732,12 @@ void QgsCircularString::deleteVertex( int i )
clearCache();
}

double QgsCircularString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, bool *leftOf, double epsilon ) const
double QgsCircularString::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
{
double minDist = std::numeric_limits<double>::max();
QgsPoint minDistSegmentPoint;
QgsVertexId minDistVertexAfter;
bool minDistLeftOf = false;
int minDistLeftOf = 0;

double currentDist = 0.0;

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

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

return sqrDistance;
@@ -82,7 +82,7 @@ class CORE_EXPORT QgsCircularString: public QgsCurve
bool insertVertex( QgsVertexId position, const QgsPoint &vertex ) override;
bool moveVertex( QgsVertexId position, const QgsPoint &newPos ) override;
bool deleteVertex( QgsVertexId position ) override;
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;
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;
bool pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const override;
void sumUpArea( double &sum SIP_OUT ) const override;
bool hasCurvedSegments() const override;
@@ -129,7 +129,7 @@ class CORE_EXPORT QgsCircularString: public QgsCurve
static QgsRectangle segmentBoundingBox( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3 );
static QgsPointSequence compassPointsOnSegment( double p1Angle, double p2Angle, double p3Angle, double centerX, double centerY, double radius );
static double closestPointOnArc( double x1, double y1, double x2, double y2, double x3, double y3,
const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, bool *leftOf, double epsilon );
const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon );
void insertVertexBetween( int after, int before, int pointOnCircle );
void deleteVertex( int i );

@@ -699,7 +699,7 @@ QVector< QPair<int, QgsVertexId> > QgsCompoundCurve::curveVertexId( QgsVertexId
return curveIds;
}

double QgsCompoundCurve::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, bool *leftOf, double epsilon ) const
double QgsCompoundCurve::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
{
return QgsGeometryUtils::closestSegmentFromComponents( mCurves, QgsGeometryUtils::Vertex, pt, segmentPt, vertexAfter, leftOf, epsilon );
}
@@ -105,7 +105,7 @@ class CORE_EXPORT QgsCompoundCurve: public QgsCurve
bool insertVertex( QgsVertexId position, const QgsPoint &vertex ) override;
bool moveVertex( QgsVertexId position, const QgsPoint &newPos ) override;
bool deleteVertex( QgsVertexId position ) override;
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;
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;
bool pointAt( int node, QgsPoint &point, QgsVertexId::VertexType &type ) const override;
void sumUpArea( double &sum SIP_OUT ) const override;

@@ -798,7 +798,7 @@ bool QgsCurvePolygon::isEmpty() const
return mExteriorRing->isEmpty();
}

double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, bool *leftOf, double epsilon ) const
double QgsCurvePolygon::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
{
if ( !mExteriorRing )
{
@@ -121,7 +121,7 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
int nCoordinates() const override;
int vertexNumberFromVertexId( QgsVertexId id ) const override;
bool isEmpty() const override;
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;
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;

bool nextVertex( QgsVertexId &id, QgsPoint &vertex SIP_OUT ) const override;
void adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex SIP_OUT, QgsVertexId &nextVertex SIP_OUT ) const override;
@@ -584,12 +584,11 @@ double QgsGeometry::closestVertexWithContext( const QgsPointXY &point, int &atVe
return QgsGeometryUtils::sqrDistance2D( closestPoint, pt );
}

double QgsGeometry::closestSegmentWithContext(
const QgsPointXY &point,
QgsPointXY &minDistPoint,
int &afterVertex,
double *leftOf,
double epsilon ) const
double QgsGeometry::closestSegmentWithContext( const QgsPointXY &point,
QgsPointXY &minDistPoint,
int &afterVertex,
int *leftOf,
double epsilon ) const
{
if ( !d->geometry )
{
@@ -598,19 +597,14 @@ double QgsGeometry::closestSegmentWithContext(

QgsPoint segmentPt;
QgsVertexId vertexAfter;
bool leftOfBool;

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

minDistPoint.setX( segmentPt.x() );
minDistPoint.setY( segmentPt.y() );
afterVertex = vertexNrFromVertexId( vertexAfter );
if ( leftOf )
{
*leftOf = leftOfBool ? 1.0 : -1.0;
}
return sqrDist;
}

@@ -555,11 +555,12 @@ class CORE_EXPORT QgsGeometry
* \param minDistPoint Receives the nearest point on the segment
* \param afterVertex Receives index of the vertex after the closest segment. The vertex
* before the closest segment is always afterVertex - 1
* \param leftOf Out: Returns if the point lies on the left of right side of the segment ( > 0 means left, < 0 means right )
* \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
* that the test was unsuccesful, e.g. for a point exactly on the line)
* \param epsilon epsilon for segment snapping
* \returns The squared Cartesian distance is also returned in sqrDist, negative number on error
*/
double closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint SIP_OUT, int &afterVertex SIP_OUT, double *leftOf SIP_OUT = nullptr, double epsilon = DEFAULT_SEGMENT_EPSILON ) const;
double closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint SIP_OUT, int &afterVertex SIP_OUT, int *leftOf SIP_OUT = nullptr, double epsilon = DEFAULT_SEGMENT_EPSILON ) const;

/**
* Adds a new ring to this geometry. This makes only sense for polygon and multipolygons.
@@ -455,7 +455,7 @@ int QgsGeometryCollection::nCoordinates() const
return count;
}

double QgsGeometryCollection::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, bool *leftOf, double epsilon ) const
double QgsGeometryCollection::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
{
return QgsGeometryUtils::closestSegmentFromComponents( mGeometries, QgsGeometryUtils::Part, pt, segmentPt, vertexAfter, leftOf, epsilon );
}
@@ -105,7 +105,7 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
QgsCoordinateSequence coordinateSequence() const override;
int nCoordinates() const override;

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;
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;
bool nextVertex( QgsVertexId &id, QgsPoint &vertex SIP_OUT ) const override;

//low-level editing

0 comments on commit 3341a3e

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