Skip to content

Commit fdea61a

Browse files
committedJan 8, 2019
Add method to calculate the geodesic line joining two points to QgsDistanceArea
Using geographiclib to calculate the line
1 parent 148505d commit fdea61a

File tree

3 files changed

+105
-0
lines changed

3 files changed

+105
-0
lines changed
 

‎python/core/auto_generated/qgsdistancearea.sip.in

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,27 @@ Datum of Australia Technical Manual", Chapter 4.
353353
--> latitudes outside these bounds cause the calculations to become unstable and can return invalid results
354354

355355
.. versionadded:: 3.0
356+
%End
357+
358+
QList< QList< QgsPointXY > > geodesicLine( const QgsPointXY &p1, const QgsPointXY &p2, double interval, bool breakLine = false ) const;
359+
%Docstring
360+
Calculates the geodesic line between ``p1`` and ``p2``, which represents the shortest path on the
361+
ellipsoid between these two points.
362+
363+
The ellipsoid settings defined on this QgsDistanceArea object will be used during the calculations.
364+
365+
``p1`` and ``p2`` must be in the sourceCrs() of this QgsDistanceArea object. The returned line
366+
will also be in this same CRS.
367+
368+
The ``interval`` parameter gives the maximum distance between points on the computed line.
369+
This argument is always specified in meters. A shorter distance results in a denser line,
370+
at the cost of extra computing time.
371+
372+
If the geodesic line crosses the international date line and ``breakLine`` is true, then
373+
the line will be split into two parts, broken at the date line. In this case the function
374+
will return two lines, corresponding to the portions at either side of the date line.
375+
376+
.. versionadded:: 3.6
356377
%End
357378

358379
};

‎src/core/qgsdistancearea.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
#include "qgsunittypes.h"
3535
#include "qgsexception.h"
3636

37+
#include <GeographicLib/Geodesic.hpp>
38+
#include <GeographicLib/GeodesicLine.hpp>
39+
3740
#define DEG2RAD(x) ((x)*M_PI/180)
3841
#define RAD2DEG(r) (180.0 * (r) / M_PI)
3942
#define POW2(x) ((x)*(x))
@@ -454,6 +457,66 @@ QgsPointXY QgsDistanceArea::computeSpheroidProject(
454457
return QgsPointXY( RAD2DEG( lambda2 ), RAD2DEG( lat2 ) );
455458
}
456459

460+
QList< QList<QgsPointXY> > QgsDistanceArea::geodesicLine( const QgsPointXY &p1, const QgsPointXY &p2, const double interval, const bool breakLine ) const
461+
{
462+
if ( !willUseEllipsoid() )
463+
{
464+
return QList< QList< QgsPointXY > >() << ( QList< QgsPointXY >() << p1 << p2 );
465+
}
466+
467+
GeographicLib::Geodesic geod( mSemiMajor, 1 / mInvFlattening );
468+
469+
QgsPointXY pp1, pp2;
470+
try
471+
{
472+
pp1 = mCoordTransform.transform( p1 );
473+
pp2 = mCoordTransform.transform( p2 );
474+
}
475+
catch ( QgsCsException & )
476+
{
477+
QgsMessageLog::logMessage( QObject::tr( "Caught a coordinate system exception while trying to transform a point. Unable to calculate geodesic line." ) );
478+
return QList< QList< QgsPointXY > >();
479+
}
480+
481+
GeographicLib::GeodesicLine line = geod.InverseLine( pp1.y(), pp1.x(), pp2.y(), pp2.x() );
482+
const double totalDist = line.Distance();
483+
484+
QList< QList< QgsPointXY > > res;
485+
QList< QgsPointXY > currentPart;
486+
currentPart << p1;
487+
double d = interval;
488+
double prevLon = p1.x();
489+
while ( d < totalDist )
490+
{
491+
double lat, lon;
492+
( void )line.Position( d, lat, lon );
493+
494+
if ( breakLine && ( ( prevLon < -120 && lon > 120 ) || ( prevLon > 120 && lon < -120 ) ) )
495+
{
496+
// TODO -- when breaking the geodesic at the date line, we need to calculate the latitude
497+
// at which the geodesic intersects the date line, and add points to both line segments at this latitude
498+
// on the date line. See Baselga & Martinez-Llario 2017 for an algorithm to calculate geodesic-geodesic intersections on ellipsoids.
499+
500+
res << currentPart;
501+
currentPart.clear();
502+
}
503+
504+
prevLon = lon;
505+
506+
try
507+
{
508+
currentPart << mCoordTransform.transform( QgsPointXY( lon, lat ), QgsCoordinateTransform::ReverseTransform );
509+
}
510+
catch ( QgsCsException & )
511+
{
512+
QgsMessageLog::logMessage( QObject::tr( "Caught a coordinate system exception while trying to transform a point." ) );
513+
}
514+
d += interval;
515+
}
516+
res << currentPart;
517+
return res;
518+
}
519+
457520
QgsUnitTypes::DistanceUnit QgsDistanceArea::lengthUnits() const
458521
{
459522
return willUseEllipsoid() ? QgsUnitTypes::DistanceMeters : mCoordTransform.sourceCrs().mapUnits();

‎src/core/qgsdistancearea.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,27 @@ class CORE_EXPORT QgsDistanceArea
288288
*/
289289
QgsPointXY computeSpheroidProject( const QgsPointXY &p1, double distance = 1, double azimuth = M_PI_2 ) const;
290290

291+
/**
292+
* Calculates the geodesic line between \a p1 and \a p2, which represents the shortest path on the
293+
* ellipsoid between these two points.
294+
*
295+
* The ellipsoid settings defined on this QgsDistanceArea object will be used during the calculations.
296+
*
297+
* \a p1 and \a p2 must be in the sourceCrs() of this QgsDistanceArea object. The returned line
298+
* will also be in this same CRS.
299+
*
300+
* The \a interval parameter gives the maximum distance between points on the computed line.
301+
* This argument is always specified in meters. A shorter distance results in a denser line,
302+
* at the cost of extra computing time.
303+
*
304+
* If the geodesic line crosses the international date line and \a breakLine is true, then
305+
* the line will be split into two parts, broken at the date line. In this case the function
306+
* will return two lines, corresponding to the portions at either side of the date line.
307+
*
308+
* \since QGIS 3.6
309+
*/
310+
QList< QList< QgsPointXY > > geodesicLine( const QgsPointXY &p1, const QgsPointXY &p2, double interval, bool breakLine = false ) const;
311+
291312
private:
292313

293314
/**

0 commit comments

Comments
 (0)
Please sign in to comment.