Skip to content

Commit

Permalink
Add method to find distance from a point to a poylgon's boundary
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Nov 14, 2016
1 parent 1d3f1f0 commit d5c307e
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 0 deletions.
8 changes: 8 additions & 0 deletions python/core/geometry/qgspolygon.sip
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,12 @@ class QgsPolygonV2: public QgsCurvePolygon
virtual void setExteriorRing( QgsCurve* ring /Transfer/ );

virtual QgsAbstractGeometry* boundary() const /Factory/;

/**
* Returns the distance from a point to the boundary of the polygon (either the
* exterior ring or any closer interior rings). The returned distance will be
* negative if the point lies outside the polygon.
* @note added in QGIS 3.0
*/
double pointDistanceToBoundary( double x, double y ) const;
};
34 changes: 34 additions & 0 deletions src/core/geometry/qgspolygon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,40 @@ QgsAbstractGeometry* QgsPolygonV2::boundary() const
}
}

double QgsPolygonV2::pointDistanceToBoundary( double x, double y ) const
{
if ( !mExteriorRing )
return std::numeric_limits< double >::quiet_NaN();

bool inside = false;
double minimumDistance = DBL_MAX;
double minDistX = 0.0;
double minDistY = 0.0;

int numRings = mInteriorRings.size() + 1;
for ( int ringIndex = 0; ringIndex < numRings; ++ringIndex )
{
const QgsLineString* ring = static_cast< const QgsLineString* >( ringIndex == 0 ? mExteriorRing : mInteriorRings.at( ringIndex - 1 ) );

int len = ring->numPoints() - 1; //assume closed
for ( int i = 0, j = len - 1; i < len; j = i++ )
{
double aX = ring->xAt( i );
double aY = ring->yAt( i );
double bX = ring->xAt( j );
double bY = ring->yAt( j );

if ((( aY > y ) != ( bY > y ) ) &&
( x < ( bX - aX ) * ( y - aY ) / ( bY - aY ) + aX ) )
inside = !inside;

minimumDistance = qMin( minimumDistance, QgsGeometryUtils::sqrDistToLine( x, y, aX, aY, bX, bY, minDistX, minDistY, 4 * DBL_EPSILON ) );
}
}

return ( inside ? 1 : -1 ) * sqrt( minimumDistance );
}

QgsPolygonV2* QgsPolygonV2::surfaceToPolygon() const
{
return clone();
Expand Down
8 changes: 8 additions & 0 deletions src/core/geometry/qgspolygon.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,13 @@ class CORE_EXPORT QgsPolygonV2: public QgsCurvePolygon

virtual QgsAbstractGeometry* boundary() const override;

/**
* Returns the distance from a point to the boundary of the polygon (either the
* exterior ring or any closer interior rings). The returned distance will be
* negative if the point lies outside the polygon.
* @note added in QGIS 3.0
*/
double pointDistanceToBoundary( double x, double y ) const;

};
#endif // QGSPOLYGONV2_H
23 changes: 23 additions & 0 deletions tests/src/core/testqgsgeometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3026,6 +3026,29 @@ void TestQgsGeometry::polygon()
QCOMPARE( lineBoundary->zAt( 3 ), 10.0 );
delete boundary;

// point distance to boundary

QgsLineString pd1;
pd1.setPoints( QList<QgsPointV2>() << QgsPointV2( 0, 0 ) << QgsPointV2( 1, 0 ) << QgsPointV2( 1, 1 ) << QgsPointV2( 0, 1 ) << QgsPointV2( 0, 0 ) );
QgsPolygonV2 pd;
// no meaning, but let's not crash
( void )pd.pointDistanceToBoundary( 0, 0 );

pd.setExteriorRing( pd1.clone() );
QGSCOMPARENEAR( pd.pointDistanceToBoundary( 0, 0.5 ), 0.0, 0.0000000001 );
QGSCOMPARENEAR( pd.pointDistanceToBoundary( 0.1, 0.5 ), 0.1, 0.0000000001 );
QGSCOMPARENEAR( pd.pointDistanceToBoundary( -0.1, 0.5 ), -0.1, 0.0000000001 );
// with a ring
QgsLineString pdRing1;
pdRing1.setPoints( QList<QgsPointV2>() << QgsPointV2( 0.1, 0.1 ) << QgsPointV2( 0.2, 0.1 ) << QgsPointV2( 0.2, 0.6 ) << QgsPointV2( 0.1, 0.6 ) << QgsPointV2( 0.1, 0.1 ) );
pd.setInteriorRings( QList< QgsCurve* >() << pdRing1.clone() );
QGSCOMPARENEAR( pd.pointDistanceToBoundary( 0, 0.5 ), 0.0, 0.0000000001 );
QGSCOMPARENEAR( pd.pointDistanceToBoundary( 0.1, 0.5 ), 0.0, 0.0000000001 );
QGSCOMPARENEAR( pd.pointDistanceToBoundary( 0.01, 0.5 ), 0.01, 0.0000000001 );
QGSCOMPARENEAR( pd.pointDistanceToBoundary( 0.08, 0.5 ), 0.02, 0.0000000001 );
QGSCOMPARENEAR( pd.pointDistanceToBoundary( 0.12, 0.5 ), -0.02, 0.0000000001 );
QGSCOMPARENEAR( pd.pointDistanceToBoundary( -0.1, 0.5 ), -0.1, 0.0000000001 );

}

void TestQgsGeometry::multiPoint()
Expand Down

0 comments on commit d5c307e

Please sign in to comment.