mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-17 00:04:02 -04:00
Add method to calculate the geodesic line joining two points to QgsDistanceArea
Using geographiclib to calculate the line
This commit is contained in:
parent
148505d47f
commit
fdea61a97c
@ -353,6 +353,27 @@ Datum of Australia Technical Manual", Chapter 4.
|
|||||||
--> latitudes outside these bounds cause the calculations to become unstable and can return invalid results
|
--> latitudes outside these bounds cause the calculations to become unstable and can return invalid results
|
||||||
|
|
||||||
.. versionadded:: 3.0
|
.. versionadded:: 3.0
|
||||||
|
%End
|
||||||
|
|
||||||
|
QList< QList< QgsPointXY > > geodesicLine( const QgsPointXY &p1, const QgsPointXY &p2, double interval, bool breakLine = false ) const;
|
||||||
|
%Docstring
|
||||||
|
Calculates the geodesic line between ``p1`` and ``p2``, which represents the shortest path on the
|
||||||
|
ellipsoid between these two points.
|
||||||
|
|
||||||
|
The ellipsoid settings defined on this QgsDistanceArea object will be used during the calculations.
|
||||||
|
|
||||||
|
``p1`` and ``p2`` must be in the sourceCrs() of this QgsDistanceArea object. The returned line
|
||||||
|
will also be in this same CRS.
|
||||||
|
|
||||||
|
The ``interval`` parameter gives the maximum distance between points on the computed line.
|
||||||
|
This argument is always specified in meters. A shorter distance results in a denser line,
|
||||||
|
at the cost of extra computing time.
|
||||||
|
|
||||||
|
If the geodesic line crosses the international date line and ``breakLine`` is true, then
|
||||||
|
the line will be split into two parts, broken at the date line. In this case the function
|
||||||
|
will return two lines, corresponding to the portions at either side of the date line.
|
||||||
|
|
||||||
|
.. versionadded:: 3.6
|
||||||
%End
|
%End
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -34,6 +34,9 @@
|
|||||||
#include "qgsunittypes.h"
|
#include "qgsunittypes.h"
|
||||||
#include "qgsexception.h"
|
#include "qgsexception.h"
|
||||||
|
|
||||||
|
#include <GeographicLib/Geodesic.hpp>
|
||||||
|
#include <GeographicLib/GeodesicLine.hpp>
|
||||||
|
|
||||||
#define DEG2RAD(x) ((x)*M_PI/180)
|
#define DEG2RAD(x) ((x)*M_PI/180)
|
||||||
#define RAD2DEG(r) (180.0 * (r) / M_PI)
|
#define RAD2DEG(r) (180.0 * (r) / M_PI)
|
||||||
#define POW2(x) ((x)*(x))
|
#define POW2(x) ((x)*(x))
|
||||||
@ -454,6 +457,66 @@ QgsPointXY QgsDistanceArea::computeSpheroidProject(
|
|||||||
return QgsPointXY( RAD2DEG( lambda2 ), RAD2DEG( lat2 ) );
|
return QgsPointXY( RAD2DEG( lambda2 ), RAD2DEG( lat2 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList< QList<QgsPointXY> > QgsDistanceArea::geodesicLine( const QgsPointXY &p1, const QgsPointXY &p2, const double interval, const bool breakLine ) const
|
||||||
|
{
|
||||||
|
if ( !willUseEllipsoid() )
|
||||||
|
{
|
||||||
|
return QList< QList< QgsPointXY > >() << ( QList< QgsPointXY >() << p1 << p2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
GeographicLib::Geodesic geod( mSemiMajor, 1 / mInvFlattening );
|
||||||
|
|
||||||
|
QgsPointXY pp1, pp2;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pp1 = mCoordTransform.transform( p1 );
|
||||||
|
pp2 = mCoordTransform.transform( p2 );
|
||||||
|
}
|
||||||
|
catch ( QgsCsException & )
|
||||||
|
{
|
||||||
|
QgsMessageLog::logMessage( QObject::tr( "Caught a coordinate system exception while trying to transform a point. Unable to calculate geodesic line." ) );
|
||||||
|
return QList< QList< QgsPointXY > >();
|
||||||
|
}
|
||||||
|
|
||||||
|
GeographicLib::GeodesicLine line = geod.InverseLine( pp1.y(), pp1.x(), pp2.y(), pp2.x() );
|
||||||
|
const double totalDist = line.Distance();
|
||||||
|
|
||||||
|
QList< QList< QgsPointXY > > res;
|
||||||
|
QList< QgsPointXY > currentPart;
|
||||||
|
currentPart << p1;
|
||||||
|
double d = interval;
|
||||||
|
double prevLon = p1.x();
|
||||||
|
while ( d < totalDist )
|
||||||
|
{
|
||||||
|
double lat, lon;
|
||||||
|
( void )line.Position( d, lat, lon );
|
||||||
|
|
||||||
|
if ( breakLine && ( ( prevLon < -120 && lon > 120 ) || ( prevLon > 120 && lon < -120 ) ) )
|
||||||
|
{
|
||||||
|
// TODO -- when breaking the geodesic at the date line, we need to calculate the latitude
|
||||||
|
// at which the geodesic intersects the date line, and add points to both line segments at this latitude
|
||||||
|
// on the date line. See Baselga & Martinez-Llario 2017 for an algorithm to calculate geodesic-geodesic intersections on ellipsoids.
|
||||||
|
|
||||||
|
res << currentPart;
|
||||||
|
currentPart.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
prevLon = lon;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
currentPart << mCoordTransform.transform( QgsPointXY( lon, lat ), QgsCoordinateTransform::ReverseTransform );
|
||||||
|
}
|
||||||
|
catch ( QgsCsException & )
|
||||||
|
{
|
||||||
|
QgsMessageLog::logMessage( QObject::tr( "Caught a coordinate system exception while trying to transform a point." ) );
|
||||||
|
}
|
||||||
|
d += interval;
|
||||||
|
}
|
||||||
|
res << currentPart;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
QgsUnitTypes::DistanceUnit QgsDistanceArea::lengthUnits() const
|
QgsUnitTypes::DistanceUnit QgsDistanceArea::lengthUnits() const
|
||||||
{
|
{
|
||||||
return willUseEllipsoid() ? QgsUnitTypes::DistanceMeters : mCoordTransform.sourceCrs().mapUnits();
|
return willUseEllipsoid() ? QgsUnitTypes::DistanceMeters : mCoordTransform.sourceCrs().mapUnits();
|
||||||
|
@ -288,6 +288,27 @@ class CORE_EXPORT QgsDistanceArea
|
|||||||
*/
|
*/
|
||||||
QgsPointXY computeSpheroidProject( const QgsPointXY &p1, double distance = 1, double azimuth = M_PI_2 ) const;
|
QgsPointXY computeSpheroidProject( const QgsPointXY &p1, double distance = 1, double azimuth = M_PI_2 ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the geodesic line between \a p1 and \a p2, which represents the shortest path on the
|
||||||
|
* ellipsoid between these two points.
|
||||||
|
*
|
||||||
|
* The ellipsoid settings defined on this QgsDistanceArea object will be used during the calculations.
|
||||||
|
*
|
||||||
|
* \a p1 and \a p2 must be in the sourceCrs() of this QgsDistanceArea object. The returned line
|
||||||
|
* will also be in this same CRS.
|
||||||
|
*
|
||||||
|
* The \a interval parameter gives the maximum distance between points on the computed line.
|
||||||
|
* This argument is always specified in meters. A shorter distance results in a denser line,
|
||||||
|
* at the cost of extra computing time.
|
||||||
|
*
|
||||||
|
* If the geodesic line crosses the international date line and \a breakLine is true, then
|
||||||
|
* the line will be split into two parts, broken at the date line. In this case the function
|
||||||
|
* will return two lines, corresponding to the portions at either side of the date line.
|
||||||
|
*
|
||||||
|
* \since QGIS 3.6
|
||||||
|
*/
|
||||||
|
QList< QList< QgsPointXY > > geodesicLine( const QgsPointXY &p1, const QgsPointXY &p2, double interval, bool breakLine = false ) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user