Skip to content
Permalink
Browse files

Merge pull request #5758 from nyalldawson/leftof

Geometry "leftOf" improvements
  • Loading branch information
nyalldawson committed Nov 29, 2017
2 parents 62dbf4c + b8a62f4 commit e4ce62342c164f76a72b1ee392ff8d880cc3c73f
Showing with 168 additions and 144 deletions.
  1. +4 −0 doc/api_break.dox
  2. +4 −2 python/core/geometry/qgsabstractgeometry.sip
  3. +1 −1 python/core/geometry/qgscircularstring.sip
  4. +1 −1 python/core/geometry/qgscompoundcurve.sip
  5. +1 −1 python/core/geometry/qgscurvepolygon.sip
  6. +3 −2 python/core/geometry/qgsgeometry.sip
  7. +1 −1 python/core/geometry/qgsgeometrycollection.sip
  8. +2 −2 python/core/geometry/qgsgeometryutils.sip
  9. +1 −1 python/core/geometry/qgslinestring.sip
  10. +1 −1 python/core/geometry/qgspoint.sip
  11. +3 −3 src/app/qgsmaptooloffsetcurve.cpp
  12. +3 −3 src/app/qgsmaptoolrectangle3points.cpp
  13. +4 −2 src/core/geometry/qgsabstractgeometry.h
  14. +6 −5 src/core/geometry/qgscircularstring.cpp
  15. +2 −2 src/core/geometry/qgscircularstring.h
  16. +1 −1 src/core/geometry/qgscompoundcurve.cpp
  17. +1 −1 src/core/geometry/qgscompoundcurve.h
  18. +1 −1 src/core/geometry/qgscurvepolygon.cpp
  19. +1 −1 src/core/geometry/qgscurvepolygon.h
  20. +6 −12 src/core/geometry/qgsgeometry.cpp
  21. +3 −6 src/core/geometry/qgsgeometry.h
  22. +1 −1 src/core/geometry/qgsgeometrycollection.cpp
  23. +1 −1 src/core/geometry/qgsgeometrycollection.h
  24. +5 −4 src/core/geometry/qgsgeometryutils.cpp
  25. +3 −3 src/core/geometry/qgsgeometryutils.h
  26. +14 −8 src/core/geometry/qgslinestring.cpp
  27. +1 −1 src/core/geometry/qgslinestring.h
  28. +3 −2 src/core/geometry/qgspoint.cpp
  29. +1 −1 src/core/geometry/qgspoint.h
  30. +1 −1 src/core/geometry/qgstriangle.cpp
  31. +74 −66 tests/src/core/testqgsgeometry.cpp
  32. +14 −7 tests/src/python/test_qgsgeometry.py
@@ -456,6 +456,9 @@ QgsAbstractGeometry {#qgis_api_break_3_0_QgsAbstractGeometry}
- asGML2() was renamed to asGml2()
- asGML3() was renamed to asGml3()
- asJSON() was renamed to asJson()
- closestSegment() now returns an integer value for the leftOf test (-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)


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


QgsGeometryAnalyzer {#qgis_api_break_3_0_QgsGeometryAnalyzer}
@@ -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/ ) 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,15 +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
*/
#ifndef SIP_RUN
double closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint, int &afterVertex, double *leftOf = nullptr, double epsilon = DEFAULT_SEGMENT_EPSILON ) const;
#else
double closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint SIP_OUT, int &afterVertex SIP_OUT ) const;
#endif
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 );
}

0 comments on commit e4ce623

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