Skip to content

Commit 193e9e7

Browse files
committedAug 29, 2016
[FEATURE] Expose GEOS linear referencing function to QgsGeometry
Adds a new QgsGeometry::lineLocatePoint() function for retrieving the distance along a linestring to the nearest position on the linestring to a given point. (cherry-picked from 409dfdf)
1 parent d3882d5 commit 193e9e7

File tree

6 files changed

+106
-0
lines changed

6 files changed

+106
-0
lines changed
 

‎python/core/geometry/qgsgeometry.sip

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,18 @@ class QgsGeometry
476476
*/
477477
QgsGeometry* interpolate( double distance ) /Factory/;
478478

479+
/** Returns a distance representing the location along this linestring of the closest point
480+
* on this linestring geometry to the specified point. Ie, the returned value indicates
481+
* how far along this linestring you need to traverse to get to the closest location
482+
* where this linestring comes to the specified point.
483+
* @param point point to seek proximity to
484+
* @return distance along line, or -1 on error
485+
* @note only valid for linestring geometries
486+
* @see interpolate()
487+
* @note added in QGIS 3.0
488+
*/
489+
double lineLocatePoint( const QgsGeometry& point ) const;
490+
479491
/** Returns a geometry representing the points shared by this geometry and other. */
480492
QgsGeometry* intersection( const QgsGeometry* geometry ) const /Factory/;
481493

‎src/core/geometry/qgsgeometry.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,24 @@ QgsGeometry* QgsGeometry::interpolate( double distance ) const
13911391
return new QgsGeometry( result );
13921392
}
13931393

1394+
double QgsGeometry::lineLocatePoint( const QgsGeometry& point ) const
1395+
{
1396+
if ( type() != QGis::Line )
1397+
return -1;
1398+
1399+
if ( QgsWKBTypes::flatType( point.d->geometry->wkbType() ) != QgsWKBTypes::Point )
1400+
return -1;
1401+
1402+
QgsGeometry segmentized = *this;
1403+
if ( QgsWKBTypes::isCurvedType( d->geometry->wkbType() ) )
1404+
{
1405+
segmentized = QgsGeometry( static_cast< QgsCurveV2* >( d->geometry )->segmentize() );
1406+
}
1407+
1408+
QgsGeos geos( d->geometry );
1409+
return geos.lineLocatePoint( *( static_cast< QgsPointV2* >( point.d->geometry ) ) );
1410+
}
1411+
13941412
QgsGeometry* QgsGeometry::intersection( const QgsGeometry* geometry ) const
13951413
{
13961414
if ( !d->geometry || !geometry->d->geometry )

‎src/core/geometry/qgsgeometry.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,9 +515,22 @@ class CORE_EXPORT QgsGeometry
515515
/**
516516
* Return interpolated point on line at distance
517517
* @note added in 1.9
518+
* @see lineLocatePoint()
518519
*/
519520
QgsGeometry* interpolate( double distance ) const;
520521

522+
/** Returns a distance representing the location along this linestring of the closest point
523+
* on this linestring geometry to the specified point. Ie, the returned value indicates
524+
* how far along this linestring you need to traverse to get to the closest location
525+
* where this linestring comes to the specified point.
526+
* @param point point to seek proximity to
527+
* @return distance along line, or -1 on error
528+
* @note only valid for linestring geometries
529+
* @see interpolate()
530+
* @note added in QGIS 3.0
531+
*/
532+
double lineLocatePoint( const QgsGeometry& point ) const;
533+
521534
/** Returns a geometry representing the points shared by this geometry and other. */
522535
QgsGeometry* intersection( const QgsGeometry* geometry ) const;
523536

‎src/core/geometry/qgsgeos.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1891,6 +1891,36 @@ QgsGeometry QgsGeos::shortestLine( const QgsGeometry& other, QString* errorMsg )
18911891
return QgsGeometry( line );
18921892
}
18931893

1894+
double QgsGeos::lineLocatePoint( const QgsPointV2& point, QString* errorMsg ) const
1895+
{
1896+
if ( !mGeos )
1897+
{
1898+
return -1;
1899+
}
1900+
1901+
GEOSGeomScopedPtr otherGeom( asGeos( &point, mPrecision ) );
1902+
if ( !otherGeom )
1903+
{
1904+
return -1;
1905+
}
1906+
1907+
double distance = -1;
1908+
try
1909+
{
1910+
distance = GEOSProject_r( geosinit.ctxt, mGeos, otherGeom.get() );
1911+
}
1912+
catch ( GEOSException &e )
1913+
{
1914+
if ( errorMsg )
1915+
{
1916+
*errorMsg = e.what();
1917+
}
1918+
return -1;
1919+
}
1920+
1921+
return distance;
1922+
}
1923+
18941924

18951925
/** Extract coordinates of linestring's endpoints. Returns false on error. */
18961926
static bool _linestringEndpoints( const GEOSGeometry* linestring, double& x1, double& y1, double& x2, double& y2 )

‎src/core/geometry/qgsgeos.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
110110
*/
111111
QgsGeometry shortestLine( const QgsGeometry& other, QString* errorMsg = nullptr ) const;
112112

113+
/** Returns a distance representing the location along this linestring of the closest point
114+
* on this linestring geometry to the specified point. Ie, the returned value indicates
115+
* how far along this linestring you need to traverse to get to the closest location
116+
* where this linestring comes to the specified point.
117+
* @param point point to seek proximity to
118+
* @param errorMsg error messages emitted, if any
119+
* @note only valid for linestring geometries
120+
* @return distance along line, or -1 on error
121+
*/
122+
double lineLocatePoint( const QgsPointV2& point, QString* errorMsg = nullptr ) const;
123+
113124
/** Create a geometry from a GEOSGeometry
114125
* @param geos GEOSGeometry. Ownership is NOT transferred.
115126
*/

‎tests/src/python/test_qgsgeometry.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3390,5 +3390,27 @@ def testMergeLines(self):
33903390
exp = 'MultiLineString((0 0, 10 10),(12 2, 14 4))'
33913391
self.assertTrue(compareWkt(result, exp, 0.00001), "Merge lines: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
33923392

3393+
def testLineLocatePoint(self):
3394+
""" test QgsGeometry.lineLocatePoint() """
3395+
3396+
# not a linestring
3397+
point = QgsGeometry.fromWkt('Point(1 2)')
3398+
self.assertEqual(point.lineLocatePoint(point), -1)
3399+
3400+
# not a point
3401+
linestring = QgsGeometry.fromWkt('LineString(0 0, 10 0)')
3402+
self.assertEqual(linestring.lineLocatePoint(linestring), -1)
3403+
3404+
# valid
3405+
self.assertEqual(linestring.lineLocatePoint(point), 1)
3406+
point = QgsGeometry.fromWkt('Point(9 -2)')
3407+
self.assertEqual(linestring.lineLocatePoint(point), 9)
3408+
3409+
# circular string
3410+
geom = QgsGeometry.fromWkt('CircularString (1 5, 6 2, 7 3)')
3411+
point = QgsGeometry.fromWkt('Point(9 -2)')
3412+
self.assertAlmostEqual(geom.lineLocatePoint(point), 7.372, places=3)
3413+
3414+
33933415
if __name__ == '__main__':
33943416
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.