Adds immediate migration results
This commit is contained in:
2395 files changed
+357322
-2524
No files matched your search
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
+198
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* \file Accumulator.hpp
|
||||
* \brief Header for GeographicLib::Accumulator class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2010-2020) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_ACCUMULATOR_HPP)
|
||||
#define GEOGRAPHICLIB_ACCUMULATOR_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief An accumulator for sums
|
||||
*
|
||||
* This allows many numbers of floating point type \e T to be added together
|
||||
* with twice the normal precision. Thus if \e T is double, the effective
|
||||
* precision of the sum is 106 bits or about 32 decimal places.
|
||||
*
|
||||
* The implementation follows J. R. Shewchuk,
|
||||
* <a href="https://doi.org/10.1007/PL00009321"> Adaptive Precision
|
||||
* Floating-Point Arithmetic and Fast Robust Geometric Predicates</a>,
|
||||
* Discrete & Computational Geometry 18(3) 305--363 (1997).
|
||||
*
|
||||
* Approximate timings (summing a vector<double>)
|
||||
* - double: 2ns
|
||||
* - Accumulator<double>: 23ns
|
||||
*
|
||||
* In the documentation of the member functions, \e sum stands for the value
|
||||
* currently held in the accumulator.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Accumulator.cpp
|
||||
**********************************************************************/
|
||||
template<typename T = Math::real>
|
||||
class GEOGRAPHICLIB_EXPORT Accumulator {
|
||||
private:
|
||||
// _s + _t accumulators for the sum.
|
||||
T _s, _t;
|
||||
// Same as Math::sum, but requires abs(u) >= abs(v). This isn't currently
|
||||
// used.
|
||||
static T fastsum(T u, T v, T& t) {
|
||||
GEOGRAPHICLIB_VOLATILE T s = u + v;
|
||||
GEOGRAPHICLIB_VOLATILE T vp = s - u;
|
||||
t = v - vp;
|
||||
return s;
|
||||
}
|
||||
void Add(T y) {
|
||||
// Here's Shewchuk's solution...
|
||||
T u; // hold exact sum as [s, t, u]
|
||||
// Accumulate starting at least significant end
|
||||
y = Math::sum(y, _t, u);
|
||||
_s = Math::sum(y, _s, _t);
|
||||
// Start is _s, _t decreasing and non-adjacent. Sum is now (s + t + u)
|
||||
// exactly with s, t, u non-adjacent and in decreasing order (except for
|
||||
// possible zeros). The following code tries to normalize the result.
|
||||
// Ideally, we want _s = round(s+t+u) and _u = round(s+t+u - _s). The
|
||||
// following does an approximate job (and maintains the decreasing
|
||||
// non-adjacent property). Here are two "failures" using 3-bit floats:
|
||||
//
|
||||
// Case 1: _s is not equal to round(s+t+u) -- off by 1 ulp
|
||||
// [12, -1] - 8 -> [4, 0, -1] -> [4, -1] = 3 should be [3, 0] = 3
|
||||
//
|
||||
// Case 2: _s+_t is not as close to s+t+u as it shold be
|
||||
// [64, 5] + 4 -> [64, 8, 1] -> [64, 8] = 72 (off by 1)
|
||||
// should be [80, -7] = 73 (exact)
|
||||
//
|
||||
// "Fixing" these problems is probably not worth the expense. The
|
||||
// representation inevitably leads to small errors in the accumulated
|
||||
// values. The additional errors illustrated here amount to 1 ulp of the
|
||||
// less significant word during each addition to the Accumulator and an
|
||||
// additional possible error of 1 ulp in the reported sum.
|
||||
//
|
||||
// Incidentally, the "ideal" representation described above is not
|
||||
// canonical, because _s = round(_s + _t) may not be true. For example,
|
||||
// with 3-bit floats:
|
||||
//
|
||||
// [128, 16] + 1 -> [160, -16] -- 160 = round(145).
|
||||
// But [160, 0] - 16 -> [128, 16] -- 128 = round(144).
|
||||
//
|
||||
if (_s == 0) // This implies t == 0,
|
||||
_s = u; // so result is u
|
||||
else
|
||||
_t += u; // otherwise just accumulate u to t.
|
||||
}
|
||||
T Sum(T y) const {
|
||||
Accumulator a(*this);
|
||||
a.Add(y);
|
||||
return a._s;
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* Construct from a \e T. This is not declared explicit, so that you can
|
||||
* write <code>Accumulator<double> a = 5;</code>.
|
||||
*
|
||||
* @param[in] y set \e sum = \e y.
|
||||
**********************************************************************/
|
||||
Accumulator(T y = T(0)) : _s(y), _t(0) {
|
||||
static_assert(!std::numeric_limits<T>::is_integer,
|
||||
"Accumulator type is not floating point");
|
||||
}
|
||||
/**
|
||||
* Set the accumulator to a number.
|
||||
*
|
||||
* @param[in] y set \e sum = \e y.
|
||||
**********************************************************************/
|
||||
Accumulator& operator=(T y) { _s = y; _t = 0; return *this; }
|
||||
/**
|
||||
* Return the value held in the accumulator.
|
||||
*
|
||||
* @return \e sum.
|
||||
**********************************************************************/
|
||||
T operator()() const { return _s; }
|
||||
/**
|
||||
* Return the result of adding a number to \e sum (but don't change \e
|
||||
* sum).
|
||||
*
|
||||
* @param[in] y the number to be added to the sum.
|
||||
* @return \e sum + \e y.
|
||||
**********************************************************************/
|
||||
T operator()(T y) const { return Sum(y); }
|
||||
/**
|
||||
* Add a number to the accumulator.
|
||||
*
|
||||
* @param[in] y set \e sum += \e y.
|
||||
**********************************************************************/
|
||||
Accumulator& operator+=(T y) { Add(y); return *this; }
|
||||
/**
|
||||
* Subtract a number from the accumulator.
|
||||
*
|
||||
* @param[in] y set \e sum -= \e y.
|
||||
**********************************************************************/
|
||||
Accumulator& operator-=(T y) { Add(-y); return *this; }
|
||||
/**
|
||||
* Multiply accumulator by an integer. To avoid loss of accuracy, use only
|
||||
* integers such that \e n × \e T is exactly representable as a \e T
|
||||
* (i.e., ± powers of two). Use \e n = −1 to negate \e sum.
|
||||
*
|
||||
* @param[in] n set \e sum *= \e n.
|
||||
**********************************************************************/
|
||||
Accumulator& operator*=(int n) { _s *= n; _t *= n; return *this; }
|
||||
/**
|
||||
* Multiply accumulator by a number. The fma (fused multiply and add)
|
||||
* instruction is used (if available) in order to maintain accuracy.
|
||||
*
|
||||
* @param[in] y set \e sum *= \e y.
|
||||
**********************************************************************/
|
||||
Accumulator& operator*=(T y) {
|
||||
using std::fma;
|
||||
T d = _s; _s *= y;
|
||||
d = fma(y, d, -_s); // the error in the first multiplication
|
||||
_t = fma(y, _t, d); // add error to the second term
|
||||
return *this;
|
||||
}
|
||||
/**
|
||||
* Reduce accumulator to the range [-y/2, y/2].
|
||||
*
|
||||
* @param[in] y the modulus.
|
||||
**********************************************************************/
|
||||
Accumulator& remainder(T y) {
|
||||
using std::remainder;
|
||||
_s = remainder(_s, y);
|
||||
Add(0); // This renormalizes the result.
|
||||
return *this;
|
||||
}
|
||||
/**
|
||||
* Test equality of an Accumulator with a number.
|
||||
**********************************************************************/
|
||||
bool operator==(T y) const { return _s == y; }
|
||||
/**
|
||||
* Test inequality of an Accumulator with a number.
|
||||
**********************************************************************/
|
||||
bool operator!=(T y) const { return _s != y; }
|
||||
/**
|
||||
* Less operator on an Accumulator and a number.
|
||||
**********************************************************************/
|
||||
bool operator<(T y) const { return _s < y; }
|
||||
/**
|
||||
* Less or equal operator on an Accumulator and a number.
|
||||
**********************************************************************/
|
||||
bool operator<=(T y) const { return _s <= y; }
|
||||
/**
|
||||
* Greater operator on an Accumulator and a number.
|
||||
**********************************************************************/
|
||||
bool operator>(T y) const { return _s > y; }
|
||||
/**
|
||||
* Greater or equal operator on an Accumulator and a number.
|
||||
**********************************************************************/
|
||||
bool operator>=(T y) const { return _s >= y; }
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_ACCUMULATOR_HPP
|
||||
+313
@@ -0,0 +1,313 @@
|
||||
/**
|
||||
* \file AlbersEqualArea.hpp
|
||||
* \brief Header for GeographicLib::AlbersEqualArea class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2010-2023) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_ALBERSEQUALAREA_HPP)
|
||||
#define GEOGRAPHICLIB_ALBERSEQUALAREA_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Albers equal area conic projection
|
||||
*
|
||||
* Implementation taken from the report,
|
||||
* - J. P. Snyder,
|
||||
* <a href="https://pubs.usgs.gov/publication/pp1395"> Map Projections: A
|
||||
* Working Manual</a>, USGS Professional Paper 1395 (1987),
|
||||
* pp. 101--102.
|
||||
*
|
||||
* This is a implementation of the equations in Snyder except that divided
|
||||
* differences will be [have been] used to transform the expressions into
|
||||
* ones which may be evaluated accurately. [In this implementation, the
|
||||
* projection correctly becomes the cylindrical equal area or the azimuthal
|
||||
* equal area projection when the standard latitude is the equator or a
|
||||
* pole.]
|
||||
*
|
||||
* The ellipsoid parameters, the standard parallels, and the scale on the
|
||||
* standard parallels are set in the constructor. Internally, the case with
|
||||
* two standard parallels is converted into a single standard parallel, the
|
||||
* latitude of minimum azimuthal scale, with an azimuthal scale specified on
|
||||
* this parallel. This latitude is also used as the latitude of origin which
|
||||
* is returned by AlbersEqualArea::OriginLatitude. The azimuthal scale on
|
||||
* the latitude of origin is given by AlbersEqualArea::CentralScale. The
|
||||
* case with two standard parallels at opposite poles is singular and is
|
||||
* disallowed. The central meridian (which is a trivial shift of the
|
||||
* longitude) is specified as the \e lon0 argument of the
|
||||
* AlbersEqualArea::Forward and AlbersEqualArea::Reverse functions.
|
||||
* AlbersEqualArea::Forward and AlbersEqualArea::Reverse also return the
|
||||
* meridian convergence, γ, and azimuthal scale, \e k. A small square
|
||||
* aligned with the cardinal directions is projected to a rectangle with
|
||||
* dimensions \e k (in the E-W direction) and 1/\e k (in the N-S direction).
|
||||
* The E-W sides of the rectangle are oriented γ degrees
|
||||
* counterclockwise from the \e x axis. There is no provision in this class
|
||||
* for specifying a false easting or false northing or a different latitude
|
||||
* of origin.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-AlbersEqualArea.cpp
|
||||
*
|
||||
* <a href="ConicProj.1.html">ConicProj</a> is a command-line utility
|
||||
* providing access to the functionality of LambertConformalConic and
|
||||
* AlbersEqualArea.
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT AlbersEqualArea {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
real eps_, epsx_, epsx2_, tol_, tol0_;
|
||||
real _a, _f, _fm, _e2, _e, _e2m, _qZ, _qx;
|
||||
real _sign, _lat0, _k0;
|
||||
real _n0, _m02, _nrho0, _k2, _txi0, _scxi0, _sxi0;
|
||||
static const int numit_ = 5; // Newton iterations in Reverse
|
||||
static const int numit0_ = 20; // Newton iterations in Init
|
||||
static real hyp(real x) {
|
||||
using std::hypot;
|
||||
return hypot(real(1), x);
|
||||
}
|
||||
// atanh( e * x)/ e if f > 0
|
||||
// atan (sqrt(-e2) * x)/sqrt(-e2) if f < 0
|
||||
// x if f = 0
|
||||
real atanhee(real x) const {
|
||||
using std::atan, std::atanh;
|
||||
return _f > 0 ? atanh(_e * x)/_e : (_f < 0 ? (atan(_e * x)/_e) : x);
|
||||
}
|
||||
// return atanh(sqrt(x))/sqrt(x) - 1, accurate for small x
|
||||
static real atanhxm1(real x);
|
||||
|
||||
// Divided differences
|
||||
// Definition: Df(x,y) = (f(x)-f(y))/(x-y)
|
||||
// See:
|
||||
// W. M. Kahan and R. J. Fateman,
|
||||
// Symbolic computation of divided differences,
|
||||
// SIGSAM Bull. 33(2), 7-28 (1999)
|
||||
// https://doi.org/10.1145/334714.334716
|
||||
// http://www.cs.berkeley.edu/~fateman/papers/divdiff.pdf
|
||||
//
|
||||
// General rules
|
||||
// h(x) = f(g(x)): Dh(x,y) = Df(g(x),g(y))*Dg(x,y)
|
||||
// h(x) = f(x)*g(x):
|
||||
// Dh(x,y) = Df(x,y)*g(x) + Dg(x,y)*f(y)
|
||||
// = Df(x,y)*g(y) + Dg(x,y)*f(x)
|
||||
// = Df(x,y)*(g(x)+g(y))/2 + Dg(x,y)*(f(x)+f(y))/2
|
||||
//
|
||||
// sn(x) = x/sqrt(1+x^2): Dsn(x,y) = (x+y)/((sn(x)+sn(y))*(1+x^2)*(1+y^2))
|
||||
static real Dsn(real x, real y, real sx, real sy) {
|
||||
// sx = x/hyp(x)
|
||||
real t = x * y;
|
||||
return t > 0 ? (x + y) * Math::sq( (sx * sy)/t ) / (sx + sy) :
|
||||
(x - y != 0 ? (sx - sy) / (x - y) : 1);
|
||||
}
|
||||
// Datanhee(x,y) = (atanee(x)-atanee(y))/(x-y)
|
||||
// = atanhee((x-y)/(1-e^2*x*y))/(x-y)
|
||||
real Datanhee(real x, real y) const {
|
||||
real t = x - y, d = 1 - _e2 * x * y;
|
||||
return t == 0 ? 1 / d :
|
||||
(x*y < 0 ? atanhee(x) - atanhee(y) : atanhee(t / d)) / t;
|
||||
}
|
||||
// DDatanhee(x,y) = (Datanhee(1,y) - Datanhee(1,x))/(y-x)
|
||||
real DDatanhee(real x, real y) const;
|
||||
real DDatanhee0(real x, real y) const;
|
||||
real DDatanhee1(real x, real y) const;
|
||||
real DDatanhee2(real x, real y) const;
|
||||
void Init(real sphi1, real cphi1, real sphi2, real cphi2, real k1);
|
||||
real txif(real tphi) const;
|
||||
real tphif(real txi) const;
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor with a single standard parallel.
|
||||
*
|
||||
* @param[in] a equatorial radius of ellipsoid (meters).
|
||||
* @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
|
||||
* Negative \e f gives a prolate ellipsoid.
|
||||
* @param[in] stdlat standard parallel (degrees), the circle of tangency.
|
||||
* @param[in] k0 azimuthal scale on the standard parallel.
|
||||
* @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k0 is
|
||||
* not positive.
|
||||
* @exception GeographicErr if \e stdlat is not in [−90°,
|
||||
* 90°].
|
||||
**********************************************************************/
|
||||
AlbersEqualArea(real a, real f, real stdlat, real k0);
|
||||
|
||||
/**
|
||||
* Constructor with two standard parallels.
|
||||
*
|
||||
* @param[in] a equatorial radius of ellipsoid (meters).
|
||||
* @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
|
||||
* Negative \e f gives a prolate ellipsoid.
|
||||
* @param[in] stdlat1 first standard parallel (degrees).
|
||||
* @param[in] stdlat2 second standard parallel (degrees).
|
||||
* @param[in] k1 azimuthal scale on the standard parallels.
|
||||
* @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k1 is
|
||||
* not positive.
|
||||
* @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in
|
||||
* [−90°, 90°], or if \e stdlat1 and \e stdlat2 are
|
||||
* opposite poles.
|
||||
**********************************************************************/
|
||||
AlbersEqualArea(real a, real f, real stdlat1, real stdlat2, real k1);
|
||||
|
||||
/**
|
||||
* Constructor with two standard parallels specified by sines and cosines.
|
||||
*
|
||||
* @param[in] a equatorial radius of ellipsoid (meters).
|
||||
* @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
|
||||
* Negative \e f gives a prolate ellipsoid.
|
||||
* @param[in] sinlat1 sine of first standard parallel.
|
||||
* @param[in] coslat1 cosine of first standard parallel.
|
||||
* @param[in] sinlat2 sine of second standard parallel.
|
||||
* @param[in] coslat2 cosine of second standard parallel.
|
||||
* @param[in] k1 azimuthal scale on the standard parallels.
|
||||
* @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k1 is
|
||||
* not positive.
|
||||
* @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in
|
||||
* [−90°, 90°], or if \e stdlat1 and \e stdlat2 are
|
||||
* opposite poles.
|
||||
*
|
||||
* This allows parallels close to the poles to be specified accurately.
|
||||
* This routine computes the latitude of origin and the azimuthal scale at
|
||||
* this latitude. If \e dlat = abs(\e lat2 − \e lat1) ≤ 160°,
|
||||
* then the error in the latitude of origin is less than 4.5 ×
|
||||
* 10<sup>−14</sup>d;.
|
||||
**********************************************************************/
|
||||
AlbersEqualArea(real a, real f,
|
||||
real sinlat1, real coslat1,
|
||||
real sinlat2, real coslat2,
|
||||
real k1);
|
||||
|
||||
/**
|
||||
* Set the azimuthal scale for the projection.
|
||||
*
|
||||
* @param[in] lat (degrees).
|
||||
* @param[in] k azimuthal scale at latitude \e lat (default 1).
|
||||
* @exception GeographicErr \e k is not positive.
|
||||
* @exception GeographicErr if \e lat is not in (−90°,
|
||||
* 90°).
|
||||
*
|
||||
* This allows a "latitude of conformality" to be specified.
|
||||
**********************************************************************/
|
||||
void SetScale(real lat, real k = real(1));
|
||||
|
||||
/**
|
||||
* Forward projection, from geographic to Lambert conformal conic.
|
||||
*
|
||||
* @param[in] lon0 central meridian longitude (degrees).
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[out] x easting of point (meters).
|
||||
* @param[out] y northing of point (meters).
|
||||
* @param[out] gamma meridian convergence at point (degrees).
|
||||
* @param[out] k azimuthal scale of projection at point; the radial
|
||||
* scale is the 1/\e k.
|
||||
*
|
||||
* The latitude origin is given by AlbersEqualArea::LatitudeOrigin(). No
|
||||
* false easting or northing is added and \e lat should be in the range
|
||||
* [−90°, 90°]. The values of \e x and \e y returned for
|
||||
* points which project to infinity (i.e., one or both of the poles) will
|
||||
* be large but finite.
|
||||
**********************************************************************/
|
||||
void Forward(real lon0, real lat, real lon,
|
||||
real& x, real& y, real& gamma, real& k) const;
|
||||
|
||||
/**
|
||||
* Reverse projection, from Lambert conformal conic to geographic.
|
||||
*
|
||||
* @param[in] lon0 central meridian longitude (degrees).
|
||||
* @param[in] x easting of point (meters).
|
||||
* @param[in] y northing of point (meters).
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] gamma meridian convergence at point (degrees).
|
||||
* @param[out] k azimuthal scale of projection at point; the radial
|
||||
* scale is the 1/\e k.
|
||||
*
|
||||
* The latitude origin is given by AlbersEqualArea::LatitudeOrigin(). No
|
||||
* false easting or northing is added. The value of \e lon returned is in
|
||||
* the range [−180°, 180°]. The value of \e lat returned is
|
||||
* in the range [−90°, 90°]. If the input point is outside
|
||||
* the legal projected space the nearest pole is returned.
|
||||
**********************************************************************/
|
||||
void Reverse(real lon0, real x, real y,
|
||||
real& lat, real& lon, real& gamma, real& k) const;
|
||||
|
||||
/**
|
||||
* AlbersEqualArea::Forward without returning the convergence and
|
||||
* scale.
|
||||
**********************************************************************/
|
||||
void Forward(real lon0, real lat, real lon,
|
||||
real& x, real& y) const {
|
||||
real gamma, k;
|
||||
Forward(lon0, lat, lon, x, y, gamma, k);
|
||||
}
|
||||
|
||||
/**
|
||||
* AlbersEqualArea::Reverse without returning the convergence and
|
||||
* scale.
|
||||
**********************************************************************/
|
||||
void Reverse(real lon0, real x, real y,
|
||||
real& lat, real& lon) const {
|
||||
real gamma, k;
|
||||
Reverse(lon0, x, y, lat, lon, gamma, k);
|
||||
}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _a; }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the value used in
|
||||
* the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _f; }
|
||||
|
||||
/**
|
||||
* @return latitude of the origin for the projection (degrees).
|
||||
*
|
||||
* This is the latitude of minimum azimuthal scale and equals the \e stdlat
|
||||
* in the 1-parallel constructor and lies between \e stdlat1 and \e stdlat2
|
||||
* in the 2-parallel constructors.
|
||||
**********************************************************************/
|
||||
Math::real OriginLatitude() const { return _lat0; }
|
||||
|
||||
/**
|
||||
* @return central scale for the projection. This is the azimuthal scale
|
||||
* on the latitude of origin.
|
||||
**********************************************************************/
|
||||
Math::real CentralScale() const { return _k0; }
|
||||
///@}
|
||||
|
||||
/**
|
||||
* A global instantiation of AlbersEqualArea with the WGS84 ellipsoid, \e
|
||||
* stdlat = 0, and \e k0 = 1. This degenerates to the cylindrical equal
|
||||
* area projection.
|
||||
**********************************************************************/
|
||||
static const AlbersEqualArea& CylindricalEqualArea();
|
||||
|
||||
/**
|
||||
* A global instantiation of AlbersEqualArea with the WGS84 ellipsoid, \e
|
||||
* stdlat = 90°, and \e k0 = 1. This degenerates to the
|
||||
* Lambert azimuthal equal area projection.
|
||||
**********************************************************************/
|
||||
static const AlbersEqualArea& AzimuthalEqualAreaNorth();
|
||||
|
||||
/**
|
||||
* A global instantiation of AlbersEqualArea with the WGS84 ellipsoid, \e
|
||||
* stdlat = −90°, and \e k0 = 1. This degenerates to the
|
||||
* Lambert azimuthal equal area projection.
|
||||
**********************************************************************/
|
||||
static const AlbersEqualArea& AzimuthalEqualAreaSouth();
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_ALBERSEQUALAREA_HPP
|
||||
@@ -0,0 +1,764 @@
|
||||
/**
|
||||
* \file Angle.hpp
|
||||
* \brief Header for the GeographicLib::AngleT class
|
||||
*
|
||||
* The class provide an accurate representation of angle via 3 numbers, its
|
||||
* sine and cosine, and the number of turns.
|
||||
*
|
||||
* Copyright (c) Charles Karney (2024-2025) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_ANGLE_HPP)
|
||||
#define GEOGRAPHICLIB_ANGLE_HPP 1
|
||||
|
||||
#include <string>
|
||||
#include <GeographicLib/Math.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief An accurate representation of angles.
|
||||
*
|
||||
* @tparam T the working floating point type.
|
||||
*
|
||||
* This class provides an accurate representation of angle via 3 numbers, its
|
||||
* sine = \e s and cosine = \e c, and the number of turns = \e n. The angle
|
||||
* is then 2\e n π + atan2(\e s, \e c). This representation offers
|
||||
* several advantages:
|
||||
* - the cardinal directors (multiples of 90°) are exactly represented (a
|
||||
* benefit shared by representing angles as degrees);
|
||||
* - angles very close to any cardinal direction are accurately represented;
|
||||
* - there's no loss of precision with large angles (outside the "normal"
|
||||
* range [−180°, +180°]);
|
||||
* - various operations, such as adding a multiple of 90° to an angle are
|
||||
* performed exactly.
|
||||
* .
|
||||
* This representation does not favor degrees over radians. However, the
|
||||
* one-argument constructor, AngleT(T), does the conversion from degrees and
|
||||
* the cast to T, AngleT::operator T(), returns the angle in degrees. There
|
||||
* are alternatives, radians(T) and radians() const, to allow these
|
||||
* conversions with radians.
|
||||
*
|
||||
* N.B. \e n is stored as a real. This allows it to be inf or nan.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Angle.cpp
|
||||
**********************************************************************/
|
||||
template<typename T = Math::real>
|
||||
class AngleT {
|
||||
// No GEOGRAPHICLIB_EXPORT because this is a template class (like
|
||||
// PolygonAreaT). Not sure why Accumulator needs GEOGRAPHICLIB_EXPORT.
|
||||
private:
|
||||
T _s, _c, _n;
|
||||
static T rnd(T x);
|
||||
public:
|
||||
/** \name Creating AngleT objects.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* The default constructor.
|
||||
*
|
||||
* This sets the angle to 0.
|
||||
**********************************************************************/
|
||||
AngleT() : _s(0), _c(1), _n(0) {}
|
||||
/**
|
||||
* The general constructor.
|
||||
*
|
||||
* @param[in] s the sine component.
|
||||
* @param[in] c the cosine component.
|
||||
* @param[in] num the number of turns (default 0).
|
||||
* @param[in] normp are \e s and \e c normalized (default false).
|
||||
*
|
||||
* \warning either \e s or \e c can be infinite, but not both.
|
||||
*
|
||||
* By default, the point (\e s, \e c) is scaled to lie on the unit circle.
|
||||
* Setting \e normp = true skips this step; in this case (\e s, \e c)
|
||||
* should already lie on the unit circle.
|
||||
**********************************************************************/
|
||||
AngleT(T s, T c, T num = 0, bool normp = false);
|
||||
/**
|
||||
* The 1-argument constructor.
|
||||
*
|
||||
* @param[in] deg the angle in degrees.
|
||||
*
|
||||
* \note This is an explicit constructor to avoid accidental conversions.
|
||||
**********************************************************************/
|
||||
explicit AngleT(T deg);
|
||||
/**
|
||||
* The convert an angle in degrees to an AngleT.
|
||||
*
|
||||
* @param[in] deg the angle in degrees.
|
||||
* @return the AngleT.
|
||||
*
|
||||
* \note This mimics the behavior of AngleT(T deg);
|
||||
**********************************************************************/
|
||||
static AngleT degrees(T deg);
|
||||
/**
|
||||
* Convert an angle in radians to an AngleT.
|
||||
*
|
||||
* @param[in] rad the angle in radians.
|
||||
* @return the AngleT.
|
||||
*
|
||||
* \note This is the radians analog of degrees(T).
|
||||
**********************************************************************/
|
||||
static AngleT radians(T rad);
|
||||
/**
|
||||
* Convert an lambertian to an AngleT.
|
||||
*
|
||||
* @param[in] q the lambertian of the angle.
|
||||
* @return the AngleT.
|
||||
*
|
||||
* This sets the angle to atan(sinh(\e q)).
|
||||
**********************************************************************/
|
||||
static AngleT lam(T q);
|
||||
/**
|
||||
* Not an angle.
|
||||
*
|
||||
* @return the AngleT equivalent to not-a-number.
|
||||
**********************************************************************/
|
||||
static AngleT NaN();
|
||||
/**
|
||||
* A cardinal direction.
|
||||
*
|
||||
* @param[in] q the number of quarter turns.
|
||||
* @return the AngleT equivalent to \e q quarter turns.
|
||||
*
|
||||
* \e q is rounded to an integer and \e q = ±0 are distinguished.
|
||||
* NaN() is returned is \e q is not finite.
|
||||
**********************************************************************/
|
||||
static AngleT cardinal(T q);
|
||||
/**
|
||||
* Return a tiny angle.
|
||||
*
|
||||
* @return a tiny angle.
|
||||
*
|
||||
* This allows angles extremely close to the cardinal directions to be
|
||||
* generated. The round() function will flush this angle to 0.
|
||||
**********************************************************************/
|
||||
static AngleT eps();
|
||||
///@}
|
||||
|
||||
/** \name Inspector functions.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return the sine of the angle.
|
||||
**********************************************************************/
|
||||
T s() const { return _s; }
|
||||
/**
|
||||
* @return the cosine of the angle.
|
||||
**********************************************************************/
|
||||
T c() const { return _c; }
|
||||
/**
|
||||
* @return the tangent of the angle.
|
||||
**********************************************************************/
|
||||
T t() const { return _s/_c; }
|
||||
/**
|
||||
* @return the number of turns.
|
||||
**********************************************************************/
|
||||
T n() const {
|
||||
return _n + 0; // Convert -0 to +0
|
||||
}
|
||||
/**
|
||||
* @return the number of turns treating −180° as +180° less 1
|
||||
* turn.
|
||||
**********************************************************************/
|
||||
T n0() const;
|
||||
///@}
|
||||
|
||||
/** \name Converting AngleT into other representations
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Convert an AngleT to degrees via a type conversion.
|
||||
*
|
||||
* @return the angle in degrees.
|
||||
*
|
||||
* \note This is an explicit type conversion to avoid accidental
|
||||
* conversions.
|
||||
**********************************************************************/
|
||||
explicit operator T() const;
|
||||
/**
|
||||
* Convert an AngleT to degrees.
|
||||
*
|
||||
* @return the angle in degrees.
|
||||
*
|
||||
* \note This mimics the behavior of AngleT::operator T().
|
||||
**********************************************************************/
|
||||
T degrees() const;
|
||||
/**
|
||||
* Convert an AngleT to degrees ignoring the number of turns.
|
||||
*
|
||||
* @return the angle in degrees assuming n() is zero.
|
||||
**********************************************************************/
|
||||
T degrees0() const;
|
||||
/**
|
||||
* Convert an AngleT to radians.
|
||||
*
|
||||
* @return the angle in radians.
|
||||
*
|
||||
* \note This is the radians analog of degrees().
|
||||
**********************************************************************/
|
||||
T radians() const;
|
||||
/**
|
||||
* Convert an AngleT to radians ignoring the number of turns.
|
||||
*
|
||||
* @return the angle in radians assuming n() is zero.
|
||||
*
|
||||
* \note This is the radians analog of degrees0().
|
||||
**********************************************************************/
|
||||
T radians0() const;
|
||||
/**
|
||||
* Return the lambertian of the AngleT.
|
||||
*
|
||||
* @return the lambertian.
|
||||
*
|
||||
* The lambertian of φ is asinh tan φ.
|
||||
**********************************************************************/
|
||||
T lam() const;
|
||||
/**
|
||||
* Return the nearest cardinal direction as an AngleT.
|
||||
*
|
||||
* @param[in] ind an indicator.
|
||||
* @return the nearest cardinal direction as an AngleT.
|
||||
*
|
||||
* If \e ind == 0 (the default) the closest cardinal direction is returned.
|
||||
* Otherwise, if \e ind is even, the closest even (N/S) cardinal direction
|
||||
* is returned; or, if \e ind is odd, the closest odd (E/W) cardinal
|
||||
* direction is returned.
|
||||
**********************************************************************/
|
||||
AngleT nearest(unsigned ind = 0U) const;
|
||||
/**
|
||||
* Return the nearest cardinal direction as an integer.
|
||||
*
|
||||
* @return the nearest cardinal direction as an integer.
|
||||
*
|
||||
* \note This is the reverse of cardinal(T).
|
||||
**********************************************************************/
|
||||
T ncardinal() const;
|
||||
unsigned quadrant() const;
|
||||
///@}
|
||||
|
||||
/** \name Elementary arithmetic operations on AngleT
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Return the negated AngleT.
|
||||
*
|
||||
* @return minus the AngleT
|
||||
**********************************************************************/
|
||||
AngleT operator-() const;
|
||||
/**
|
||||
* Implement the += operator.
|
||||
*
|
||||
* @param[in] p the AngleT to be added.
|
||||
* @return the current AngleT after the addition.
|
||||
**********************************************************************/
|
||||
AngleT& operator+=(const AngleT& p);
|
||||
/**
|
||||
* Implement the -= operator.
|
||||
*
|
||||
* @param[in] p the AngleT to be subtracted.
|
||||
* @return the current AngleT after the subtraction.
|
||||
**********************************************************************/
|
||||
AngleT& operator-=(const AngleT& p);
|
||||
/**
|
||||
* Implement the + operator.
|
||||
*
|
||||
* @param[in] p the AngleT to be added.
|
||||
* @return the result of the addition; the current AngleT is not modified.
|
||||
**********************************************************************/
|
||||
AngleT operator+(const AngleT& p) const;
|
||||
/**
|
||||
* Implement the - operator.
|
||||
*
|
||||
* @param[in] p the AngleT to be subtracted.
|
||||
* @return the result of the subtraction; the current AngleT is not
|
||||
* modified.
|
||||
**********************************************************************/
|
||||
AngleT operator-(const AngleT& p) const;
|
||||
/**
|
||||
* Test for a zero angle.
|
||||
*
|
||||
* @param[in] mult multiplier of machine epsilon used in test (default 0).
|
||||
* @return true if this AngleT is withing \e mult ε of zero.
|
||||
**********************************************************************/
|
||||
bool zerop(T mult = 0) const;
|
||||
/**
|
||||
* Implement the == operator.
|
||||
*
|
||||
* @param[in] p the AngleT to be compared against
|
||||
* @return *this == \e p.
|
||||
**********************************************************************/
|
||||
bool operator==(const AngleT& p) const;
|
||||
///@}
|
||||
|
||||
/** \name Operations which modify a AngleT
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* "Round" the AngleT the == operator.
|
||||
*
|
||||
* @return the AngleT with tiny values of s() and c() set to ±0.
|
||||
*
|
||||
* This ensures that the smallest gaps between sine and cosine values is
|
||||
* ε/2048.
|
||||
**********************************************************************/
|
||||
AngleT& round();
|
||||
/**
|
||||
* Renormalize the sine and cosine values
|
||||
*
|
||||
* @return the modified AngleT.
|
||||
*
|
||||
* During arithmetic operations on AngleT object, no effort is mode to
|
||||
* ensure that (s(), c()) remains on the unit circle. This function
|
||||
* corrects this.
|
||||
**********************************************************************/
|
||||
AngleT& renormalize();
|
||||
/**
|
||||
* Reduce the angle to [−180°, +180°]
|
||||
*
|
||||
* @return the modified AngleT, obtained by setting n() to zero.
|
||||
**********************************************************************/
|
||||
AngleT& setn(T n = 0);
|
||||
/**
|
||||
* Reduce the angle to (−180°, +180°]
|
||||
*
|
||||
* @return the modified AngleT.
|
||||
*
|
||||
* This differs from setn(T) by treating −180° as +180° less
|
||||
* 1 turn.
|
||||
**********************************************************************/
|
||||
AngleT& setn0(T n = 0);
|
||||
/**
|
||||
* Set the quadrant of an AngleT the angle to (−180°, +180°]
|
||||
*
|
||||
* @param[in] q the quadrant.
|
||||
* @return the modified AngleT.
|
||||
*
|
||||
* This sets the signs of s() and c() according to e q.
|
||||
*
|
||||
* \note Only the low two bits of \e q are used. n() is unchanged.
|
||||
**********************************************************************/
|
||||
AngleT& setquadrant(unsigned q);
|
||||
/**
|
||||
* Reflect the angle is various ways
|
||||
*
|
||||
* @param[in] flips change the sign of s()
|
||||
* @param[in] flipc change the sign of c()
|
||||
* @param[in] swapp swap s() and c()
|
||||
* @return the modified AngleT.
|
||||
*
|
||||
* \note The operations are carried out in the order of the parameters.
|
||||
**********************************************************************/
|
||||
AngleT& reflect(bool flips, bool flipc = false, bool swapp = false);
|
||||
///@}
|
||||
|
||||
/** \name Operations which return a new AngleT
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Return an AngleT in [−180°, +180°].
|
||||
*
|
||||
* @return the new AngleT.
|
||||
*
|
||||
* This returns the AngleT with n() set to zero.
|
||||
**********************************************************************/
|
||||
AngleT base() const;
|
||||
/**
|
||||
* Return an AngleT centered about another AngleT
|
||||
*
|
||||
* @param[in] c the center AngleT
|
||||
* @return the new AngleT.
|
||||
*
|
||||
* This returns the result of adjusting n() so that the new AngleT is with
|
||||
* ±180° of \e c.
|
||||
**********************************************************************/
|
||||
AngleT rebase(const AngleT& c) const;
|
||||
/**
|
||||
* Return an AngleT with the sign optionally flipped
|
||||
*
|
||||
* @param[in] mult
|
||||
* @return the new AngleT.
|
||||
*
|
||||
* return signbit(\e mult) ? -*this : *this.
|
||||
**********************************************************************/
|
||||
AngleT flipsign(T mult) const;
|
||||
/**
|
||||
* The "reduced latitude" operation.
|
||||
*
|
||||
* @param[in] m
|
||||
* @return the atan(m * tan(*this))
|
||||
*
|
||||
* However the quadrant of the result tracking that of *this through
|
||||
* multiples turns.
|
||||
**********************************************************************/
|
||||
AngleT modang(T m) const;
|
||||
///@}
|
||||
|
||||
/** \name Converting AngleT to and from a string representation
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Interpret two strings as latitude and longitude.
|
||||
*
|
||||
* @param[in] stra the first string
|
||||
* @param[in] strb the second string
|
||||
* @param[out] lat the latitude
|
||||
* @param[out] lon the longitude
|
||||
* @param[in] longfirst (default false) whether the longitude is given
|
||||
* first.
|
||||
*
|
||||
* In the absence of hemisphere indicators (N/S for latitude and E/W for
|
||||
* longitude), it is assumed that the first string is the latitude.
|
||||
* Setting \e longfirst = true uses the opposite convention. The
|
||||
* hemisphere indicators can also be used to set the signs of the angles.
|
||||
**********************************************************************/
|
||||
static void DecodeLatLon(const std::string& stra, const std::string& strb,
|
||||
AngleT& lat, AngleT& lon,
|
||||
bool longfirst = false);
|
||||
/**
|
||||
* Interpret a string as azimuth
|
||||
*
|
||||
* @param[in] azistr the string representing the azimuth
|
||||
* @return the azimuth
|
||||
*
|
||||
* The hemisphere indicators E/W can be used to set the sign of the
|
||||
* azimuth.
|
||||
**********************************************************************/
|
||||
static AngleT DecodeAzimuth(const std::string& azistr);
|
||||
/**
|
||||
* Create a string for a latitude-longitude pair.
|
||||
*
|
||||
* @param[in] lat the latitude.
|
||||
* @param[in] lon the longitude.
|
||||
* @param[in] prec the precision relative to 1°.
|
||||
* @param[in] dms (default false) whether to use degrees/minutes/seconds as
|
||||
* opposed to decimal degrees
|
||||
* @param[in] dmssep (default NULL) the separator to use with the DMS
|
||||
* representation instead of d ' ".
|
||||
* @param[in] longfirst (default false) whether to list the longitude
|
||||
* first.
|
||||
* @return string representation
|
||||
*
|
||||
* With dms = true the hemisphere indicators N/S and E/W are used to
|
||||
* indicator the signs of the latitude and longitude.
|
||||
**********************************************************************/
|
||||
static std::string LatLonString(AngleT lat, AngleT lon, int prec,
|
||||
bool dms = false, char dmssep = '\0',
|
||||
bool longfirst = false);
|
||||
/**
|
||||
* Create a string for an azimuth.
|
||||
*
|
||||
* @param[in] azi the azimuth.
|
||||
* @param[in] prec the precision relative to 1°.
|
||||
* @param[in] dms (default false) whether to use degrees/minutes/seconds as
|
||||
* opposed to decimal degrees
|
||||
* @param[in] dmssep (default NULL) the separator to use with the DMS
|
||||
* representation instead of d ' ".
|
||||
* @return string representation
|
||||
*
|
||||
* With dms = true the hemisphere indicators and E/W is used to
|
||||
* indicator the sign of the azimuth.
|
||||
**********************************************************************/
|
||||
static std::string AzimuthString(AngleT azi, int prec,
|
||||
bool dms = false, char dmssep = '\0');
|
||||
///@}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T>::AngleT(T s, T c, T num, bool normp)
|
||||
: _s(s)
|
||||
, _c(c)
|
||||
, _n(num)
|
||||
{
|
||||
using std::isfinite, std::isnan, std::isinf, std::hypot,
|
||||
std::copysign, std::rint;
|
||||
_n = rint(_n);
|
||||
if (!normp) {
|
||||
// Cannot just use Math::norm because of all the special cases
|
||||
T h = hypot(_s, _c);
|
||||
if (h == 0) {
|
||||
// If y is +/-0 and x = -0, +/-pi is returned.
|
||||
// If y is +/-0 and x = +0, +/-0 is returned.
|
||||
// So retain the sign of _s = +/-0
|
||||
_c = copysign(T(1), _c);
|
||||
} else if (isfinite(h)) {
|
||||
_s /= h; _c /= h;
|
||||
} else if (isnan(h) || (isinf(_s) && isinf(_c)))
|
||||
_s = _c = Math::NaN();
|
||||
else if (isinf(_s)) {
|
||||
// infinite, finite
|
||||
_s = copysign(T(1), _s);
|
||||
_c = copysign(T(0), _c);
|
||||
} else {
|
||||
// isinf(cos); finite, infinite
|
||||
_s = copysign(T(0), _s);
|
||||
_c = copysign(T(1), _c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T>::AngleT(T deg) {
|
||||
using std::rint;
|
||||
Math::sincosd(deg, _s, _c);
|
||||
_n = rint( (deg - Math::atan2d(_s, _c)) / Math::td );
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T>::operator T() const {
|
||||
T d = degrees0();
|
||||
// Preserve sign of +/-0
|
||||
return _n == 0 ? d : d + Math::td * _n;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T> AngleT<T>::degrees(T deg) {
|
||||
return AngleT<T>(deg);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T AngleT<T>::degrees() const {
|
||||
return T(*this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T AngleT<T>::degrees0() const {
|
||||
return Math::atan2d(_s, _c);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T> AngleT<T>::radians(T rad) {
|
||||
using std::sin, std::cos, std::atan2, std::rint;
|
||||
T sn = sin(rad), cs = cos(rad);
|
||||
return AngleT<T>(sn, cs, rint( (rad - atan2(sn, cs)) / (2 * Math::pi()) ),
|
||||
true);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T AngleT<T>::radians() const {
|
||||
T r = radians0();
|
||||
// Preserve sign of +/-0
|
||||
return _n == 0 ? r : r + 2 * Math::pi() * _n;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T AngleT<T>::radians0() const {
|
||||
using std::atan2;
|
||||
return atan2(_s, _c);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T> AngleT<T>::lam(T psi) {
|
||||
using std::sinh;
|
||||
return AngleT<T>(sinh(psi), 1, 0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T AngleT<T>::lam() const {
|
||||
using std::asinh;
|
||||
return asinh(t());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T> AngleT<T>::NaN() {
|
||||
return AngleT<T>(Math::NaN(), Math::NaN(), 0, true);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T AngleT<T>::ncardinal() const {
|
||||
using std::signbit, std::fabs;
|
||||
int iq = (signbit(_s) ? -1 : 1) * (signbit(_c) ?
|
||||
( -_c >= fabs(_s) ? 2 : 1 ) :
|
||||
( _c >= fabs(_s) ? 0 : 1 ));
|
||||
return 4 * _n + iq;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T> AngleT<T>::eps() {
|
||||
return AngleT<T>(std::numeric_limits<T>::epsilon() / (1 << 20), 1, 0, true);
|
||||
}
|
||||
|
||||
// AngleT<T> AngleT<T>::operator+() const { return *this; }
|
||||
template<typename T>
|
||||
inline AngleT<T> AngleT<T>::operator-() const {
|
||||
return AngleT<T>(-_s, _c, -_n, true);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T>& AngleT<T>::operator+=(const AngleT<T>& p) {
|
||||
using std::rint;
|
||||
T q = ncardinal() + p.ncardinal();
|
||||
T c = _c * p._c - _s * p._s;
|
||||
_s = _s * p._c + _c * p._s;
|
||||
_c = c;
|
||||
_n += p._n;
|
||||
q -= ncardinal();
|
||||
_n += rint(q / 4);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T> AngleT<T>::operator+(const AngleT<T>& p) const {
|
||||
AngleT<T> t = *this; t += p;
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T>& AngleT<T>::operator-=(const AngleT<T>& p) {
|
||||
*this += -p;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T> AngleT<T>::operator-(const AngleT<T>& p) const {
|
||||
AngleT<T> t = *this; t -= p;
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool AngleT<T>::zerop(T mult) const {
|
||||
using std::fabs;
|
||||
return _n == 0 &&_c > 0 &&
|
||||
fabs(_s) <= mult * std::numeric_limits<T>::epsilon();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool AngleT<T>::operator==(const AngleT<T>& p) const {
|
||||
AngleT<T> t = *this; t -= p;
|
||||
return t.zerop();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T>& AngleT<T>::round() {
|
||||
_s = rnd(_s); _c = rnd(_c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T> AngleT<T>::base() const {
|
||||
return AngleT<T>(_s, _c, 0, true);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T> AngleT<T>::rebase(const AngleT<T>& c) const {
|
||||
// This is exact for c = cardinal direction
|
||||
// return (*this - c).base() + c;
|
||||
AngleT<T> t = *this;
|
||||
return t.setn0(((*this - c).base() + c).n0());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T>& AngleT<T>::renormalize() {
|
||||
using std::hypot;
|
||||
T h = hypot(_s, _c); _s /= h; _c /= h;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T>& AngleT<T>::setn(T n) {
|
||||
using std::rint;
|
||||
_n = rint(n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T AngleT<T>::n0() const {
|
||||
using std::signbit;
|
||||
return (_n - (_s == 0 && signbit(_s) && _c < 0 ? 1 : 0)) + 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T>& AngleT<T>::setn0(T n) {
|
||||
using std::rint, std::signbit;
|
||||
_n = rint(n) + (_s == 0 && signbit(_s) && _c < 0 ? 1 : 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T>& AngleT<T>::setquadrant(unsigned q) {
|
||||
using std::copysign;
|
||||
_s = copysign(_s, T( q & 2U ? -1 : 1 ));
|
||||
_c = copysign(_c, T( ((q >> 1) ^ q) & 1U ? -1 : 1 ));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline unsigned AngleT<T>::quadrant() const {
|
||||
using std::signbit;
|
||||
return 2U * signbit(_s) + (signbit(_c) ^ signbit(_s));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T>& AngleT<T>::reflect(bool flips, bool flipc, bool swapp) {
|
||||
using std::swap;
|
||||
if (flips) _s *= -1;
|
||||
if (flipc) _c *= -1;
|
||||
if (swapp) swap(_s, _c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T> AngleT<T>::flipsign(T mult) const {
|
||||
using std::signbit;
|
||||
return signbit(mult) ? -*this : *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T> AngleT<T>::modang(T m) const {
|
||||
using std::signbit;
|
||||
return signbit(m) ? AngleT<T>::NaN() :
|
||||
// Avoid nans if m == inf.
|
||||
AngleT<T>( _s * (m > 1 ? 1 : m),
|
||||
_c / (m > 1 ? m : 1),
|
||||
_n );
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T> AngleT<T>::cardinal(T q) {
|
||||
using std::isfinite, std::rint, std::remainder;
|
||||
if (!isfinite(q)) return AngleT<T>::NaN();
|
||||
q = rint(q);
|
||||
int iq = int(remainder(q, T(4)));
|
||||
// iq is in [-2, 2];
|
||||
// We could fold iq = -2 to iq = 2; but this way works too.
|
||||
T s, c, z = 0;
|
||||
switch (iq) {
|
||||
case -2: s = -z; c = -1; break;
|
||||
case -1: s = -1; c = z; break;
|
||||
case 1: s = 1; c = z; break;
|
||||
case 2: s = z; c = -1; break;
|
||||
default:
|
||||
// iq = 0, but distinguish q = +/-0
|
||||
s = q != 0 ? z : q; c = 1;
|
||||
break;
|
||||
}
|
||||
return AngleT<T>(s, c, rint((q - iq) / 4), true);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline AngleT<T> AngleT<T>::nearest(unsigned ind) const {
|
||||
using std::fabs, std::copysign;
|
||||
T s, c;
|
||||
if (ind == 0U) {
|
||||
if (fabs(_c) >= fabs(_s)) {
|
||||
s = copysign(T(0), _s); c = copysign(T(1), _c);
|
||||
} else {
|
||||
s = copysign(T(1), _s); c = copysign(T(0), _c);
|
||||
}
|
||||
} else if ((ind & 1U) == 0U) { // ind nonzero and even
|
||||
s = copysign(T(0), _s); c = copysign(T(1), _c);
|
||||
} else { // ind odd
|
||||
s = copysign(T(1), _s); c = copysign(T(0), _c);
|
||||
}
|
||||
return AngleT<T>(s, c, _n, true);
|
||||
}
|
||||
|
||||
using Angle = AngleT<Math::real>;
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_ANGLE_HPP
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
/**
|
||||
* \file AuxAngle.hpp
|
||||
* \brief Header for the GeographicLib::AuxAngle class
|
||||
*
|
||||
* This file is an implementation of the methods described in
|
||||
* - C. F. F. Karney,
|
||||
* <a href="https://doi.org/10.1080/00396265.2023.2217604">
|
||||
* On auxiliary latitudes,</a>
|
||||
* Survey Review 56(395), 165--180 (2024);
|
||||
* preprint
|
||||
* <a href="https://arxiv.org/abs/2212.05818">arXiv:2212.05818</a>.
|
||||
* .
|
||||
* Copyright (c) Charles Karney (2022-2023) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_AUXANGLE_HPP)
|
||||
#define GEOGRAPHICLIB_AUXANGLE_HPP 1
|
||||
|
||||
#include <GeographicLib/Math.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief An accurate representation of angles.
|
||||
*
|
||||
* This class is an implementation of the methods described in
|
||||
* - C. F. F. Karney,
|
||||
* <a href="https://doi.org/10.1080/00396265.2023.2217604">
|
||||
* On auxiliary latitudes,</a>
|
||||
* Survey Review 56(395), 165--180 (2024);
|
||||
* preprint
|
||||
* <a href="https://arxiv.org/abs/2212.05818">arXiv:2212.05818</a>.
|
||||
*
|
||||
* An angle is represented be the \e y and \e x coordinates of a point in the
|
||||
* 2d plane. The two coordinates are proportional to the sine and cosine of
|
||||
* the angle. This allows angles close to the cardinal points to be
|
||||
* represented accurately. It also saves on unnecessary recomputations of
|
||||
* trigonometric functions of the angle. Only angles in [−180°,
|
||||
* 180°] can be represented. (A possible extension would be to keep
|
||||
* count of the number of turns.)
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-AuxAngle.cpp
|
||||
*
|
||||
* \deprecated The functionality offer by this class is also provided by the
|
||||
* more general class Angle. It is recommended to use Angle for new
|
||||
* applications.
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT AuxAngle {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
real _y, _x;
|
||||
public:
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @param[in] y the \e y coordinate.
|
||||
* @param[in] x the \e x coordinate.
|
||||
*
|
||||
* \note the \e y coordinate is specified \e first.
|
||||
* \warning either \e x or \e y can be infinite, but not both.
|
||||
*
|
||||
* The defaults (\e x = 1 and \e y = 0) are such that
|
||||
* + no arguments gives an angle of 0;
|
||||
* + 1 argument specifies the tangent of the angle.
|
||||
**********************************************************************/
|
||||
explicit AuxAngle(real y = 0, real x = 1) : _y(y), _x(x) {}
|
||||
/**
|
||||
* @return the \e y component. This is the sine of the angle if the
|
||||
* AuxAngle has been normalized.
|
||||
**********************************************************************/
|
||||
Math::real y() const { return _y; }
|
||||
/**
|
||||
* @return the \e x component. This is the cosine of the angle if the
|
||||
* AuxAngle has been normalized.
|
||||
**********************************************************************/
|
||||
Math::real x() const { return _x; }
|
||||
/**
|
||||
* @return a reference to the \e y component. This allows this component
|
||||
* to be altered.
|
||||
**********************************************************************/
|
||||
Math::real& y() { return _y; }
|
||||
/**
|
||||
* @return a reference to the \e x component. This allows this component
|
||||
* to be altered.
|
||||
**********************************************************************/
|
||||
Math::real& x() { return _x; }
|
||||
/**
|
||||
* @return the AuxAngle converted to the conventional angle measured in
|
||||
* degrees.
|
||||
**********************************************************************/
|
||||
Math::real degrees() const;
|
||||
/**
|
||||
* @return the AuxAngle converted to the conventional angle measured in
|
||||
* radians.
|
||||
**********************************************************************/
|
||||
Math::real radians() const;
|
||||
/**
|
||||
* @return the lambertian of the AuxAngle.
|
||||
*
|
||||
* \note the lambertian of an angle χ is
|
||||
* lam(χ) = asinh(tan(χ)).
|
||||
**********************************************************************/
|
||||
Math::real lam() const;
|
||||
/**
|
||||
* @return the lambertian of the AuxAngle in degrees.
|
||||
*
|
||||
* \note the lambertian of an angle χ is
|
||||
* lam(χ) = asinh(tan(χ)).
|
||||
**********************************************************************/
|
||||
Math::real lamd() const;
|
||||
/**
|
||||
* @return the tangent of the angle.
|
||||
**********************************************************************/
|
||||
Math::real tan() const { return _y / _x; }
|
||||
/**
|
||||
* @return a new normalized AuxAngle with the point lying on the unit
|
||||
* circle and the \e y and \e x components are equal to the sine and
|
||||
* cosine of the angle.
|
||||
**********************************************************************/
|
||||
AuxAngle normalized() const;
|
||||
/**
|
||||
* Normalize the AuxAngle in place so that the \e y and \e x components are
|
||||
* equal to the sine and cosine of the angle.
|
||||
**********************************************************************/
|
||||
void normalize() { *this = normalized(); }
|
||||
/**
|
||||
* Set the quadrant for the AuxAngle.
|
||||
*
|
||||
* @param[in] p the AuxAngle from which the quadrant information is taken.
|
||||
* @return the new AuxAngle in the same quadrant as \e p.
|
||||
**********************************************************************/
|
||||
AuxAngle copyquadrant(const AuxAngle& p) const;
|
||||
/**
|
||||
* Add an AuxAngle.
|
||||
*
|
||||
* @param[in] p the AuxAngle to be added.
|
||||
* @return a reference to the new AuxAngle.
|
||||
*
|
||||
* The addition is done in place, altering the current AuxAngle.
|
||||
*
|
||||
* \warning Neither *this nor \e p should have an infinite component. If
|
||||
* necessary, invoke AuxAngle::normalize on these angles first.
|
||||
**********************************************************************/
|
||||
AuxAngle& operator+=(const AuxAngle& p);
|
||||
/**
|
||||
* Construct and return an AuxAngle specied as an angle in degrees.
|
||||
*
|
||||
* @param[in] d the angle measured in degrees.
|
||||
* @return the corresponding AuxAngle.
|
||||
*
|
||||
* This allows a new AuxAngle to be initialized as an angle in degrees with
|
||||
* @code
|
||||
* AuxAngle phi(AuxAngle::degrees(d));
|
||||
* @endcode
|
||||
**********************************************************************/
|
||||
static AuxAngle degrees(real d);
|
||||
/**
|
||||
* Construct and return an AuxAngle specied as an angle in radians.
|
||||
*
|
||||
* @param[in] r the angle measured in radians.
|
||||
* @return the corresponding AuxAngle.
|
||||
*
|
||||
* This allows a new AuxAngle to be initialized as an angle in radians with
|
||||
* @code
|
||||
* AuxAngle phi(AuxAngle::radians(r));
|
||||
* @endcode
|
||||
**********************************************************************/
|
||||
static AuxAngle radians(real r);
|
||||
/**
|
||||
* Construct and return an AuxAngle specied by the lambertian of the angle.
|
||||
*
|
||||
* @param[in] psi the lambertian of the angle.
|
||||
* @return the corresponding AuxAngle.
|
||||
*
|
||||
* This allows a new AuxAngle to be initialized given the lambertian with
|
||||
* @code
|
||||
* AuxAngle chi(AuxAngle::lam(psi));
|
||||
* @endcode
|
||||
*
|
||||
* \note this sets the angle χ to gd(ψ) = atan(sinh(ψ)).
|
||||
**********************************************************************/
|
||||
static AuxAngle lam(real psi);
|
||||
/**
|
||||
* Construct and return an AuxAngle specied by the lambertian of the angle
|
||||
* in degrees.
|
||||
*
|
||||
* @param[in] psid the lambertian of the angle in degrees.
|
||||
* @return the corresponding AuxAngle.
|
||||
*
|
||||
* This allows a new AuxAngle to be initialized given the lambertian with
|
||||
* @code
|
||||
* AuxAngle chi(AuxAngle::lamd(psid));
|
||||
* @endcode
|
||||
*
|
||||
* \note this sets the angle χ to gd(ψ) = atan(sinh(ψ)).
|
||||
**********************************************************************/
|
||||
static AuxAngle lamd(real psid);
|
||||
/**
|
||||
* @return a "NaN" AuxAngle.
|
||||
**********************************************************************/
|
||||
static AuxAngle NaN();
|
||||
};
|
||||
|
||||
inline AuxAngle AuxAngle::degrees(real d) {
|
||||
real y, x;
|
||||
Math::sincosd(d, y, x);
|
||||
return AuxAngle(y, x);
|
||||
}
|
||||
|
||||
inline AuxAngle AuxAngle::radians(real r) {
|
||||
using std::sin, std::cos;
|
||||
return AuxAngle(sin(r), cos(r));
|
||||
}
|
||||
|
||||
inline AuxAngle AuxAngle::lam(real psi) {
|
||||
using std::sinh;
|
||||
return AuxAngle(sinh(psi));
|
||||
}
|
||||
|
||||
inline AuxAngle AuxAngle::lamd(real psid) {
|
||||
using std::sinh;
|
||||
return AuxAngle(sinh(psid * Math::degree()));
|
||||
}
|
||||
|
||||
inline Math::real AuxAngle::degrees() const {
|
||||
return Math::atan2d(_y, _x);
|
||||
}
|
||||
|
||||
inline Math::real AuxAngle::radians() const {
|
||||
using std::atan2; return atan2(_y, _x);
|
||||
}
|
||||
|
||||
inline Math::real AuxAngle::lam() const {
|
||||
using std::asinh; return asinh( tan() );
|
||||
}
|
||||
|
||||
inline Math::real AuxAngle::lamd() const {
|
||||
using std::asinh; return asinh( tan() ) / Math::degree();
|
||||
}
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_AUXANGLE_HPP
|
||||
+406
@@ -0,0 +1,406 @@
|
||||
/**
|
||||
* \file AuxLatitude.hpp
|
||||
* \brief Header for the GeographicLib::AuxLatitude class
|
||||
*
|
||||
* This file is an implementation of the methods described in
|
||||
* - C. F. F. Karney,
|
||||
* <a href="https://doi.org/10.1080/00396265.2023.2217604">
|
||||
* On auxiliary latitudes,</a>
|
||||
* Survey Review 56(395), 165--180 (2024);
|
||||
* preprint
|
||||
* <a href="https://arxiv.org/abs/2212.05818">arXiv:2212.05818</a>.
|
||||
* .
|
||||
* Copyright (c) Charles Karney (2022-2023) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_AUXLATITUDE_HPP)
|
||||
#define GEOGRAPHICLIB_AUXLATITUDE_HPP 1
|
||||
|
||||
#include <utility>
|
||||
#include <GeographicLib/Math.hpp>
|
||||
#include <GeographicLib/AuxAngle.hpp>
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_AUXLATITUDE_ORDER)
|
||||
/**
|
||||
* The order of the series approximation used in AuxLatitude.
|
||||
* GEOGRAPHICLIB_AUXLATITUDE_ORDER can be set to one of [4, 6, 8]. Use order
|
||||
* appropriate for double precision, 6, also for GEOGRAPHICLIB_PRECISION == 5
|
||||
* to enable truncation errors to be measured easily.
|
||||
**********************************************************************/
|
||||
# define GEOGRAPHICLIB_AUXLATITUDE_ORDER \
|
||||
(GEOGRAPHICLIB_PRECISION == 2 || GEOGRAPHICLIB_PRECISION >= 5 ? 6 : \
|
||||
(GEOGRAPHICLIB_PRECISION == 1 ? 4 : 8))
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Conversions between auxiliary latitudes.
|
||||
*
|
||||
* This class is an implementation of the methods described in
|
||||
* - C. F. F. Karney,
|
||||
* <a href="https://doi.org/10.1080/00396265.2023.2217604">
|
||||
* On auxiliary latitudes,</a>
|
||||
* Survey Review 56(395), 165--180 (2024);
|
||||
* preprint
|
||||
* <a href="https://arxiv.org/abs/2212.05818">arXiv:2212.05818</a>.
|
||||
*
|
||||
* The provides accurate conversions between geographic (\e phi, φ),
|
||||
* parametric (\e beta, β), geocentric (\e theta, θ), rectifying
|
||||
* (\e mu, μ), conformal (\e chi, χ), and authalic (\e xi, ξ)
|
||||
* latitudes for an ellipsoid of revolution. A latitude is represented by
|
||||
* the class AuxAngle in order to maintain precision close to the poles.
|
||||
*
|
||||
* The class implements two methods for the conversion:
|
||||
* - Direct evaluation of the defining equations, the \e exact method. These
|
||||
* equations are formulated so as to preserve relative accuracy of the
|
||||
* tangent of the latitude, ensuring high accuracy near the equator and the
|
||||
* poles. Newton's method is used for those conversions that can't be
|
||||
* expressed in closed form.
|
||||
* - Expansions in powers of \e n, the third flattening, the \e series
|
||||
* method. This delivers full accuracy for abs(\e f) ≤ 1/150. Here, \e
|
||||
* f is the flattening of the ellipsoid.
|
||||
*
|
||||
* The series method is the preferred method of conversion for any conversion
|
||||
* involving μ, χ, or ξ, with abs(\e f) ≤ 1/150. The equations
|
||||
* for the conversions between φ, β, and θ are sufficiently
|
||||
* simple that the exact method should be used for such conversions and also
|
||||
* for conversions with with abs(\e f) > 1/150.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-AuxLatitude.cpp
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT AuxLatitude {
|
||||
typedef Math::real real;
|
||||
AuxLatitude(const std::pair<real, real>& axes);
|
||||
public:
|
||||
/**
|
||||
* The floating-point type for real numbers. This just connects to the
|
||||
* template parameters for the class.
|
||||
**********************************************************************/
|
||||
/**
|
||||
* The different auxiliary latitudes.
|
||||
**********************************************************************/
|
||||
enum aux {
|
||||
/**
|
||||
* Geographic latitude, \e phi, φ
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
GEOGRAPHIC = 0,
|
||||
/**
|
||||
* Parametric latitude, \e beta, β
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
PARAMETRIC = 1,
|
||||
/**
|
||||
* %Geocentric latitude, \e theta, θ
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
GEOCENTRIC = 2,
|
||||
/**
|
||||
* Rectifying latitude, \e mu, μ
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
RECTIFYING = 3,
|
||||
/**
|
||||
* Conformal latitude, \e chi, χ
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
CONFORMAL = 4,
|
||||
/**
|
||||
* Authalic latitude, \e xi, ξ
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
AUTHALIC = 5,
|
||||
/**
|
||||
* The total number of auxiliary latitudes
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
AUXNUMBER = 6,
|
||||
/**
|
||||
* An alias for GEOGRAPHIC
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
PHI = GEOGRAPHIC,
|
||||
/**
|
||||
* An alias for PARAMETRIC
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
BETA = PARAMETRIC,
|
||||
/**
|
||||
* An alias for GEOCENTRIC
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
THETA = GEOCENTRIC,
|
||||
/**
|
||||
* An alias for RECTIFYING
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
MU = RECTIFYING,
|
||||
/**
|
||||
* An alias for CONFORMAL
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
CHI = CONFORMAL,
|
||||
/**
|
||||
* An alias for AUTHALIC
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
XI = AUTHALIC,
|
||||
/**
|
||||
* An alias for GEOGRAPHIC
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
COMMON = GEOGRAPHIC,
|
||||
/**
|
||||
* An alias for GEOGRAPHIC
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
GEODETIC = GEOGRAPHIC,
|
||||
/**
|
||||
* An alias for PARAMETRIC
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
REDUCED = PARAMETRIC,
|
||||
};
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param[in] a equatorial radius.
|
||||
* @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
|
||||
* Negative \e f gives a prolate ellipsoid.
|
||||
* @exception GeographicErr if \e a or (1 − \e f) \e a is not
|
||||
* positive.
|
||||
*
|
||||
* \note the constructor does not precompute the coefficients for the
|
||||
* Fourier series for the series conversions. These are computed and saved
|
||||
* when first needed.
|
||||
**********************************************************************/
|
||||
AuxLatitude(real a, real f);
|
||||
/**
|
||||
* Construct and return an AuxLatitude object specified in terms of the
|
||||
* semiaxes
|
||||
*
|
||||
* @param[in] a equatorial radius.
|
||||
* @param[in] b polar semiaxis.
|
||||
* @exception GeographicErr if \e a or \e b is not positive.
|
||||
*
|
||||
* This allows a new AuxAngle to be initialized as an angle in radians with
|
||||
* @code
|
||||
* AuxLatitude aux(AuxLatitude::axes(a, b));
|
||||
* @endcode
|
||||
**********************************************************************/
|
||||
static AuxLatitude axes(real a, real b) {
|
||||
return AuxLatitude(std::pair<real, real>(a, b));
|
||||
}
|
||||
/**
|
||||
* Convert between any two auxiliary latitudes specified as AuxAngle.
|
||||
*
|
||||
* @param[in] auxin an AuxLatitude::aux indicating the type of
|
||||
* auxiliary latitude \e zeta.
|
||||
* @param[in] auxout an AuxLatitude::aux indicating the type of
|
||||
* auxiliary latitude \e eta.
|
||||
* @param[in] zeta the input auxiliary latitude as an AuxAngle
|
||||
* @param[in] exact if true use the exact equations instead of the Taylor
|
||||
* series [default false].
|
||||
* @return the output auxiliary latitude \e eta as an AuxAngle.
|
||||
*
|
||||
* With \e exact = false, the Fourier coefficients for a specific \e auxin
|
||||
* and \e auxout are computed and saved on the first call; the saved
|
||||
* coefficients are used on subsequent calls. The series method is
|
||||
* accurate for abs(\e f) ≤ 1/150; for other \e f, the exact method
|
||||
* should be used.
|
||||
**********************************************************************/
|
||||
AuxAngle Convert(int auxin, int auxout, const AuxAngle& zeta,
|
||||
bool exact = false) const;
|
||||
/**
|
||||
* Convert between any two auxiliary latitudes specified in degrees.
|
||||
*
|
||||
* @param[in] auxin an AuxLatitude::aux indicating the type of
|
||||
* auxiliary latitude \e zeta.
|
||||
* @param[in] auxout an AuxLatitude::aux indicating the type of
|
||||
* auxiliary latitude \e eta.
|
||||
* @param[in] zeta the input auxiliary latitude in degrees.
|
||||
* @param[in] exact if true use the exact equations instead of the Taylor
|
||||
* series [default false].
|
||||
* @return the output auxiliary latitude \e eta in degrees.
|
||||
*
|
||||
* With \e exact = false, the Fourier coefficients for a specific \e auxin
|
||||
* and \e auxout are computed and saved on the first call; the saved
|
||||
* coefficients are used on subsequent calls. The series method is
|
||||
* accurate for abs(\e f) ≤ 1/150; for other \e f, the exact method
|
||||
* should be used.
|
||||
**********************************************************************/
|
||||
Math::real Convert(int auxin, int auxout, real zeta, bool exact = false)
|
||||
const;
|
||||
/**
|
||||
* Convert geographic latitude to an auxiliary latitude \e eta.
|
||||
*
|
||||
* @param[in] auxout an AuxLatitude::aux indicating the auxiliary
|
||||
* latitude returned.
|
||||
* @param[in] phi the geographic latitude.
|
||||
* @param[out] diff optional pointer to the derivative d tan(\e eta) / d
|
||||
* tan(\e phi).
|
||||
* @return the auxiliary latitude \e eta.
|
||||
*
|
||||
* This uses the exact equations.
|
||||
**********************************************************************/
|
||||
AuxAngle ToAuxiliary(int auxout, const AuxAngle& phi, real* diff = nullptr)
|
||||
const;
|
||||
/**
|
||||
* Convert an auxiliary latitude \e zeta to geographic latitude.
|
||||
*
|
||||
* @param[in] auxin an AuxLatitude::aux indicating the type of
|
||||
* auxiliary latitude \e zeta.
|
||||
* @param[in] zeta the input auxiliary latitude.
|
||||
* @param[out] niter optional pointer to the number of iterations.
|
||||
* @return the geographic latitude \e phi.
|
||||
*
|
||||
* This uses the exact equations.
|
||||
**********************************************************************/
|
||||
AuxAngle FromAuxiliary(int auxin, const AuxAngle& zeta,
|
||||
int* niter = nullptr) const;
|
||||
/**
|
||||
* Return the rectifying radius.
|
||||
*
|
||||
* @param[in] exact if true use the exact expression instead of the Taylor
|
||||
* series [default false].
|
||||
* @return the rectifying radius in the same units as \e a.
|
||||
**********************************************************************/
|
||||
Math::real RectifyingRadius(bool exact = false) const;
|
||||
/**
|
||||
* Return the authalic radius squared.
|
||||
*
|
||||
* @param[in] exact if true use the exact expression instead of the Taylor
|
||||
* series [default false].
|
||||
* @return the authalic radius squared in the same units as \e a.
|
||||
**********************************************************************/
|
||||
Math::real AuthalicRadiusSquared(bool exact = false) const;
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters).
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _a; }
|
||||
/**
|
||||
* @return \e b the polar semiaxis of the ellipsoid (meters).
|
||||
**********************************************************************/
|
||||
Math::real PolarSemiAxis() const { return _b; }
|
||||
/**
|
||||
* @return \e f, the flattening of the ellipsoid.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _f; }
|
||||
/**
|
||||
* Use Clenshaw to sum a Fouier series.
|
||||
*
|
||||
* @param[in] sinp if true sum the sine series, else sum the cosine series.
|
||||
* @param[in] szeta sin(\e zeta).
|
||||
* @param[in] czeta cos(\e zeta).
|
||||
* @param[in] c the array of coefficients.
|
||||
* @param[in] K the number of coefficients.
|
||||
* @return the Clenshaw sum.
|
||||
*
|
||||
* The result returned is \f$ \sum_0^{K-1} c_k \sin (2k+2)\zeta \f$, if \e
|
||||
* sinp is true; with \e sinp false, replace sin by cos.
|
||||
**********************************************************************/
|
||||
// Clenshaw applied to sum(c[k] * sin( (2*k+2) * zeta), i, 0, K-1);
|
||||
// if !sinp then subst sine->cosine.
|
||||
static Math::real Clenshaw(bool sinp, real szeta, real czeta,
|
||||
const real c[], int K);
|
||||
/**
|
||||
* The order of the series expansions. This is set at compile time to
|
||||
* either 4, 6, or 8, by the preprocessor macro
|
||||
* GEOGRAPHICLIB_AUXLATITUDE_ORDER.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
static const int Lmax = GEOGRAPHICLIB_AUXLATITUDE_ORDER;
|
||||
/**
|
||||
* A global instantiation of Ellipsoid with the parameters for the WGS84
|
||||
* ellipsoid.
|
||||
**********************************************************************/
|
||||
static const AuxLatitude& WGS84();
|
||||
private:
|
||||
// Maximum number of iterations for Newton's method
|
||||
static const int numit_ = 1000;
|
||||
real tol_, bmin_, bmax_; // Static consts for Newton's method
|
||||
// the function atanh(e * sphi)/e + sphi / (1 - (e * sphi)^2);
|
||||
protected:
|
||||
/**
|
||||
* Convert geographic latitude to parametric latitude
|
||||
*
|
||||
* @param[in] phi geographic latitude.
|
||||
* @param[out] diff optional pointer to the derivative d tan(\e beta) / d
|
||||
* tan(\e phi).
|
||||
* @return \e beta, the parametric latitude
|
||||
**********************************************************************/
|
||||
AuxAngle Parametric(const AuxAngle& phi, real* diff = nullptr) const;
|
||||
/**
|
||||
* Convert geographic latitude to geocentric latitude
|
||||
*
|
||||
* @param[in] phi geographic latitude.
|
||||
* @param[out] diff optional pointer to the derivative d tan(\e theta) / d
|
||||
* tan(\e phi).
|
||||
* @return \e theta, the geocentric latitude.
|
||||
**********************************************************************/
|
||||
AuxAngle Geocentric(const AuxAngle& phi, real* diff = nullptr) const;
|
||||
/**
|
||||
* Convert geographic latitude to rectifying latitude
|
||||
*
|
||||
* @param[in] phi geographic latitude.
|
||||
* @param[out] diff optional pointer to the derivative d tan(\e mu) / d
|
||||
* tan(\e phi).
|
||||
* @return \e mu, the rectifying latitude.
|
||||
**********************************************************************/
|
||||
AuxAngle Rectifying(const AuxAngle& phi, real* diff = nullptr) const;
|
||||
/**
|
||||
* Convert geographic latitude to conformal latitude
|
||||
*
|
||||
* @param[in] phi geographic latitude.
|
||||
* @param[out] diff optional pointer to the derivative d tan(\e chi) / d
|
||||
* tan(\e phi).
|
||||
* @return \e chi, the conformal latitude.
|
||||
**********************************************************************/
|
||||
AuxAngle Conformal(const AuxAngle& phi, real* diff = nullptr) const;
|
||||
/**
|
||||
* Convert geographic latitude to authalic latitude
|
||||
*
|
||||
* @param[in] phi geographic latitude.
|
||||
* @param[out] diff optional pointer to the derivative d tan(\e xi) / d
|
||||
* tan(\e phi).
|
||||
* @return \e xi, the authalic latitude.
|
||||
**********************************************************************/
|
||||
AuxAngle Authalic(const AuxAngle& phi, real* diff = nullptr) const;
|
||||
/// \cond SKIP
|
||||
// Ellipsoid parameters
|
||||
real _a, _b, _f, _fm1, _e2, _e2m1, _e12, _e12p1, _n, _e, _e1, _n2, _q;
|
||||
// To hold computed Fourier coefficients
|
||||
mutable real _c[Lmax * AUXNUMBER * AUXNUMBER];
|
||||
// 1d index into AUXNUMBER x AUXNUMBER data
|
||||
static int ind(int auxout, int auxin) {
|
||||
return (auxout >= 0 && auxout < AUXNUMBER &&
|
||||
auxin >= 0 && auxin < AUXNUMBER) ?
|
||||
AUXNUMBER * auxout + auxin : -1;
|
||||
}
|
||||
// the function sqrt(1 + tphi^2), convert tan to sec
|
||||
static real sc(real tphi)
|
||||
{ using std::hypot; return hypot(real(1), tphi); }
|
||||
// the function tphi / sqrt(1 + tphi^2), convert tan to sin
|
||||
static real sn(real tphi) {
|
||||
using std::isinf, std::copysign;
|
||||
return isinf(tphi) ? copysign(real(1), tphi) : tphi / sc(tphi);
|
||||
}
|
||||
// Populate [_c[Lmax * k], _c[Lmax * (k + 1)])
|
||||
void fillcoeff(int auxin, int auxout, int k) const;
|
||||
// the function atanh(e * sphi)/e; works for e^2 = 0 and e^2 < 0
|
||||
real atanhee(real tphi) const;
|
||||
/// \endcond
|
||||
private:
|
||||
// the function atanh(e * sphi)/e + sphi / (1 - (e * sphi)^2);
|
||||
real q(real tphi) const;
|
||||
// The divided difference of (q(1) - q(sphi)) / (1 - sphi)
|
||||
real Dq(real tphi) const;
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_AUXLATITUDE_HPP
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* \file AzimuthalEquidistant.hpp
|
||||
* \brief Header for GeographicLib::AzimuthalEquidistant class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2009-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_AZIMUTHALEQUIDISTANT_HPP)
|
||||
#define GEOGRAPHICLIB_AZIMUTHALEQUIDISTANT_HPP 1
|
||||
|
||||
#include <GeographicLib/Geodesic.hpp>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Azimuthal equidistant projection
|
||||
*
|
||||
* Azimuthal equidistant projection centered at an arbitrary position on the
|
||||
* ellipsoid. For a point in projected space (\e x, \e y), the geodesic
|
||||
* distance from the center position is hypot(\e x, \e y) and the azimuth of
|
||||
* the geodesic from the center point is atan2(\e x, \e y). The Forward and
|
||||
* Reverse methods also return the azimuth \e azi of the geodesic at (\e x,
|
||||
* \e y) and reciprocal scale \e rk in the azimuthal direction which,
|
||||
* together with the basic properties of the projection, serve to specify
|
||||
* completely the local affine transformation between geographic and
|
||||
* projected coordinates.
|
||||
*
|
||||
* The conversions all take place using a Geodesic object (by default
|
||||
* Geodesic::WGS84()). For more information on geodesics see \ref geodesic.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-AzimuthalEquidistant.cpp
|
||||
*
|
||||
* <a href="GeodesicProj.1.html">GeodesicProj</a> is a command-line utility
|
||||
* providing access to the functionality of AzimuthalEquidistant, Gnomonic,
|
||||
* and CassiniSoldner.
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT AzimuthalEquidistant {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
real eps_;
|
||||
Geodesic _earth;
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for AzimuthalEquidistant.
|
||||
*
|
||||
* @param[in] earth the Geodesic object to use for geodesic calculations.
|
||||
* By default this uses the WGS84 ellipsoid.
|
||||
**********************************************************************/
|
||||
explicit AzimuthalEquidistant(const Geodesic& earth = Geodesic::WGS84());
|
||||
|
||||
/**
|
||||
* Forward projection, from geographic to azimuthal equidistant.
|
||||
*
|
||||
* @param[in] lat0 latitude of center point of projection (degrees).
|
||||
* @param[in] lon0 longitude of center point of projection (degrees).
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[out] x easting of point (meters).
|
||||
* @param[out] y northing of point (meters).
|
||||
* @param[out] azi azimuth of geodesic at point (degrees).
|
||||
* @param[out] rk reciprocal of azimuthal scale at point.
|
||||
*
|
||||
* \e lat0 and \e lat should be in the range [−90°, 90°].
|
||||
* The scale of the projection is 1 in the "radial" direction, \e azi
|
||||
* clockwise from true north, and is 1/\e rk in the direction perpendicular
|
||||
* to this. A call to Forward followed by a call to Reverse will return
|
||||
* the original (\e lat, \e lon) (to within roundoff).
|
||||
**********************************************************************/
|
||||
void Forward(real lat0, real lon0, real lat, real lon,
|
||||
real& x, real& y, real& azi, real& rk) const;
|
||||
|
||||
/**
|
||||
* Reverse projection, from azimuthal equidistant to geographic.
|
||||
*
|
||||
* @param[in] lat0 latitude of center point of projection (degrees).
|
||||
* @param[in] lon0 longitude of center point of projection (degrees).
|
||||
* @param[in] x easting of point (meters).
|
||||
* @param[in] y northing of point (meters).
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] azi azimuth of geodesic at point (degrees).
|
||||
* @param[out] rk reciprocal of azimuthal scale at point.
|
||||
*
|
||||
* \e lat0 should be in the range [−90°, 90°]. \e lat will
|
||||
* be in the range [−90°, 90°] and \e lon will be in the
|
||||
* range [−180°, 180°]. The scale of the projection is 1 in
|
||||
* the "radial" direction, \e azi clockwise from true north, and is 1/\e rk
|
||||
* in the direction perpendicular to this. A call to Reverse followed by a
|
||||
* call to Forward will return the original (\e x, \e y) (to roundoff) only
|
||||
* if the geodesic to (\e x, \e y) is a shortest path.
|
||||
**********************************************************************/
|
||||
void Reverse(real lat0, real lon0, real x, real y,
|
||||
real& lat, real& lon, real& azi, real& rk) const;
|
||||
|
||||
/**
|
||||
* AzimuthalEquidistant::Forward without returning the azimuth and scale.
|
||||
**********************************************************************/
|
||||
void Forward(real lat0, real lon0, real lat, real lon,
|
||||
real& x, real& y) const {
|
||||
real azi, rk;
|
||||
Forward(lat0, lon0, lat, lon, x, y, azi, rk);
|
||||
}
|
||||
|
||||
/**
|
||||
* AzimuthalEquidistant::Reverse without returning the azimuth and scale.
|
||||
**********************************************************************/
|
||||
void Reverse(real lat0, real lon0, real x, real y,
|
||||
real& lat, real& lon) const {
|
||||
real azi, rk;
|
||||
Reverse(lat0, lon0, x, y, lat, lon, azi, rk);
|
||||
}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value inherited from the Geodesic object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the value
|
||||
* inherited from the Geodesic object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _earth.Flattening(); }
|
||||
///@}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_AZIMUTHALEQUIDISTANT_HPP
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* \file CassiniSoldner.hpp
|
||||
* \brief Header for GeographicLib::CassiniSoldner class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2009-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_CASSINISOLDNER_HPP)
|
||||
#define GEOGRAPHICLIB_CASSINISOLDNER_HPP 1
|
||||
|
||||
#include <GeographicLib/Geodesic.hpp>
|
||||
#include <GeographicLib/GeodesicLine.hpp>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Cassini-Soldner projection
|
||||
*
|
||||
* Cassini-Soldner projection centered at an arbitrary position, \e lat0, \e
|
||||
* lon0, on the ellipsoid. This projection is a transverse cylindrical
|
||||
* equidistant projection. The projection from (\e lat, \e lon) to easting
|
||||
* and northing (\e x, \e y) is defined by geodesics as follows. Go north
|
||||
* along a geodesic a distance \e y from the central point; then turn
|
||||
* clockwise 90° and go a distance \e x along a geodesic.
|
||||
* (Although the initial heading is north, this changes to south if the pole
|
||||
* is crossed.) This procedure uniquely defines the reverse projection. The
|
||||
* forward projection is constructed as follows. Find the point (\e lat1, \e
|
||||
* lon1) on the meridian closest to (\e lat, \e lon). Here we consider the
|
||||
* full meridian so that \e lon1 may be either \e lon0 or \e lon0 +
|
||||
* 180°. \e x is the geodesic distance from (\e lat1, \e lon1) to
|
||||
* (\e lat, \e lon), appropriately signed according to which side of the
|
||||
* central meridian (\e lat, \e lon) lies. \e y is the shortest distance
|
||||
* along the meridian from (\e lat0, \e lon0) to (\e lat1, \e lon1), again,
|
||||
* appropriately signed according to the initial heading. [Note that, in the
|
||||
* case of prolate ellipsoids, the shortest meridional path from (\e lat0, \e
|
||||
* lon0) to (\e lat1, \e lon1) may not be the shortest path.] This procedure
|
||||
* uniquely defines the forward projection except for a small class of points
|
||||
* for which there may be two equally short routes for either leg of the
|
||||
* path.
|
||||
*
|
||||
* Because of the properties of geodesics, the (\e x, \e y) grid is
|
||||
* orthogonal. The scale in the easting direction is unity. The scale, \e
|
||||
* k, in the northing direction is unity on the central meridian and
|
||||
* increases away from the central meridian. The projection routines return
|
||||
* \e azi, the true bearing of the easting direction, and \e rk = 1/\e k, the
|
||||
* reciprocal of the scale in the northing direction.
|
||||
*
|
||||
* The conversions all take place using a Geodesic object (by default
|
||||
* Geodesic::WGS84()). For more information on geodesics see \ref geodesic.
|
||||
* The determination of (\e lat1, \e lon1) in the forward projection is by
|
||||
* solving the inverse geodesic problem for (\e lat, \e lon) and its twin
|
||||
* obtained by reflection in the meridional plane. The scale is found by
|
||||
* determining where two neighboring geodesics intersecting the central
|
||||
* meridian at \e lat1 and \e lat1 + \e dlat1 intersect and taking the ratio
|
||||
* of the reduced lengths for the two geodesics between that point and,
|
||||
* respectively, (\e lat1, \e lon1) and (\e lat, \e lon).
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-CassiniSoldner.cpp
|
||||
*
|
||||
* <a href="GeodesicProj.1.html">GeodesicProj</a> is a command-line utility
|
||||
* providing access to the functionality of AzimuthalEquidistant, Gnomonic,
|
||||
* and CassiniSoldner.
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT CassiniSoldner {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
Geodesic _earth;
|
||||
GeodesicLine _meridian;
|
||||
real _sbet0, _cbet0;
|
||||
static const unsigned maxit_ = 10;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for CassiniSoldner.
|
||||
*
|
||||
* @param[in] earth the Geodesic object to use for geodesic calculations.
|
||||
* By default this uses the WGS84 ellipsoid.
|
||||
*
|
||||
* This constructor makes an "uninitialized" object. Call Reset to set the
|
||||
* central latitude and longitude, prior to calling Forward and Reverse.
|
||||
**********************************************************************/
|
||||
explicit CassiniSoldner(const Geodesic& earth = Geodesic::WGS84());
|
||||
|
||||
/**
|
||||
* Constructor for CassiniSoldner specifying a center point.
|
||||
*
|
||||
* @param[in] lat0 latitude of center point of projection (degrees).
|
||||
* @param[in] lon0 longitude of center point of projection (degrees).
|
||||
* @param[in] earth the Geodesic object to use for geodesic calculations.
|
||||
* By default this uses the WGS84 ellipsoid.
|
||||
*
|
||||
* \e lat0 should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
CassiniSoldner(real lat0, real lon0,
|
||||
const Geodesic& earth = Geodesic::WGS84());
|
||||
|
||||
/**
|
||||
* Set the central point of the projection
|
||||
*
|
||||
* @param[in] lat0 latitude of center point of projection (degrees).
|
||||
* @param[in] lon0 longitude of center point of projection (degrees).
|
||||
*
|
||||
* \e lat0 should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
void Reset(real lat0, real lon0);
|
||||
|
||||
/**
|
||||
* Forward projection, from geographic to Cassini-Soldner.
|
||||
*
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[out] x easting of point (meters).
|
||||
* @param[out] y northing of point (meters).
|
||||
* @param[out] azi azimuth of easting direction at point (degrees).
|
||||
* @param[out] rk reciprocal of azimuthal northing scale at point.
|
||||
*
|
||||
* \e lat should be in the range [−90°, 90°]. A call to
|
||||
* Forward followed by a call to Reverse will return the original (\e lat,
|
||||
* \e lon) (to within roundoff). The routine does nothing if the origin
|
||||
* has not been set.
|
||||
**********************************************************************/
|
||||
void Forward(real lat, real lon,
|
||||
real& x, real& y, real& azi, real& rk) const;
|
||||
|
||||
/**
|
||||
* Reverse projection, from Cassini-Soldner to geographic.
|
||||
*
|
||||
* @param[in] x easting of point (meters).
|
||||
* @param[in] y northing of point (meters).
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] azi azimuth of easting direction at point (degrees).
|
||||
* @param[out] rk reciprocal of azimuthal northing scale at point.
|
||||
*
|
||||
* A call to Reverse followed by a call to Forward will return the original
|
||||
* (\e x, \e y) (to within roundoff), provided that \e x and \e y are
|
||||
* sufficiently small not to "wrap around" the earth. The routine does
|
||||
* nothing if the origin has not been set.
|
||||
**********************************************************************/
|
||||
void Reverse(real x, real y,
|
||||
real& lat, real& lon, real& azi, real& rk) const;
|
||||
|
||||
/**
|
||||
* CassiniSoldner::Forward without returning the azimuth and scale.
|
||||
**********************************************************************/
|
||||
void Forward(real lat, real lon,
|
||||
real& x, real& y) const {
|
||||
real azi, rk;
|
||||
Forward(lat, lon, x, y, azi, rk);
|
||||
}
|
||||
|
||||
/**
|
||||
* CassiniSoldner::Reverse without returning the azimuth and scale.
|
||||
**********************************************************************/
|
||||
void Reverse(real x, real y,
|
||||
real& lat, real& lon) const {
|
||||
real azi, rk;
|
||||
Reverse(x, y, lat, lon, azi, rk);
|
||||
}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return true if the object has been initialized.
|
||||
**********************************************************************/
|
||||
bool Init() const { return _meridian.Init(); }
|
||||
|
||||
/**
|
||||
* @return \e lat0 the latitude of origin (degrees).
|
||||
**********************************************************************/
|
||||
Math::real LatitudeOrigin() const
|
||||
{ return _meridian.Latitude(); }
|
||||
|
||||
/**
|
||||
* @return \e lon0 the longitude of origin (degrees).
|
||||
**********************************************************************/
|
||||
Math::real LongitudeOrigin() const
|
||||
{ return _meridian.Longitude(); }
|
||||
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value inherited from the Geodesic object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the value
|
||||
* inherited from the Geodesic object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _earth.Flattening(); }
|
||||
///@}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_CASSINISOLDNER_HPP
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* \file CircularEngine.hpp
|
||||
* \brief Header for GeographicLib::CircularEngine class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2011-2015) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_CIRCULARENGINE_HPP)
|
||||
#define GEOGRAPHICLIB_CIRCULARENGINE_HPP 1
|
||||
|
||||
#include <vector>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/SphericalEngine.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Squelch warnings about dll vs vector
|
||||
# pragma warning (push)
|
||||
# pragma warning (disable: 4251)
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Spherical harmonic sums for a circle
|
||||
*
|
||||
* The class is a companion to SphericalEngine. If the results of a
|
||||
* spherical harmonic sum are needed for several points on a circle of
|
||||
* constant latitude \e lat and height \e h, then SphericalEngine::Circle can
|
||||
* compute the inner sum, which is independent of longitude \e lon, and
|
||||
* produce a CircularEngine object. CircularEngine::operator()() can
|
||||
* then be used to perform the outer sum for particular vales of \e lon.
|
||||
* This can lead to substantial improvements in computational speed for high
|
||||
* degree sum (approximately by a factor of \e N / 2 where \e N is the
|
||||
* maximum degree).
|
||||
*
|
||||
* CircularEngine is tightly linked to the internals of SphericalEngine. For
|
||||
* that reason, the constructor for this class is private. Use
|
||||
* SphericalHarmonic::Circle, SphericalHarmonic1::Circle, and
|
||||
* SphericalHarmonic2::Circle to create instances of this class.
|
||||
*
|
||||
* CircularEngine stores the coefficients needed to allow the summation over
|
||||
* order to be performed in 2 or 6 vectors of length \e M + 1 (depending on
|
||||
* whether gradients are to be calculated). For this reason the constructor
|
||||
* may throw a std::bad_alloc exception.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-CircularEngine.cpp
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT CircularEngine {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
enum normalization {
|
||||
FULL = SphericalEngine::FULL,
|
||||
SCHMIDT = SphericalEngine::SCHMIDT,
|
||||
};
|
||||
int _mM;
|
||||
bool _gradp;
|
||||
unsigned _norm;
|
||||
real _a, _r, _u, _t;
|
||||
std::vector<real> _wc, _ws, _wrc, _wrs, _wtc, _wts;
|
||||
real _q, _uq, _uq2;
|
||||
|
||||
Math::real Value(bool gradp, real sl, real cl,
|
||||
real& gradx, real& grady, real& gradz) const;
|
||||
|
||||
friend class SphericalEngine;
|
||||
CircularEngine(int M, bool gradp, unsigned norm,
|
||||
real a, real r, real u, real t)
|
||||
: _mM(M)
|
||||
, _gradp(gradp)
|
||||
, _norm(norm)
|
||||
, _a(a)
|
||||
, _r(r)
|
||||
, _u(u)
|
||||
, _t(t)
|
||||
, _wc(std::vector<real>(_mM + 1, 0))
|
||||
, _ws(std::vector<real>(_mM + 1, 0))
|
||||
, _wrc(std::vector<real>(_gradp ? _mM + 1 : 0, 0))
|
||||
, _wrs(std::vector<real>(_gradp ? _mM + 1 : 0, 0))
|
||||
, _wtc(std::vector<real>(_gradp ? _mM + 1 : 0, 0))
|
||||
, _wts(std::vector<real>(_gradp ? _mM + 1 : 0, 0))
|
||||
{
|
||||
_q = _a / _r;
|
||||
_uq = _u * _q;
|
||||
_uq2 = Math::sq(_uq);
|
||||
}
|
||||
|
||||
void SetCoeff(int m, real wc, real ws)
|
||||
{ _wc[m] = wc; _ws[m] = ws; }
|
||||
|
||||
void SetCoeff(int m, real wc, real ws,
|
||||
real wrc, real wrs, real wtc, real wts) {
|
||||
_wc[m] = wc; _ws[m] = ws;
|
||||
if (_gradp) {
|
||||
_wrc[m] = wrc; _wrs[m] = wrs;
|
||||
_wtc[m] = wtc; _wts[m] = wts;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* A default constructor. CircularEngine::operator()() on the resulting
|
||||
* object returns zero. The resulting object can be assigned to the result
|
||||
* of SphericalHarmonic::Circle.
|
||||
**********************************************************************/
|
||||
CircularEngine()
|
||||
: _mM(-1)
|
||||
, _gradp(true)
|
||||
, _u(0)
|
||||
, _t(1)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Evaluate the sum for a particular longitude given in terms of its
|
||||
* sine and cosine.
|
||||
*
|
||||
* @param[in] sinlon the sine of the longitude.
|
||||
* @param[in] coslon the cosine of the longitude.
|
||||
* @return \e V the value of the sum.
|
||||
*
|
||||
* The arguments must satisfy <i>sinlon</i><sup>2</sup> +
|
||||
* <i>coslon</i><sup>2</sup> = 1.
|
||||
**********************************************************************/
|
||||
Math::real operator()(real sinlon, real coslon) const {
|
||||
real dummy;
|
||||
return Value(false, sinlon, coslon, dummy, dummy, dummy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the sum for a particular longitude.
|
||||
*
|
||||
* @param[in] lon the longitude (degrees).
|
||||
* @return \e V the value of the sum.
|
||||
**********************************************************************/
|
||||
Math::real operator()(real lon) const {
|
||||
real sinlon, coslon;
|
||||
Math::sincosd(lon, sinlon, coslon);
|
||||
return (*this)(sinlon, coslon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the sum and its gradient for a particular longitude given in
|
||||
* terms of its sine and cosine.
|
||||
*
|
||||
* @param[in] sinlon the sine of the longitude.
|
||||
* @param[in] coslon the cosine of the longitude.
|
||||
* @param[out] gradx \e x component of the gradient.
|
||||
* @param[out] grady \e y component of the gradient.
|
||||
* @param[out] gradz \e z component of the gradient.
|
||||
* @return \e V the value of the sum.
|
||||
*
|
||||
* The gradients will only be computed if the CircularEngine object was
|
||||
* created with this capability (e.g., via \e gradp = true in
|
||||
* SphericalHarmonic::Circle). If not, \e gradx, etc., will not be
|
||||
* touched. The arguments must satisfy <i>sinlon</i><sup>2</sup> +
|
||||
* <i>coslon</i><sup>2</sup> = 1.
|
||||
**********************************************************************/
|
||||
Math::real operator()(real sinlon, real coslon,
|
||||
real& gradx, real& grady, real& gradz) const {
|
||||
return Value(true, sinlon, coslon, gradx, grady, gradz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the sum and its gradient for a particular longitude.
|
||||
*
|
||||
* @param[in] lon the longitude (degrees).
|
||||
* @param[out] gradx \e x component of the gradient.
|
||||
* @param[out] grady \e y component of the gradient.
|
||||
* @param[out] gradz \e z component of the gradient.
|
||||
* @return \e V the value of the sum.
|
||||
*
|
||||
* The gradients will only be computed if the CircularEngine object was
|
||||
* created with this capability (e.g., via \e gradp = true in
|
||||
* SphericalHarmonic::Circle). If not, \e gradx, etc., will not be
|
||||
* touched.
|
||||
**********************************************************************/
|
||||
Math::real operator()(real lon,
|
||||
real& gradx, real& grady, real& gradz) const {
|
||||
real sinlon, coslon;
|
||||
Math::sincosd(lon, sinlon, coslon);
|
||||
return (*this)(sinlon, coslon, gradx, grady, gradz);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#endif // GEOGRAPHICLIB_CIRCULARENGINE_HPP
|
||||
@@ -0,0 +1,25 @@
|
||||
#define GEOGRAPHICLIB_VERSION_STRING "2.7"
|
||||
#define GEOGRAPHICLIB_VERSION_MAJOR 2
|
||||
#define GEOGRAPHICLIB_VERSION_MINOR 7
|
||||
#define GEOGRAPHICLIB_VERSION_PATCH 0
|
||||
#define GEOGRAPHICLIB_DATA "/usr/local/share/GeographicLib"
|
||||
|
||||
// These are macros which affect the building of the library
|
||||
#define GEOGRAPHICLIB_HAVE_LONG_DOUBLE 1
|
||||
#define GEOGRAPHICLIB_WORDS_BIGENDIAN 0
|
||||
#define GEOGRAPHICLIB_PRECISION 2
|
||||
|
||||
// Specify whether GeographicLib is a shared or static library. When compiling
|
||||
// under Visual Studio it is necessary to specify whether GeographicLib is a
|
||||
// shared library. This is done with the macro GEOGRAPHICLIB_SHARED_LIB, which
|
||||
// cmake will correctly define as 0 or 1 when only one type of library is in
|
||||
// the package. If both shared and static libraries are available,
|
||||
// GEOGRAPHICLIB_SHARED_LIB is set to 2 which triggers a preprocessor error in
|
||||
// Constants.hpp. In this case, the appropriate value (0 or 1) for
|
||||
// GEOGRAPHICLIB_SHARED_LIB must be specified when compiling any program that
|
||||
// includes GeographicLib headers. This is done automatically if GeographicLib
|
||||
// and the user's code were built with cmake version 2.8.11 (which introduced
|
||||
// the command target_compile_definitions) or later.
|
||||
#if !defined(GEOGRAPHICLIB_SHARED_LIB)
|
||||
#define GEOGRAPHICLIB_SHARED_LIB 1
|
||||
#endif
|
||||
+358
@@ -0,0 +1,358 @@
|
||||
/**
|
||||
* \file Constants.hpp
|
||||
* \brief Header for GeographicLib::Constants class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2008-2024) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_CONSTANTS_HPP)
|
||||
#define GEOGRAPHICLIB_CONSTANTS_HPP 1
|
||||
|
||||
#include <GeographicLib/Config.h>
|
||||
|
||||
/**
|
||||
* @relates GeographicLib::Constants
|
||||
* Pack the version components into a single integer. Users should not rely on
|
||||
* this particular packing of the components of the version number; see the
|
||||
* documentation for GEOGRAPHICLIB_VERSION, below.
|
||||
**********************************************************************/
|
||||
#define GEOGRAPHICLIB_VERSION_NUM(a,b,c) ((((a) * 10000 + (b)) * 100) + (c))
|
||||
|
||||
/**
|
||||
* @relates GeographicLib::Constants
|
||||
* The version of GeographicLib as a single integer, packed as MMmmmmpp where
|
||||
* MM is the major version, mmmm is the minor version, and pp is the patch
|
||||
* level. Users should not rely on this particular packing of the components
|
||||
* of the version number. Instead they should use a test such as \code
|
||||
#if GEOGRAPHICLIB_VERSION >= GEOGRAPHICLIB_VERSION_NUM(1,37,0)
|
||||
...
|
||||
#endif
|
||||
* \endcode
|
||||
**********************************************************************/
|
||||
#define GEOGRAPHICLIB_VERSION \
|
||||
GEOGRAPHICLIB_VERSION_NUM(GEOGRAPHICLIB_VERSION_MAJOR, \
|
||||
GEOGRAPHICLIB_VERSION_MINOR, \
|
||||
GEOGRAPHICLIB_VERSION_PATCH)
|
||||
|
||||
// For reference, here is a table of Visual Studio and _MSC_VER
|
||||
// correspondences:
|
||||
//
|
||||
// _MSC_VER Visual Studio
|
||||
// 1900 vc14 (2015) First version of VS to include enough C++11 support
|
||||
// 191[0-6] vc15 (2017) First version of VS to include enough C++17 support
|
||||
// 192[0-9] vc16 (2019)
|
||||
// 1930-1944 vc17 (2022)
|
||||
// vc18 (2026)
|
||||
|
||||
#if defined(_MSC_VER) && defined(GEOGRAPHICLIB_SHARED_LIB) && \
|
||||
GEOGRAPHICLIB_SHARED_LIB
|
||||
# if GEOGRAPHICLIB_SHARED_LIB > 1
|
||||
# error GEOGRAPHICLIB_SHARED_LIB must be 0 or 1
|
||||
# elif defined(GeographicLib_SHARED_EXPORTS)
|
||||
# define GEOGRAPHICLIB_EXPORT __declspec(dllexport)
|
||||
# else
|
||||
# define GEOGRAPHICLIB_EXPORT __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define GEOGRAPHICLIB_EXPORT
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <GeographicLib/Math.hpp>
|
||||
|
||||
/**
|
||||
* \brief Namespace for %GeographicLib
|
||||
*
|
||||
* All of %GeographicLib is defined within the GeographicLib namespace. In
|
||||
* addition all the header files are included via GeographicLib/Class.hpp.
|
||||
* This minimizes the likelihood of conflicts with other packages.
|
||||
**********************************************************************/
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief %Constants needed by %GeographicLib
|
||||
*
|
||||
* Define constants specifying the WGS84 ellipsoid, the UTM and UPS
|
||||
* projections, and various unit conversions.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Constants.cpp
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT Constants {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
Constants() = delete; // Disable constructor
|
||||
|
||||
public:
|
||||
/**
|
||||
* A synonym for Math::degree<real>().
|
||||
**********************************************************************/
|
||||
static Math::real degree() { return Math::degree(); }
|
||||
/**
|
||||
* @return the number of radians in an arcminute.
|
||||
**********************************************************************/
|
||||
static Math::real arcminute()
|
||||
{ return Math::degree() / Math::dm; }
|
||||
/**
|
||||
* @return the number of radians in an arcsecond.
|
||||
**********************************************************************/
|
||||
static Math::real arcsecond()
|
||||
{ return Math::degree() / Math::ds; }
|
||||
|
||||
/** \name Ellipsoid parameters
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the equatorial radius of WGS84 ellipsoid (6378137 m).
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T WGS84_a()
|
||||
{ return 6378137 * meter<T>(); }
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the flattening of WGS84 ellipsoid (1/298.257223563).
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T WGS84_f() {
|
||||
// Evaluating this as 1000000000 / T(298257223563LL) reduces the
|
||||
// round-off error by about 10%. However, expressing the flattening as
|
||||
// 1/298.257223563 is well ingrained.
|
||||
return 1 / ( T(298257223563LL) / 1000000000 );
|
||||
}
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the gravitational constant of the WGS84 ellipsoid, \e GM, in
|
||||
* m<sup>3</sup> s<sup>−2</sup>.
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T WGS84_GM()
|
||||
{ return T(3986004) * 100000000 + 41800000; }
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the angular velocity of the WGS84 ellipsoid, ω, in rad
|
||||
* s<sup>−1</sup>.
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T WGS84_omega()
|
||||
{ return 7292115 / (T(1000000) * 100000); }
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the equatorial radius of GRS80 ellipsoid, \e a, in m.
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T GRS80_a()
|
||||
{ return 6378137 * meter<T>(); }
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the gravitational constant of the GRS80 ellipsoid, \e GM, in
|
||||
* m<sup>3</sup> s<sup>−2</sup>.
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T GRS80_GM()
|
||||
{ return T(3986005) * 100000000; }
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the angular velocity of the GRS80 ellipsoid, ω, in rad
|
||||
* s<sup>−1</sup>.
|
||||
*
|
||||
* This is about 2 π 366.25 / (365.25 × 24 × 3600) rad
|
||||
* s<sup>−1</sup>. 365.25 is the number of days in a Julian year and
|
||||
* 365.35/366.25 converts from solar days to sidereal days. Using the
|
||||
* number of days in a Gregorian year (365.2425) results in a worse
|
||||
* approximation (because the Gregorian year includes the precession of the
|
||||
* earth's axis).
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T GRS80_omega()
|
||||
{ return 7292115 / (T(1000000) * 100000); }
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the dynamical form factor of the GRS80 ellipsoid,
|
||||
* <i>J</i><sub>2</sub>.
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T GRS80_J2()
|
||||
{ return T(108263) / 100000000; }
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the central scale factor for UTM (0.9996).
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T UTM_k0()
|
||||
{return T(9996) / 10000; }
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the central scale factor for UPS (0.994).
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T UPS_k0()
|
||||
{ return T(994) / 1000; }
|
||||
///@}
|
||||
|
||||
/** \name Triaxial ellipsoid parameters
|
||||
*
|
||||
* These parameters are close to the values given by Milan Bursa, Vladimira
|
||||
* Fialova, "Parameters of the Earth's tri-axial level ellipsoid", Studia
|
||||
* Geophysica et Geodaetica 37(1), 1-13 (1993).
|
||||
* - longitude of major axis = −14.93° ± 0.05°
|
||||
* - \e a = 6378171.36 m ± 0.30 m
|
||||
* - \e a / (\e a − \e c) = 297.7738 ± 0.0003
|
||||
* - \e a / (\e a − \e b) = 91449 ± 60
|
||||
* .
|
||||
* which gives: \e a = 6378171.36 m, \e b = 6378101.61 m, \e c = 6356751.84
|
||||
* m. Here take the semiaxes to be whole numbers of meters, with (\e a +
|
||||
* \e b)/2 = WGS84_a(), \e a − \e b = 70 m, \e c = round(WGS84_a() *
|
||||
* (1 - WGS84_f())). This gives
|
||||
* - \e a = 6378172 m
|
||||
* - \e b = 6378102 m
|
||||
* - \e c = 6356752 m
|
||||
* - \e lon0 = −14.93°
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the major semiaxis of a triaxial approximation to the Earth, \e
|
||||
* a, in m (= 6378172).
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T Triaxial_Earth_a()
|
||||
{ return WGS84_a<T>() + 70/real(2); }
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the median semiaxis of a triaxial approximation to the Earth, \e
|
||||
* b, in m (= 6378102).
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T Triaxial_Earth_b()
|
||||
{ return WGS84_a<T>() - 70/real(2); }
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the minor semiaxis of a triaxial approximation to the Earth, \e
|
||||
* c, in m (= 6356752).
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T Triaxial_Earth_c()
|
||||
{ using std::round; return round(WGS84_a<T>() * (1 - WGS84_f())); }
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the longitude, with respect to Greenwich, of the major semiaxis
|
||||
* of of a triaxial approximation to the Earth, \e lon0, in degrees (=
|
||||
* −14.93).
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T Triaxial_Earth_lon0()
|
||||
{ return T(1493) / 100; }
|
||||
///@}
|
||||
|
||||
/** \name SI units
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the number of meters in a meter.
|
||||
*
|
||||
* This is unity, but this lets the internal system of units be changed if
|
||||
* necessary.
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T meter() { return T(1); }
|
||||
/**
|
||||
* @return the number of meters in a kilometer.
|
||||
**********************************************************************/
|
||||
static Math::real kilometer()
|
||||
{ return 1000 * meter<real>(); }
|
||||
/**
|
||||
* @return the number of meters in a nautical mile (approximately 1 arc
|
||||
* minute)
|
||||
**********************************************************************/
|
||||
static Math::real nauticalmile()
|
||||
{ return 1852 * meter<real>(); }
|
||||
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the number of square meters in a square meter.
|
||||
*
|
||||
* This is unity, but this lets the internal system of units be changed if
|
||||
* necessary.
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T square_meter()
|
||||
{ return meter<T>() * meter<T>(); }
|
||||
/**
|
||||
* @return the number of square meters in a hectare.
|
||||
**********************************************************************/
|
||||
static Math::real hectare()
|
||||
{ return 10000 * square_meter<real>(); }
|
||||
/**
|
||||
* @return the number of square meters in a square kilometer.
|
||||
**********************************************************************/
|
||||
static Math::real square_kilometer()
|
||||
{ return kilometer() * kilometer(); }
|
||||
/**
|
||||
* @return the number of square meters in a square nautical mile.
|
||||
**********************************************************************/
|
||||
static Math::real square_nauticalmile()
|
||||
{ return nauticalmile() * nauticalmile(); }
|
||||
///@}
|
||||
|
||||
/** \name Anachronistic British units
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return the number of meters in an international foot.
|
||||
**********************************************************************/
|
||||
static Math::real foot()
|
||||
{ return real(254 * 12) / 10000 * meter<real>(); }
|
||||
/**
|
||||
* @return the number of meters in a yard.
|
||||
**********************************************************************/
|
||||
static Math::real yard() { return 3 * foot(); }
|
||||
/**
|
||||
* @return the number of meters in a fathom.
|
||||
**********************************************************************/
|
||||
static Math::real fathom() { return 2 * yard(); }
|
||||
/**
|
||||
* @return the number of meters in a chain.
|
||||
**********************************************************************/
|
||||
static Math::real chain() { return 22 * yard(); }
|
||||
/**
|
||||
* @return the number of meters in a furlong.
|
||||
**********************************************************************/
|
||||
static Math::real furlong() { return 10 * chain(); }
|
||||
/**
|
||||
* @return the number of meters in a statute mile.
|
||||
**********************************************************************/
|
||||
static Math::real mile() { return 8 * furlong(); }
|
||||
/**
|
||||
* @return the number of square meters in an acre.
|
||||
**********************************************************************/
|
||||
static Math::real acre() { return chain() * furlong(); }
|
||||
/**
|
||||
* @return the number of square meters in a square statute mile.
|
||||
**********************************************************************/
|
||||
static Math::real square_mile() { return mile() * mile(); }
|
||||
///@}
|
||||
|
||||
/** \name Anachronistic US units
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return the number of meters in a US survey foot.
|
||||
**********************************************************************/
|
||||
static Math::real surveyfoot()
|
||||
{ return real(1200) / 3937 * meter<real>(); }
|
||||
///@}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Exception handling for %GeographicLib
|
||||
*
|
||||
* A class to handle exceptions. It's derived from std::runtime_error so it
|
||||
* can be caught by the usual catch clauses.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-GeographicErr.cpp
|
||||
**********************************************************************/
|
||||
class GeographicErr : public std::runtime_error {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param[in] msg a string message, which is accessible in the catch
|
||||
* clause via what().
|
||||
**********************************************************************/
|
||||
GeographicErr(const std::string& msg) : std::runtime_error(msg) {}
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_CONSTANTS_HPP
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* \file DAuxLatitude.hpp
|
||||
* \brief Header for the GeographicLib::DAuxLatitude class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2022-2023) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_DAUXLATITUDE_HPP)
|
||||
#define GEOGRAPHICLIB_DAUXLATITUDE_HPP 1
|
||||
|
||||
#include <GeographicLib/AuxLatitude.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Divided differences of auxiliary latitudes.
|
||||
*
|
||||
* This class computed the divided differences of auxiliary latitudes and
|
||||
* some other divided differences needed to support rhumb line calculations.
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT DAuxLatitude : public AuxLatitude {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
typedef AuxLatitude base;
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param[in] a equatorial radius.
|
||||
* @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
|
||||
* Negative \e f gives a prolate ellipsoid.
|
||||
* @exception GeographicErr if \e a or (1 − \e f) \e a is not
|
||||
* positive.
|
||||
**********************************************************************/
|
||||
DAuxLatitude(real a, real f) : AuxLatitude(a, f) {}
|
||||
/**
|
||||
* The divided difference of one auxiliary latitude with respect to
|
||||
* another.
|
||||
*
|
||||
* @param[in] auxin an AuxLatitude::aux indicating the type of
|
||||
* auxiliary latitude \e zeta.
|
||||
* @param[in] auxout an AuxLatitude::aux indicating the type of
|
||||
* auxiliary latitude \e eta.
|
||||
* @param[in] zeta1 the first of the input auxiliary latitudes.
|
||||
* @param[in] zeta2 the second of the input auxiliary latitudes.
|
||||
* @return the divided difference (\e eta2 - \e eta1) / (\e zeta2 - \e
|
||||
* zeta1).
|
||||
*
|
||||
* \note This routine uses the series method.
|
||||
*
|
||||
* In the expression for the divided difference above, the angle quantities
|
||||
* should be understood as the conventional measure of angle (either in
|
||||
* radians or in degrees).
|
||||
*
|
||||
* The Fourier coefficients for a specific \e auxin and \e auxout are
|
||||
* computed and saved on the first call; the saved coefficients are used on
|
||||
* subsequent calls. The series method is accurate for abs(\e f) ≤
|
||||
* 1/150.
|
||||
**********************************************************************/
|
||||
Math::real DConvert(int auxin, int auxout,
|
||||
const AuxAngle& zeta1, const AuxAngle& zeta2) const;
|
||||
/**
|
||||
* The divided difference of the parametric latitude with respect to the
|
||||
* geographic latitude.
|
||||
*
|
||||
* @param[in] phi1 the first geographic latitude as an AuxAngle.
|
||||
* @param[in] phi2 the second geographic latitude as an AuxAngle.
|
||||
* @return the divided difference (\e beta2 - \e beta1) / (\e phi2 - \e
|
||||
* phi1), where \e beta is the parametric latitude.
|
||||
*
|
||||
* \note This routine uses the exact formulas and is valid for arbitrary
|
||||
* flattening.
|
||||
**********************************************************************/
|
||||
Math::real DParametric(const AuxAngle& phi1, const AuxAngle& phi2) const;
|
||||
/**
|
||||
* The divided difference of the rectifying latitude with respect to the
|
||||
* geographic latitude.
|
||||
*
|
||||
* @param[in] phi1 the first geographic latitude as an AuxAngle.
|
||||
* @param[in] phi2 the second geographic latitude as an AuxAngle.
|
||||
* @return the divided difference (\e mu2 - \e mu1) / (\e phi2 - \e
|
||||
* phi1), where \e mu is the rectifying latitude.
|
||||
*
|
||||
* \note This routine uses the exact formulas and is valid for arbitrary
|
||||
* flattening.
|
||||
**********************************************************************/
|
||||
Math::real DRectifying(const AuxAngle& phi1, const AuxAngle& phi2) const;
|
||||
/**
|
||||
* The divided difference of the isometric latitude with respect to the
|
||||
* geographic latitude.
|
||||
*
|
||||
* @param[in] phi1 the first geographic latitude as an AuxAngle.
|
||||
* @param[in] phi2 the second geographic latitude as an AuxAngle.
|
||||
* @return the divided difference (\e psi2 - \e psi1) / (\e phi2 - \e
|
||||
* phi1), where \e psi = asinh( tan(\e chi) ) is the isometric latitude
|
||||
* and \e chi is the conformal latitude.
|
||||
*
|
||||
* \note This routine uses the exact formulas and is valid for arbitrary
|
||||
* flattening.
|
||||
**********************************************************************/
|
||||
Math::real DIsometric(const AuxAngle& phi1, const AuxAngle& phi2) const;
|
||||
/**
|
||||
* The divided difference of AuxLatitude::Clenshaw.
|
||||
*
|
||||
* @param[in] sinp if true sum the sine series, else sum the cosine series.
|
||||
* @param[in] Delta either 1 \e or (zeta2 - zeta1) in radians.
|
||||
* @param[in] szeta1 sin(\e zeta1).
|
||||
* @param[in] czeta1 cos(\e zeta1).
|
||||
* @param[in] szeta2 sin(\e zeta2).
|
||||
* @param[in] czeta2 cos(\e zeta2).
|
||||
* @param[in] c the array of coefficients.
|
||||
* @param[in] K the number of coefficients.
|
||||
* @return the divided difference.
|
||||
*
|
||||
* The result is
|
||||
* <pre>
|
||||
* ( AuxLatitude::Clenshaw(sinp, szeta2, czeta2, c, K) -
|
||||
* AuxLatitude::Clenshaw(sinp, szeta1, czeta1, c, K) ) / Delta
|
||||
* </pre>
|
||||
*
|
||||
* \warning \e Delta **must** be either 1 or (\e zeta2 - \e zeta1);
|
||||
* other values will return nonsense.
|
||||
**********************************************************************/
|
||||
static Math::real DClenshaw(bool sinp, real Delta,
|
||||
real szeta1, real czeta1,
|
||||
real szeta2, real czeta2,
|
||||
const real c[], int K);
|
||||
/**
|
||||
* The divided difference of the isometric latitude with respect to the
|
||||
* conformal latitude.
|
||||
*
|
||||
* @param[in] x tan(\e chi1).
|
||||
* @param[in] y tan(\e chi2).
|
||||
* @return the divided difference (\e psi2 - \e psi1) / (\e chi2 - \e
|
||||
* chi1), where \e psi = asinh( tan(\e chi) ).
|
||||
*
|
||||
* \note This parameters for this routine are the \e tangents of conformal
|
||||
* latitude.
|
||||
*
|
||||
* This routine computes Dasinh(x, y) / Datan(x, y).
|
||||
**********************************************************************/
|
||||
static Math::real Dlam(real x, real y) {
|
||||
using std::isnan, std::isinf;
|
||||
return x == y ? base::sc(x) :
|
||||
(isnan(x) || isnan(y) ? std::numeric_limits<real>::quiet_NaN() :
|
||||
(isinf(x) || isinf(y) ? std::numeric_limits<real>::infinity() :
|
||||
Dasinh(x, y) / Datan(x, y)));
|
||||
}
|
||||
// Dp0Dpsi in terms of chi
|
||||
/**
|
||||
* The divided difference of the spherical rhumb area term with respect to
|
||||
* the isometric latitude.
|
||||
*
|
||||
* @param[in] x tan(\e chi1).
|
||||
* @param[in] y tan(\e chi2).
|
||||
* @return the divided difference (p0(\e chi2) - p0(\e chi1)) / (\e psi2 -
|
||||
* \e psi1), where p0(\e chi) = log( sec(\e chi) ) and \e psi = asinh(
|
||||
* tan(\e chi) ).
|
||||
*
|
||||
* \note This parameters for this routine are the \e tangents of conformal
|
||||
* latitude.
|
||||
**********************************************************************/
|
||||
static Math::real Dp0Dpsi(real x, real y) {
|
||||
using std::isnan, std::isinf, std::copysign;
|
||||
return x == y ? base::sn(x) :
|
||||
(isnan(x + y) ? x + y : // N.B. nan for inf-inf
|
||||
(isinf(x) ? copysign(real(1), x) :
|
||||
(isinf(y) ? copysign(real(1), y) :
|
||||
Dasinh(h(x), h(y)) * Dh(x, y) / Dasinh(x, y))));
|
||||
}
|
||||
protected: // so TestAux can access these functions
|
||||
/// \cond SKIP
|
||||
// (sn(y) - sn(x)) / (y - x)
|
||||
static real Dsn(real x, real y);
|
||||
static real Datan(real x, real y);
|
||||
static real Dasinh(real x, real y);
|
||||
// h(tan(x)) = tan(x) * sin(x) / 2
|
||||
static real h(real x) { return x * base::sn(x) / 2; }
|
||||
static real Dh(real x, real y);
|
||||
real Datanhee(real tphi1, real tphi2) const;
|
||||
/// \endcond
|
||||
private:
|
||||
static real Dsin(real x, real y) {
|
||||
using std::sin, std::cos;
|
||||
real d = (x - y) / 2;
|
||||
return cos((x + y) / 2) * (d != 0 ? sin(d) / d : 1);
|
||||
}
|
||||
// (E(x) - E(y)) / (x - y)
|
||||
real DE(const AuxAngle& X, const AuxAngle& Y) const;
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_DAUXLATITUDE_HPP
|
||||
@@ -0,0 +1,397 @@
|
||||
/**
|
||||
* \file DMS.hpp
|
||||
* \brief Header for GeographicLib::DMS class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2008-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_DMS_HPP)
|
||||
#define GEOGRAPHICLIB_DMS_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/Utility.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Squelch warnings about unrepresentable characters
|
||||
# pragma warning (push)
|
||||
# pragma warning (disable: 4819)
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Convert between degrees and the %DMS representation
|
||||
*
|
||||
* Parse a string representing degree, minutes, and seconds and return the
|
||||
* angle in degrees and format an angle in degrees as degree, minutes, and
|
||||
* seconds. In addition, handle NANs and infinities on input and output.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-DMS.cpp
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT DMS {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Indicator for presence of hemisphere indicator (N/S/E/W) on latitudes
|
||||
* and longitudes.
|
||||
**********************************************************************/
|
||||
enum flag {
|
||||
/**
|
||||
* No indicator present.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
NONE = 0,
|
||||
/**
|
||||
* Latitude indicator (N/S) present.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LATITUDE = 1,
|
||||
/**
|
||||
* Longitude indicator (E/W) present.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LONGITUDE = 2,
|
||||
/**
|
||||
* Used in Encode to indicate output of an azimuth in [000, 360) with no
|
||||
* letter indicator.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
AZIMUTH = 3,
|
||||
/**
|
||||
* Used in Encode to indicate output of a plain number.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
NUMBER = 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicator for trailing units on an angle.
|
||||
**********************************************************************/
|
||||
enum component {
|
||||
/**
|
||||
* Trailing unit is degrees.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
DEGREE = 0,
|
||||
/**
|
||||
* Trailing unit is arc minutes.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
MINUTE = 1,
|
||||
/**
|
||||
* Trailing unit is arc seconds.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
SECOND = 2,
|
||||
};
|
||||
|
||||
private:
|
||||
typedef Math::real real;
|
||||
// Replace all occurrences of pat by c. If c is NULL remove pat.
|
||||
static void replace(std::string& s, const std::string& pat, char c);
|
||||
static const char* const hemispheres_;
|
||||
static const char* const signs_;
|
||||
static const char* const digits_;
|
||||
static const char* const dmsindicators_;
|
||||
static const char* const components_[3];
|
||||
static Math::real NumMatch(const std::string& s);
|
||||
static Math::real InternalDecode(const std::string& dmsa, flag& ind);
|
||||
DMS() = delete; // Disable constructor
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Convert a string in DMS to an angle.
|
||||
*
|
||||
* @param[in] dms string input.
|
||||
* @param[out] ind a DMS::flag value signaling the presence of a
|
||||
* hemisphere indicator.
|
||||
* @exception GeographicErr if \e dms is malformed (see below).
|
||||
* @return angle (degrees).
|
||||
*
|
||||
* Degrees, minutes, and seconds are indicated by the characters d, '
|
||||
* (single quote), " (double quote), and these components may only be
|
||||
* given in this order. Any (but not all) components may be omitted and
|
||||
* other symbols (e.g., the ° symbol for degrees and the unicode prime
|
||||
* and double prime symbols for minutes and seconds) may be substituted;
|
||||
* two single quotes can be used instead of ". The last component
|
||||
* indicator may be omitted and is assumed to be the next smallest unit
|
||||
* (thus 33d10 is interpreted as 33d10'). The final component may be a
|
||||
* decimal fraction but the non-final components must be integers. Instead
|
||||
* of using d, ', and " to indicate degrees, minutes, and seconds, :
|
||||
* (colon) may be used to <i>separate</i> these components (numbers must
|
||||
* appear before and after each colon); thus 50d30'10.3" may be
|
||||
* written as 50:30:10.3, 5.5' may be written 0:5.5, and so on. The
|
||||
* integer parts of the minutes and seconds components must be less
|
||||
* than 60. A single leading sign is permitted. A hemisphere designator
|
||||
* (N, E, W, S) may be added to the beginning or end of the string. The
|
||||
* result is multiplied by the implied sign of the hemisphere designator
|
||||
* (negative for S and W). In addition \e ind is set to DMS::LATITUDE if N
|
||||
* or S is present, to DMS::LONGITUDE if E or W is present, and to
|
||||
* DMS::NONE otherwise. Leading and trailing whitespace is removed from
|
||||
* the string before processing. This routine throws an error on a
|
||||
* malformed string. No check is performed on the range of the result.
|
||||
* Examples of legal and illegal strings are
|
||||
* - <i>LEGAL</i> (all the entries on each line are equivalent)
|
||||
* - -20.51125, 20d30'40.5"S, -20°30'40.5, -20d30.675,
|
||||
* N-20d30'40.5", -20:30:40.5
|
||||
* - 4d0'9, 4d9", 4d9'', 4:0:9, 004:00:09, 4.0025, 4.0025d, 4d0.15,
|
||||
* 04:.15
|
||||
* - 4:59.99999999999999, 4:60.0, 4:59:59.9999999999999, 4:59:60.0, 5
|
||||
* - <i>ILLEGAL</i> (the exception thrown explains the problem)
|
||||
* - 4d5"4', 4::5, 4:5:, :4:5, 4d4.5'4", -N20.5, 1.8e2d, 4:60,
|
||||
* 4:59:60
|
||||
*
|
||||
* The decoding operation can also perform addition and subtraction
|
||||
* operations. If the string includes <i>internal</i> signs (i.e., not at
|
||||
* the beginning nor immediately after an initial hemisphere designator),
|
||||
* then the string is split immediately before such signs and each piece is
|
||||
* decoded according to the above rules and the results added; thus
|
||||
* <code>S3-2.5+4.1N</code> is parsed as the sum of <code>S3</code>,
|
||||
* <code>-2.5</code>, <code>+4.1N</code>. Any piece can include a
|
||||
* hemisphere designator; however, if multiple designators are given, they
|
||||
* must compatible; e.g., you cannot mix N and E. In addition, the
|
||||
* designator can appear at the beginning or end of the first piece, but
|
||||
* must be at the end of all subsequent pieces (a hemisphere designator is
|
||||
* not allowed after the initial sign). Examples of legal and illegal
|
||||
* combinations are
|
||||
* - <i>LEGAL</i> (these are all equivalent)
|
||||
* - -070:00:45, 70:01:15W+0:0.5, 70:01:15W-0:0:30W, W70:01:15+0:0:30E
|
||||
* - <i>ILLEGAL</i> (the exception thrown explains the problem)
|
||||
* - 70:01:15W+0:0:15N, W70:01:15+W0:0:15
|
||||
*
|
||||
* \warning The "exponential" notation is not recognized. Thus
|
||||
* <code>7.0E1</code> is illegal, while <code>7.0E+1</code> is parsed as
|
||||
* <code>(7.0E) + (+1)</code>, yielding the same result as
|
||||
* <code>8.0E</code>.
|
||||
*
|
||||
* \note At present, all the string handling in the C++ implementation of
|
||||
* %GeographicLib is with 8-bit characters. The support for unicode
|
||||
* symbols for degrees, minutes, and seconds is therefore via the
|
||||
* <a href="https://en.wikipedia.org/wiki/UTF-8">UTF-8</a> encoding. (The
|
||||
* JavaScript implementation of this class uses unicode natively, of
|
||||
* course.)
|
||||
*
|
||||
* Here is the list of Unicode symbols supported for degrees, minutes,
|
||||
* seconds, and the plus and minus signs; various symbols denoting variants
|
||||
* of a space, which may separate the components of a DMS string, are
|
||||
* removed:
|
||||
* - degrees:
|
||||
* - d, D lower and upper case letters
|
||||
* - U+00b0 degree symbol (°)
|
||||
* - U+00ba masculine ordinal indicator (º)
|
||||
* - U+2070 superscript zero (⁰)
|
||||
* - U+02da ring above (˚)
|
||||
* - U+2218 compose function (∘)
|
||||
* - * the <a href="https://grid.nga.mil">GRiD</a> symbol for degrees
|
||||
* - minutes:
|
||||
* - ' apostrophe
|
||||
* - ` grave accent
|
||||
* - U+2032 prime (′)
|
||||
* - U+2035 back prime (‵)
|
||||
* - U+00b4 acute accent (´)
|
||||
* - U+2018 left single quote (‘)
|
||||
* - U+2019 right single quote (’)
|
||||
* - U+201b reversed-9 single quote (‛)
|
||||
* - U+02b9 modifier letter prime (ʹ)
|
||||
* - U+02ca modifier letter acute accent (ˊ)
|
||||
* - U+02cb modifier letter grave accent (ˋ)
|
||||
* - seconds:
|
||||
* - " quotation mark
|
||||
* - U+2033 double prime (″)
|
||||
* - U+2036 reversed double prime (‶)
|
||||
* + U+02dd double acute accent (˝)
|
||||
* - U+201c left double quote (“)
|
||||
* - U+201d right double quote (”)
|
||||
* - U+201f reversed-9 double quote (‟)
|
||||
* - U+02ba modifier letter double prime (ʺ)
|
||||
* - ' ' any two consecutive symbols for minutes
|
||||
* - plus sign:
|
||||
* - + plus
|
||||
* - U+2795 heavy plus (➕)
|
||||
* - U+2064 invisible plus (||)
|
||||
* - minus sign:
|
||||
* - - hyphen
|
||||
* - U+2010 dash (‐)
|
||||
* - U+2011 non-breaking hyphen (‑)
|
||||
* - U+2013 en dash (–)
|
||||
* - U+2014 em dash (—)
|
||||
* - U+2212 minus sign (−)
|
||||
* - U+2796 heavy minus (➖)
|
||||
* - ignored spaces:
|
||||
* - U+00a0 non-breaking space
|
||||
* - U+2007 figure space (| |)
|
||||
* - U+2009 thin space (| |)
|
||||
* - U+200a hair space ( | |)
|
||||
* - U+200b invisible space (||)
|
||||
* - U+202f narrow space ( | |)
|
||||
* - U+2063 invisible separator (||)
|
||||
* .
|
||||
* The codes with a leading zero byte, e.g., U+00b0, are accepted in their
|
||||
* UTF-8 coded form 0xc2 0xb0 and as a single byte 0xb0.
|
||||
**********************************************************************/
|
||||
static Math::real Decode(const std::string& dms, flag& ind);
|
||||
|
||||
/**
|
||||
* Convert DMS to an angle.
|
||||
*
|
||||
* @param[in] d degrees.
|
||||
* @param[in] m arc minutes.
|
||||
* @param[in] s arc seconds.
|
||||
* @return angle (degrees)
|
||||
*
|
||||
* This does not propagate the sign on \e d to the other components,
|
||||
* so -3d20' would need to be represented as - DMS::Decode(3.0, 20.0) or
|
||||
* DMS::Decode(-3.0, -20.0).
|
||||
**********************************************************************/
|
||||
static Math::real Decode(real d, real m = 0, real s = 0)
|
||||
{ return d + (m + s / real(Math::ms)) / real(Math::dm); }
|
||||
|
||||
/**
|
||||
* Convert a pair of strings to latitude and longitude.
|
||||
*
|
||||
* @param[in] dmsa first string.
|
||||
* @param[in] dmsb second string.
|
||||
* @param[out] lat latitude (degrees).
|
||||
* @param[out] lon longitude (degrees).
|
||||
* @param[in] longfirst if true assume longitude is given before latitude
|
||||
* in the absence of hemisphere designators (default false).
|
||||
* @exception GeographicErr if \e dmsa or \e dmsb is malformed.
|
||||
* @exception GeographicErr if \e dmsa and \e dmsb are both interpreted as
|
||||
* latitudes.
|
||||
* @exception GeographicErr if \e dmsa and \e dmsb are both interpreted as
|
||||
* longitudes.
|
||||
* @exception GeographicErr if decoded latitude is not in [−90°,
|
||||
* 90°].
|
||||
*
|
||||
* By default, the \e lat (resp., \e lon) is assigned to the results of
|
||||
* decoding \e dmsa (resp., \e dmsb). However this is overridden if either
|
||||
* \e dmsa or \e dmsb contain a latitude or longitude hemisphere designator
|
||||
* (N, S, E, W). If an exception is thrown, \e lat and \e lon are
|
||||
* unchanged.
|
||||
**********************************************************************/
|
||||
static void DecodeLatLon(const std::string& dmsa, const std::string& dmsb,
|
||||
real& lat, real& lon,
|
||||
bool longfirst = false);
|
||||
|
||||
/**
|
||||
* Convert a string to an angle in degrees.
|
||||
*
|
||||
* @param[in] angstr input string.
|
||||
* @exception GeographicErr if \e angstr is malformed.
|
||||
* @exception GeographicErr if \e angstr includes a hemisphere designator.
|
||||
* @return angle (degrees)
|
||||
*
|
||||
* No hemisphere designator is allowed and no check is done on the range of
|
||||
* the result.
|
||||
**********************************************************************/
|
||||
static Math::real DecodeAngle(const std::string& angstr);
|
||||
|
||||
/**
|
||||
* Convert a string to an azimuth in degrees.
|
||||
*
|
||||
* @param[in] azistr input string.
|
||||
* @exception GeographicErr if \e azistr is malformed.
|
||||
* @exception GeographicErr if \e azistr includes a N/S designator.
|
||||
* @return azimuth (degrees) reduced to the range [−180°,
|
||||
* 180°].
|
||||
*
|
||||
* A hemisphere designator E/W can be used; the result is multiplied by
|
||||
* −1 if W is present.
|
||||
**********************************************************************/
|
||||
static Math::real DecodeAzimuth(const std::string& azistr);
|
||||
|
||||
/**
|
||||
* Convert angle (in degrees) into a DMS string (using d, ', and ").
|
||||
*
|
||||
* @param[in] angle input angle (degrees)
|
||||
* @param[in] trailing DMS::component value indicating the trailing units
|
||||
* of the string (this component is given as a decimal number if
|
||||
* necessary).
|
||||
* @param[in] prec the number of digits after the decimal point for the
|
||||
* trailing component.
|
||||
* @param[in] ind DMS::flag value indicating additional formatting.
|
||||
* @param[in] dmssep if non-null, use as the DMS separator character
|
||||
* (instead of d, ', " delimiters).
|
||||
* @exception std::bad_alloc if memory for the string can't be allocated.
|
||||
* @return formatted string
|
||||
*
|
||||
* The interpretation of \e ind is as follows:
|
||||
* - ind == DMS::NONE, signed result no leading zeros on degrees except in
|
||||
* the units place, e.g., -8d03'.
|
||||
* - ind == DMS::LATITUDE, trailing N or S hemisphere designator, no sign,
|
||||
* pad degrees to 2 digits, e.g., 08d03'S.
|
||||
* - ind == DMS::LONGITUDE, trailing E or W hemisphere designator, no
|
||||
* sign, pad degrees to 3 digits, e.g., 008d03'W.
|
||||
* - ind == DMS::AZIMUTH, convert to the range [0, 360°), no
|
||||
* sign, pad degrees to 3 digits, e.g., 351d57'.
|
||||
* .
|
||||
* The integer parts of the minutes and seconds components are always given
|
||||
* with 2 digits.
|
||||
**********************************************************************/
|
||||
static std::string Encode(real angle, component trailing, unsigned prec,
|
||||
flag ind = NONE, char dmssep = char(0));
|
||||
|
||||
/**
|
||||
* Convert angle into a DMS string (using d, ', and ") selecting the
|
||||
* trailing component based on the precision.
|
||||
*
|
||||
* @param[in] angle input angle (degrees)
|
||||
* @param[in] prec the precision relative to 1 degree.
|
||||
* @param[in] ind DMS::flag value indicated additional formatting.
|
||||
* @param[in] dmssep if non-null, use as the DMS separator character
|
||||
* (instead of d, ', " delimiters).
|
||||
* @exception std::bad_alloc if memory for the string can't be allocated.
|
||||
* @return formatted string
|
||||
*
|
||||
* \e prec indicates the precision relative to 1 degree, e.g., \e prec = 3
|
||||
* gives a result accurate to 0.1' and \e prec = 4 gives a result accurate
|
||||
* to 1". \e ind is interpreted as in DMS::Encode with the additional
|
||||
* facility that DMS::NUMBER represents \e angle as a number in fixed
|
||||
* format with precision \e prec.
|
||||
**********************************************************************/
|
||||
static std::string Encode(real angle, unsigned prec, flag ind = NONE,
|
||||
char dmssep = char(0)) {
|
||||
return ind == NUMBER ? Utility::str(angle, int(prec)) :
|
||||
Encode(angle,
|
||||
prec < 2 ? DEGREE : (prec < 4 ? MINUTE : SECOND),
|
||||
prec < 2 ? prec : (prec < 4 ? prec - 2 : prec - 4),
|
||||
ind, dmssep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split angle into degrees and minutes
|
||||
*
|
||||
* @param[in] ang angle (degrees)
|
||||
* @param[out] d degrees (an integer returned as a real)
|
||||
* @param[out] m arc minutes.
|
||||
**********************************************************************/
|
||||
static void Encode(real ang, real& d, real& m) {
|
||||
d = int(ang); m = real(Math::dm) * (ang - d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split angle into degrees and minutes and seconds.
|
||||
*
|
||||
* @param[in] ang angle (degrees)
|
||||
* @param[out] d degrees (an integer returned as a real)
|
||||
* @param[out] m arc minutes (an integer returned as a real)
|
||||
* @param[out] s arc seconds.
|
||||
**********************************************************************/
|
||||
static void Encode(real ang, real& d, real& m, real& s) {
|
||||
d = int(ang); ang = real(Math::dm) * (ang - d);
|
||||
m = int(ang); s = real(Math::ms) * (ang - m);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#endif // GEOGRAPHICLIB_DMS_HPP
|
||||
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* \file DST.hpp
|
||||
* \brief Header for GeographicLib::DST class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2022-2024) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_DST_HPP)
|
||||
#define GEOGRAPHICLIB_DST_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
/// \cond SKIP
|
||||
template<typename scalar_t>
|
||||
class kissfft;
|
||||
/// \endcond
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Discrete sine transforms
|
||||
*
|
||||
* This decomposes periodic functions \f$ f(\sigma) \f$ (period \f$ 2\pi \f$)
|
||||
* which are odd about \f$ \sigma = 0 \f$ and even about \f$ \sigma = \frac12
|
||||
* \pi \f$ into a Fourier series
|
||||
* \f[
|
||||
* f(\sigma) = \sum_{l=0}^\infty F_l \sin\bigl((2l+1)\sigma\bigr).
|
||||
* \f]
|
||||
*
|
||||
* The first \f$ N \f$ components of \f$ F_l \f$, for \f$0 \le l < N\f$ may
|
||||
* be approximated by
|
||||
* \f[
|
||||
* F_l = \frac2N \sum_{j=1}^{N}
|
||||
* p_j f(\sigma_j) \sin\bigl((2l+1)\sigma_j\bigr),
|
||||
* \f]
|
||||
* where \f$ \sigma_j = j\pi/(2N) \f$ and \f$ p_j = \frac12 \f$ for \f$ j = N
|
||||
* \f$ and \f$ 1 \f$ otherwise. \f$ F_l \f$ is a discrete sine transform of
|
||||
* type DST-III and may be conveniently computed using the fast Fourier
|
||||
* transform, FFT; this is implemented with the DST::transform method.
|
||||
*
|
||||
* Having computed \f$ F_l \f$ based on \f$ N \f$ evaluations of \f$
|
||||
* f(\sigma) \f$ at \f$ \sigma_j = j\pi/(2N) \f$, it is possible to
|
||||
* refine these transform values and add another \f$ N \f$ coefficients by
|
||||
* evaluating \f$ f(\sigma) \f$ at \f$ (j-\frac12)\pi/(2N) \f$; this is
|
||||
* implemented with the DST::refine method.
|
||||
*
|
||||
* Here we compute FFTs using the kissfft package
|
||||
* https://github.com/mborgerding/kissfft by Mark Borgerding.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-DST.cpp
|
||||
*
|
||||
* \note The FFTW package https://www.fftw.org/ can also be used. However
|
||||
* this is a more complicated dependency, its CMake support is broken, and it
|
||||
* doesn't work with mpreals (GEOGRAPHICLIB_PRECISION = 5).
|
||||
*
|
||||
* \deprecated The functionality offered by this class is also provided by
|
||||
* the more general class Trigfun. It is recommended to use Trigfun for
|
||||
* new applications.
|
||||
**********************************************************************/
|
||||
|
||||
class DST {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
int _nN;
|
||||
typedef kissfft<real> fft_t;
|
||||
std::shared_ptr<fft_t> _fft;
|
||||
// Implement DST-III (centerp = false) or DST-IV (centerp = true)
|
||||
void fft_transform(real data[], real F[], bool centerp) const;
|
||||
// Add another N terms to F
|
||||
void fft_transform2(real data[], real F[]) const;
|
||||
public:
|
||||
/**
|
||||
* Constructor specifying the number of points to use.
|
||||
*
|
||||
* @param[in] N the number of points to use.
|
||||
**********************************************************************/
|
||||
GEOGRAPHICLIB_EXPORT DST(int N = 0);
|
||||
|
||||
/**
|
||||
* Reset the given number of points.
|
||||
*
|
||||
* @param[in] N the number of points to use.
|
||||
**********************************************************************/
|
||||
void GEOGRAPHICLIB_EXPORT reset(int N);
|
||||
|
||||
/**
|
||||
* Return the number of points.
|
||||
*
|
||||
* @return the number of points to use.
|
||||
**********************************************************************/
|
||||
int N() const { return _nN; }
|
||||
|
||||
/**
|
||||
* Determine first \e N terms in the Fourier series
|
||||
*
|
||||
* @param[in] f the function used for evaluation.
|
||||
* @param[out] F the first \e N coefficients of the Fourier series.
|
||||
*
|
||||
* The evaluates \f$ f(\sigma) \f$ at \f$ \sigma = (j + 1) \pi / (2 N) \f$
|
||||
* for integer \f$ j \in [0, N) \f$. \e F should be an array of length at
|
||||
* least \e N.
|
||||
**********************************************************************/
|
||||
void GEOGRAPHICLIB_EXPORT transform(std::function<real(real)> f, real F[])
|
||||
const;
|
||||
|
||||
/**
|
||||
* Refine the Fourier series by doubling the number of points sampled
|
||||
*
|
||||
* @param[in] f the function used for evaluation.
|
||||
* @param[inout] F on input the first \e N coefficents of the Fourier
|
||||
* series; on output the refined transform based on 2\e N points, i.e.,
|
||||
* the first 2\e N coefficents.
|
||||
*
|
||||
* The evaluates \f$ f(\sigma) \f$ at additional points \f$ \sigma = (j +
|
||||
* \frac12) \pi / (2 N) \f$ for integer \f$ j \in [0, N) \f$, computes the
|
||||
* DST-IV transform of these, and combines this with the input \e F to
|
||||
* compute the 2\e N term DST-III discrete sine transform. This is
|
||||
* equivalent to calling transform with twice the value of \e N but is more
|
||||
* efficient, given that the \e N term coefficients are already known. See
|
||||
* the example code above.
|
||||
**********************************************************************/
|
||||
void GEOGRAPHICLIB_EXPORT refine(std::function<real(real)> f, real F[])
|
||||
const;
|
||||
|
||||
/**
|
||||
* Evaluate the Fourier sum given the sine and cosine of the angle
|
||||
*
|
||||
* @param[in] sinx sinσ.
|
||||
* @param[in] cosx cosσ.
|
||||
* @param[in] F the array of Fourier coefficients.
|
||||
* @param[in] N the number of Fourier coefficients.
|
||||
* @return the value of the Fourier sum.
|
||||
**********************************************************************/
|
||||
static real GEOGRAPHICLIB_EXPORT eval(real sinx, real cosx,
|
||||
const real F[], int N);
|
||||
|
||||
/**
|
||||
* Evaluate the integral of Fourier sum given the sine and cosine of the
|
||||
* angle
|
||||
*
|
||||
* @param[in] sinx sinσ.
|
||||
* @param[in] cosx cosσ.
|
||||
* @param[in] F the array of Fourier coefficients.
|
||||
* @param[in] N the number of Fourier coefficients.
|
||||
* @return the value of the integral.
|
||||
*
|
||||
* The constant of integration is chosen so that the integral is zero at
|
||||
* \f$ \sigma = \frac12\pi \f$.
|
||||
**********************************************************************/
|
||||
static real GEOGRAPHICLIB_EXPORT integral(real sinx, real cosx,
|
||||
const real F[], int N);
|
||||
|
||||
/**
|
||||
* Evaluate the definite integral of Fourier sum given the sines and
|
||||
* cosines of the angles at the endpoints.
|
||||
*
|
||||
* @param[in] sinx sinσ<sub>1</sub>.
|
||||
* @param[in] cosx cosσ<sub>1</sub>.
|
||||
* @param[in] siny sinσ<sub>2</sub>.
|
||||
* @param[in] cosy cosσ<sub>2</sub>.
|
||||
* @param[in] F the array of Fourier coefficients.
|
||||
* @param[in] N the number of Fourier coefficients.
|
||||
* @return the value of the integral.
|
||||
*
|
||||
* The integral is evaluated between limits σ<sub>1</sub> and
|
||||
* σ<sub>2</sub>.
|
||||
**********************************************************************/
|
||||
static real GEOGRAPHICLIB_EXPORT integral(real sinx, real cosx,
|
||||
real siny, real cosy,
|
||||
const real F[], int N);
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_DST_HPP
|
||||
+521
@@ -0,0 +1,521 @@
|
||||
/**
|
||||
* \file Ellipsoid.hpp
|
||||
* \brief Header for GeographicLib::Ellipsoid class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2012-2023) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_ELLIPSOID_HPP)
|
||||
#define GEOGRAPHICLIB_ELLIPSOID_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/AuxLatitude.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Properties of an ellipsoid
|
||||
*
|
||||
* This class returns various properties of the ellipsoid and converts
|
||||
* between various types of latitudes. This is for the most part a thin
|
||||
* wrapper on top of the AuxLatitude class which is called with \e exact =
|
||||
* true so that the results are valid for arbitrary flattenings −100 <
|
||||
* \e f < 99/100 (i.e., 1/100 < b/a < 100).
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Ellipsoid.cpp
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT Ellipsoid {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
static const int numit_ = 10;
|
||||
real stol_;
|
||||
real _a, _f,_b, _e2, _e12, _n;
|
||||
AuxLatitude _aux;
|
||||
real _rm, _c2;
|
||||
|
||||
public:
|
||||
/** \name Constructor
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Constructor for an ellipsoid with
|
||||
*
|
||||
* @param[in] a equatorial radius (meters).
|
||||
* @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
|
||||
* Negative \e f gives a prolate ellipsoid.
|
||||
* @exception GeographicErr if \e a or (1 − \e f) \e a is not
|
||||
* positive.
|
||||
**********************************************************************/
|
||||
Ellipsoid(real a, real f);
|
||||
///@}
|
||||
|
||||
/** \name %Ellipsoid dimensions.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _a; }
|
||||
|
||||
/**
|
||||
* @return \e b the polar semiaxis (meters).
|
||||
**********************************************************************/
|
||||
Math::real PolarRadius() const { return _b; }
|
||||
|
||||
/**
|
||||
* @return \e L the distance between the equator and a pole along a
|
||||
* meridian (meters). For a sphere \e L = (π/2) \e a. The radius
|
||||
* of a sphere with the same meridian length is \e L / (π/2).
|
||||
**********************************************************************/
|
||||
Math::real QuarterMeridian() const;
|
||||
|
||||
/**
|
||||
* @return \e A the total area of the ellipsoid (meters<sup>2</sup>). For
|
||||
* a sphere \e A = 4π <i>a</i><sup>2</sup>. The radius of a sphere
|
||||
* with the same area is sqrt(\e A / (4π)).
|
||||
**********************************************************************/
|
||||
Math::real Area() const;
|
||||
|
||||
/**
|
||||
* @return \e V the total volume of the ellipsoid (meters<sup>3</sup>).
|
||||
* For a sphere \e V = (4π / 3) <i>a</i><sup>3</sup>. The radius of
|
||||
* a sphere with the same volume is cbrt(\e V / (4π/3)).
|
||||
**********************************************************************/
|
||||
Math::real Volume() const
|
||||
{ return (4 * Math::pi()) * Math::sq(_a) * _b / 3; }
|
||||
///@}
|
||||
|
||||
/** \name %Ellipsoid shape
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @return \e f = (\e a − \e b) / \e a, the flattening of the
|
||||
* ellipsoid. This is the value used in the constructor. This is zero,
|
||||
* positive, or negative for a sphere, oblate ellipsoid, or prolate
|
||||
* ellipsoid.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _f; }
|
||||
|
||||
/**
|
||||
* @return \e f ' = (\e a − \e b) / \e b, the second flattening of
|
||||
* the ellipsoid. This is zero, positive, or negative for a sphere,
|
||||
* oblate ellipsoid, or prolate ellipsoid.
|
||||
**********************************************************************/
|
||||
Math::real SecondFlattening() const { return _f / (1 - _f); }
|
||||
|
||||
/**
|
||||
* @return \e n = (\e a − \e b) / (\e a + \e b), the third flattening
|
||||
* of the ellipsoid. This is zero, positive, or negative for a sphere,
|
||||
* oblate ellipsoid, or prolate ellipsoid.
|
||||
**********************************************************************/
|
||||
Math::real ThirdFlattening() const { return _n; }
|
||||
|
||||
/**
|
||||
* @return <i>e</i><sup>2</sup> = (<i>a</i><sup>2</sup> −
|
||||
* <i>b</i><sup>2</sup>) / <i>a</i><sup>2</sup>, the eccentricity squared
|
||||
* of the ellipsoid. This is zero, positive, or negative for a sphere,
|
||||
* oblate ellipsoid, or prolate ellipsoid.
|
||||
**********************************************************************/
|
||||
Math::real EccentricitySq() const { return _e2; }
|
||||
|
||||
/**
|
||||
* @return <i>e'</i> <sup>2</sup> = (<i>a</i><sup>2</sup> −
|
||||
* <i>b</i><sup>2</sup>) / <i>b</i><sup>2</sup>, the second eccentricity
|
||||
* squared of the ellipsoid. This is zero, positive, or negative for a
|
||||
* sphere, oblate ellipsoid, or prolate ellipsoid.
|
||||
**********************************************************************/
|
||||
Math::real SecondEccentricitySq() const { return _e12; }
|
||||
|
||||
/**
|
||||
* @return <i>e''</i> <sup>2</sup> = (<i>a</i><sup>2</sup> −
|
||||
* <i>b</i><sup>2</sup>) / (<i>a</i><sup>2</sup> + <i>b</i><sup>2</sup>),
|
||||
* the third eccentricity squared of the ellipsoid. This is zero,
|
||||
* positive, or negative for a sphere, oblate ellipsoid, or prolate
|
||||
* ellipsoid.
|
||||
**********************************************************************/
|
||||
Math::real ThirdEccentricitySq() const { return _e2 / (2 - _e2); }
|
||||
///@}
|
||||
|
||||
/** \name Latitude conversion.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @param[in] phi the geographic latitude (degrees).
|
||||
* @return β the parametric latitude (degrees).
|
||||
*
|
||||
* The geographic latitude, φ, is the angle between the equatorial
|
||||
* plane and a vector normal to the surface of the ellipsoid.
|
||||
*
|
||||
* The parametric latitude (also called the reduced latitude), β,
|
||||
* allows the cartesian coordinated of a meridian to be expressed
|
||||
* conveniently in parametric form as
|
||||
* - \e R = \e a cos β
|
||||
* - \e Z = \e b sin β
|
||||
* .
|
||||
* where \e a and \e b are the equatorial radius and the polar semiaxis.
|
||||
* For a sphere β = φ.
|
||||
*
|
||||
* φ must lie in the range [−90°, 90°]; the
|
||||
* result is undefined if this condition does not hold. The returned value
|
||||
* β lies in [−90°, 90°].
|
||||
**********************************************************************/
|
||||
Math::real ParametricLatitude(real phi) const;
|
||||
|
||||
/**
|
||||
* @param[in] beta the parametric latitude (degrees).
|
||||
* @return φ the geographic latitude (degrees).
|
||||
*
|
||||
* β must lie in the range [−90°, 90°]; the
|
||||
* result is undefined if this condition does not hold. The returned value
|
||||
* φ lies in [−90°, 90°].
|
||||
**********************************************************************/
|
||||
Math::real InverseParametricLatitude(real beta) const;
|
||||
|
||||
/**
|
||||
* @param[in] phi the geographic latitude (degrees).
|
||||
* @return θ the geocentric latitude (degrees).
|
||||
*
|
||||
* The geocentric latitude, θ, is the angle between the equatorial
|
||||
* plane and a line between the center of the ellipsoid and a point on the
|
||||
* ellipsoid. For a sphere θ = φ.
|
||||
*
|
||||
* φ must lie in the range [−90°, 90°]; the
|
||||
* result is undefined if this condition does not hold. The returned value
|
||||
* θ lies in [−90°, 90°].
|
||||
**********************************************************************/
|
||||
Math::real GeocentricLatitude(real phi) const;
|
||||
|
||||
/**
|
||||
* @param[in] theta the geocentric latitude (degrees).
|
||||
* @return φ the geographic latitude (degrees).
|
||||
*
|
||||
* θ must lie in the range [−90°, 90°]; the
|
||||
* result is undefined if this condition does not hold. The returned value
|
||||
* φ lies in [−90°, 90°].
|
||||
**********************************************************************/
|
||||
Math::real InverseGeocentricLatitude(real theta) const;
|
||||
|
||||
/**
|
||||
* @param[in] phi the geographic latitude (degrees).
|
||||
* @return μ the rectifying latitude (degrees).
|
||||
*
|
||||
* The rectifying latitude, μ, has the property that the distance along
|
||||
* a meridian of the ellipsoid between two points with rectifying latitudes
|
||||
* μ<sub>1</sub> and μ<sub>2</sub> is equal to
|
||||
* (μ<sub>2</sub> - μ<sub>1</sub>) \e L / 90°,
|
||||
* where \e L = QuarterMeridian(). For a sphere μ = φ.
|
||||
*
|
||||
* φ must lie in the range [−90°, 90°]; the
|
||||
* result is undefined if this condition does not hold. The returned value
|
||||
* μ lies in [−90°, 90°].
|
||||
**********************************************************************/
|
||||
Math::real RectifyingLatitude(real phi) const;
|
||||
|
||||
/**
|
||||
* @param[in] mu the rectifying latitude (degrees).
|
||||
* @return φ the geographic latitude (degrees).
|
||||
*
|
||||
* μ must lie in the range [−90°, 90°]; the
|
||||
* result is undefined if this condition does not hold. The returned value
|
||||
* φ lies in [−90°, 90°].
|
||||
**********************************************************************/
|
||||
Math::real InverseRectifyingLatitude(real mu) const;
|
||||
|
||||
/**
|
||||
* @param[in] phi the geographic latitude (degrees).
|
||||
* @return ξ the authalic latitude (degrees).
|
||||
*
|
||||
* The authalic latitude, ξ, has the property that the area of the
|
||||
* ellipsoid between two circles with authalic latitudes
|
||||
* ξ<sub>1</sub> and ξ<sub>2</sub> is equal to (sin
|
||||
* ξ<sub>2</sub> - sin ξ<sub>1</sub>) \e A / 2, where \e A
|
||||
* = Area(). For a sphere ξ = φ.
|
||||
*
|
||||
* φ must lie in the range [−90°, 90°]; the
|
||||
* result is undefined if this condition does not hold. The returned value
|
||||
* ξ lies in [−90°, 90°].
|
||||
**********************************************************************/
|
||||
Math::real AuthalicLatitude(real phi) const;
|
||||
|
||||
/**
|
||||
* @param[in] xi the authalic latitude (degrees).
|
||||
* @return φ the geographic latitude (degrees).
|
||||
*
|
||||
* ξ must lie in the range [−90°, 90°]; the
|
||||
* result is undefined if this condition does not hold. The returned value
|
||||
* φ lies in [−90°, 90°].
|
||||
**********************************************************************/
|
||||
Math::real InverseAuthalicLatitude(real xi) const;
|
||||
|
||||
/**
|
||||
* @param[in] phi the geographic latitude (degrees).
|
||||
* @return χ the conformal latitude (degrees).
|
||||
*
|
||||
* The conformal latitude, χ, gives the mapping of the ellipsoid to a
|
||||
* sphere which which is conformal (angles are preserved) and in which the
|
||||
* equator of the ellipsoid maps to the equator of the sphere. For a
|
||||
* sphere χ = φ.
|
||||
*
|
||||
* φ must lie in the range [−90°, 90°]; the
|
||||
* result is undefined if this condition does not hold. The returned value
|
||||
* χ lies in [−90°, 90°].
|
||||
**********************************************************************/
|
||||
Math::real ConformalLatitude(real phi) const;
|
||||
|
||||
/**
|
||||
* @param[in] chi the conformal latitude (degrees).
|
||||
* @return φ the geographic latitude (degrees).
|
||||
*
|
||||
* χ must lie in the range [−90°, 90°]; the
|
||||
* result is undefined if this condition does not hold. The returned value
|
||||
* φ lies in [−90°, 90°].
|
||||
**********************************************************************/
|
||||
Math::real InverseConformalLatitude(real chi) const;
|
||||
|
||||
/**
|
||||
* @param[in] phi the geographic latitude (degrees).
|
||||
* @return ψ the isometric latitude (degrees).
|
||||
*
|
||||
* The isometric latitude gives the mapping of the ellipsoid to a plane
|
||||
* which which is conformal (angles are preserved) and in which the equator
|
||||
* of the ellipsoid maps to a straight line of constant scale; this mapping
|
||||
* defines the Mercator projection. For a sphere ψ =
|
||||
* sinh<sup>−1</sup> tan φ.
|
||||
*
|
||||
* φ must lie in the range [−90°, 90°]; the result is
|
||||
* undefined if this condition does not hold. The value returned for φ
|
||||
* = ±90° is some (positive or negative) large but finite value,
|
||||
* such that InverseIsometricLatitude returns the original value of φ.
|
||||
**********************************************************************/
|
||||
Math::real IsometricLatitude(real phi) const;
|
||||
|
||||
/**
|
||||
* @param[in] psi the isometric latitude (degrees).
|
||||
* @return φ the geographic latitude (degrees).
|
||||
*
|
||||
* The returned value φ lies in [−90°, 90°]. For a
|
||||
* sphere φ = tan<sup>−1</sup> sinh ψ.
|
||||
**********************************************************************/
|
||||
Math::real InverseIsometricLatitude(real psi) const;
|
||||
///@}
|
||||
|
||||
/** \name Other quantities.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @param[in] phi the geographic latitude (degrees).
|
||||
* @return \e R = \e a cos β the radius of a circle of latitude
|
||||
* φ (meters). \e R (π/180°) gives meters per degree
|
||||
* longitude measured along a circle of latitude.
|
||||
*
|
||||
* φ must lie in the range [−90°, 90°]; the
|
||||
* result is undefined if this condition does not hold.
|
||||
**********************************************************************/
|
||||
Math::real CircleRadius(real phi) const;
|
||||
|
||||
/**
|
||||
* @param[in] phi the geographic latitude (degrees).
|
||||
* @return \e Z = \e b sin β the distance of a circle of latitude
|
||||
* φ from the equator measured parallel to the ellipsoid axis
|
||||
* (meters).
|
||||
*
|
||||
* φ must lie in the range [−90°, 90°]; the
|
||||
* result is undefined if this condition does not hold.
|
||||
**********************************************************************/
|
||||
Math::real CircleHeight(real phi) const;
|
||||
|
||||
/**
|
||||
* @param[in] phi the geographic latitude (degrees).
|
||||
* @return \e s the distance along a meridian
|
||||
* between the equator and a point of latitude φ (meters). \e s is
|
||||
* given by \e s = μ \e L / 90°, where \e L =
|
||||
* QuarterMeridian()).
|
||||
*
|
||||
* φ must lie in the range [−90°, 90°]; the
|
||||
* result is undefined if this condition does not hold.
|
||||
**********************************************************************/
|
||||
Math::real MeridianDistance(real phi) const;
|
||||
|
||||
/**
|
||||
* @param[in] phi the geographic latitude (degrees).
|
||||
* @return ρ the meridional radius of curvature of the ellipsoid at
|
||||
* latitude φ (meters); this is the curvature of the meridian. \e
|
||||
* rho is given by ρ = (180°/π) d\e s / dφ,
|
||||
* where \e s = MeridianDistance(); thus ρ (π/180°)
|
||||
* gives meters per degree latitude measured along a meridian.
|
||||
*
|
||||
* φ must lie in the range [−90°, 90°]; the
|
||||
* result is undefined if this condition does not hold.
|
||||
**********************************************************************/
|
||||
Math::real MeridionalCurvatureRadius(real phi) const;
|
||||
|
||||
/**
|
||||
* @param[in] phi the geographic latitude (degrees).
|
||||
* @return ν the transverse radius of curvature of the ellipsoid at
|
||||
* latitude φ (meters); this is the curvature of a curve on the
|
||||
* ellipsoid which also lies in a plane perpendicular to the ellipsoid
|
||||
* and to the meridian. ν is related to \e R = CircleRadius() by \e
|
||||
* R = ν cos φ.
|
||||
*
|
||||
* φ must lie in the range [−90°, 90°]; the
|
||||
* result is undefined if this condition does not hold.
|
||||
**********************************************************************/
|
||||
Math::real TransverseCurvatureRadius(real phi) const;
|
||||
|
||||
/**
|
||||
* @param[in] phi the geographic latitude (degrees).
|
||||
* @param[in] azi the angle between the meridian and the normal section
|
||||
* (degrees).
|
||||
* @return the radius of curvature of the ellipsoid in the normal
|
||||
* section at latitude φ inclined at an angle \e azi to the
|
||||
* meridian (meters).
|
||||
*
|
||||
* φ must lie in the range [−90°, 90°]; the result is
|
||||
* undefined this condition does not hold.
|
||||
**********************************************************************/
|
||||
Math::real NormalCurvatureRadius(real phi, real azi) const;
|
||||
///@}
|
||||
|
||||
/** \name Eccentricity conversions.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @param[in] fp = \e f ' = (\e a − \e b) / \e b, the second
|
||||
* flattening.
|
||||
* @return \e f = (\e a − \e b) / \e a, the flattening.
|
||||
*
|
||||
* \e f ' should lie in (−1, ∞).
|
||||
* The returned value \e f lies in (−∞, 1).
|
||||
**********************************************************************/
|
||||
static Math::real SecondFlatteningToFlattening(real fp)
|
||||
{ return fp / (1 + fp); }
|
||||
|
||||
/**
|
||||
* @param[in] f = (\e a − \e b) / \e a, the flattening.
|
||||
* @return \e f ' = (\e a − \e b) / \e b, the second flattening.
|
||||
*
|
||||
* \e f should lie in (−∞, 1).
|
||||
* The returned value \e f ' lies in (−1, ∞).
|
||||
**********************************************************************/
|
||||
static Math::real FlatteningToSecondFlattening(real f)
|
||||
{ return f / (1 - f); }
|
||||
|
||||
/**
|
||||
* @param[in] n = (\e a − \e b) / (\e a + \e b), the third
|
||||
* flattening.
|
||||
* @return \e f = (\e a − \e b) / \e a, the flattening.
|
||||
*
|
||||
* \e n should lie in (−1, 1).
|
||||
* The returned value \e f lies in (−∞, 1).
|
||||
**********************************************************************/
|
||||
static Math::real ThirdFlatteningToFlattening(real n)
|
||||
{ return 2 * n / (1 + n); }
|
||||
|
||||
/**
|
||||
* @param[in] f = (\e a − \e b) / \e a, the flattening.
|
||||
* @return \e n = (\e a − \e b) / (\e a + \e b), the third
|
||||
* flattening.
|
||||
*
|
||||
* \e f should lie in (−∞, 1).
|
||||
* The returned value \e n lies in (−1, 1).
|
||||
**********************************************************************/
|
||||
static Math::real FlatteningToThirdFlattening(real f)
|
||||
{ return f / (2 - f); }
|
||||
|
||||
/**
|
||||
* @param[in] e2 = <i>e</i><sup>2</sup> = (<i>a</i><sup>2</sup> −
|
||||
* <i>b</i><sup>2</sup>) / <i>a</i><sup>2</sup>, the eccentricity
|
||||
* squared.
|
||||
* @return \e f = (\e a − \e b) / \e a, the flattening.
|
||||
*
|
||||
* <i>e</i><sup>2</sup> should lie in (−∞, 1).
|
||||
* The returned value \e f lies in (−∞, 1).
|
||||
**********************************************************************/
|
||||
static Math::real EccentricitySqToFlattening(real e2)
|
||||
{ using std::sqrt; return e2 / (sqrt(1 - e2) + 1); }
|
||||
|
||||
/**
|
||||
* @param[in] f = (\e a − \e b) / \e a, the flattening.
|
||||
* @return <i>e</i><sup>2</sup> = (<i>a</i><sup>2</sup> −
|
||||
* <i>b</i><sup>2</sup>) / <i>a</i><sup>2</sup>, the eccentricity
|
||||
* squared.
|
||||
*
|
||||
* \e f should lie in (−∞, 1).
|
||||
* The returned value <i>e</i><sup>2</sup> lies in (−∞, 1).
|
||||
**********************************************************************/
|
||||
static Math::real FlatteningToEccentricitySq(real f)
|
||||
{ return f * (2 - f); }
|
||||
|
||||
/**
|
||||
* @param[in] ep2 = <i>e'</i> <sup>2</sup> = (<i>a</i><sup>2</sup> −
|
||||
* <i>b</i><sup>2</sup>) / <i>b</i><sup>2</sup>, the second eccentricity
|
||||
* squared.
|
||||
* @return \e f = (\e a − \e b) / \e a, the flattening.
|
||||
*
|
||||
* <i>e'</i> <sup>2</sup> should lie in (−1, ∞).
|
||||
* The returned value \e f lies in (−∞, 1).
|
||||
**********************************************************************/
|
||||
static Math::real SecondEccentricitySqToFlattening(real ep2)
|
||||
{ using std::sqrt; return ep2 / (sqrt(1 + ep2) + 1 + ep2); }
|
||||
|
||||
/**
|
||||
* @param[in] f = (\e a − \e b) / \e a, the flattening.
|
||||
* @return <i>e'</i> <sup>2</sup> = (<i>a</i><sup>2</sup> −
|
||||
* <i>b</i><sup>2</sup>) / <i>b</i><sup>2</sup>, the second eccentricity
|
||||
* squared.
|
||||
*
|
||||
* \e f should lie in (−∞, 1).
|
||||
* The returned value <i>e'</i> <sup>2</sup> lies in (−1, ∞).
|
||||
**********************************************************************/
|
||||
static Math::real FlatteningToSecondEccentricitySq(real f)
|
||||
{ return f * (2 - f) / Math::sq(1 - f); }
|
||||
|
||||
/**
|
||||
* @param[in] epp2 = <i>e''</i> <sup>2</sup> = (<i>a</i><sup>2</sup>
|
||||
* − <i>b</i><sup>2</sup>) / (<i>a</i><sup>2</sup> +
|
||||
* <i>b</i><sup>2</sup>), the third eccentricity squared.
|
||||
* @return \e f = (\e a − \e b) / \e a, the flattening.
|
||||
*
|
||||
* <i>e''</i> <sup>2</sup> should lie in (−1, 1).
|
||||
* The returned value \e f lies in (−∞, 1).
|
||||
**********************************************************************/
|
||||
static Math::real ThirdEccentricitySqToFlattening(real epp2) {
|
||||
using std::sqrt;
|
||||
return 2 * epp2 / (sqrt((1 - epp2) * (1 + epp2)) + 1 + epp2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param[in] f = (\e a − \e b) / \e a, the flattening.
|
||||
* @return <i>e''</i> <sup>2</sup> = (<i>a</i><sup>2</sup> −
|
||||
* <i>b</i><sup>2</sup>) / (<i>a</i><sup>2</sup> + <i>b</i><sup>2</sup>),
|
||||
* the third eccentricity squared.
|
||||
*
|
||||
* \e f should lie in (−∞, 1).
|
||||
* The returned value <i>e''</i> <sup>2</sup> lies in (−1, 1).
|
||||
**********************************************************************/
|
||||
static Math::real FlatteningToThirdEccentricitySq(real f)
|
||||
{ return f * (2 - f) / (1 + Math::sq(1 - f)); }
|
||||
|
||||
///@}
|
||||
|
||||
/**
|
||||
* A global instantiation of Ellipsoid with the parameters for the WGS84
|
||||
* ellipsoid.
|
||||
**********************************************************************/
|
||||
static const Ellipsoid& WGS84();
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_ELLIPSOID_HPP
|
||||
+737
@@ -0,0 +1,737 @@
|
||||
/**
|
||||
* \file EllipticFunction.hpp
|
||||
* \brief Header for GeographicLib::EllipticFunction class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2008-2024) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_ELLIPTICFUNCTION_HPP)
|
||||
#define GEOGRAPHICLIB_ELLIPTICFUNCTION_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Elliptic integrals and functions
|
||||
*
|
||||
* This provides the elliptic functions and integrals needed for Ellipsoid,
|
||||
* GeodesicExact, and TransverseMercatorExact. Two categories of function
|
||||
* are provided:
|
||||
* - \e static functions to compute symmetric elliptic integrals
|
||||
* (https://dlmf.nist.gov/19.16.i)
|
||||
* - \e member functions to compute Legrendre's elliptic
|
||||
* integrals (https://dlmf.nist.gov/19.2.ii) and the
|
||||
* Jacobi elliptic functions (https://dlmf.nist.gov/22.2).
|
||||
* .
|
||||
* In the latter case, an object is constructed giving the modulus \e k (and
|
||||
* optionally the parameter α<sup>2</sup>). The modulus is always
|
||||
* passed as its square <i>k</i><sup>2</sup> which allows \e k to be pure
|
||||
* imaginary (<i>k</i><sup>2</sup> < 0). (Confusingly, Abramowitz and
|
||||
* Stegun call \e m = <i>k</i><sup>2</sup> the "parameter" and \e n =
|
||||
* α<sup>2</sup> the "characteristic".)
|
||||
*
|
||||
* In geodesic applications, it is convenient to separate the incomplete
|
||||
* integrals into secular and periodic components, e.g.,
|
||||
* \f[
|
||||
* E(\phi, k) = (2 E(k) / \pi) [ \phi + \delta E(\phi, k) ]
|
||||
* \f]
|
||||
* where δ\e E(φ, \e k) is an odd periodic function with period
|
||||
* π.
|
||||
*
|
||||
* The computation of the elliptic integrals uses the algorithms given in
|
||||
* - B. C. Carlson,
|
||||
* <a href="https://doi.org/10.1007/BF02198293"> Computation of real or
|
||||
* complex elliptic integrals</a>, Numerical Algorithms 10, 13--26 (1995);
|
||||
* <a href="https://arxiv.org/abs/math/9409227">preprint</a>.
|
||||
* .
|
||||
* with the additional optimizations given in https://dlmf.nist.gov/19.36.i.
|
||||
* The computation of the Jacobi elliptic functions uses the algorithm given
|
||||
* in
|
||||
* - R. Bulirsch,
|
||||
* <a href="https://doi.org/10.1007/BF01397975"> Numerical Calculation of
|
||||
* Elliptic Integrals and Elliptic Functions</a>, Numericshe Mathematik 7,
|
||||
* 78--90 (1965).
|
||||
* .
|
||||
* The notation follows https://dlmf.nist.gov/19 and https://dlmf.nist.gov/22
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-EllipticFunction.cpp
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT EllipticFunction {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
|
||||
enum { num_ = 25 }; // Max depth required for sncndn; probably 5 is enough.
|
||||
real _k2, _kp2, _alpha2, _alphap2, _eps;
|
||||
real _kKc, _eEc, _dDc, _pPic, _gGc, _hHc;
|
||||
public:
|
||||
/** \name Constructor
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Constructor specifying the modulus and parameter.
|
||||
*
|
||||
* @param[in] k2 the square of the modulus <i>k</i><sup>2</sup>.
|
||||
* <i>k</i><sup>2</sup> must lie in (−∞, 1].
|
||||
* @param[in] alpha2 the parameter α<sup>2</sup>.
|
||||
* α<sup>2</sup> must lie in (−∞, 1].
|
||||
* @exception GeographicErr if \e k2 or \e alpha2 is out of its legal
|
||||
* range.
|
||||
*
|
||||
* If only elliptic integrals of the first and second kinds are needed,
|
||||
* then set α<sup>2</sup> = 0 (the default value); in this case, we
|
||||
* have Π(φ, 0, \e k) = \e F(φ, \e k), \e G(φ, 0, \e k) = \e
|
||||
* E(φ, \e k), and \e H(φ, 0, \e k) = \e F(φ, \e k) - \e
|
||||
* D(φ, \e k).
|
||||
**********************************************************************/
|
||||
EllipticFunction(real k2 = 0, real alpha2 = 0)
|
||||
{ Reset(k2, alpha2); }
|
||||
|
||||
/**
|
||||
* Constructor specifying the modulus and parameter and their complements.
|
||||
*
|
||||
* @param[in] k2 the square of the modulus <i>k</i><sup>2</sup>.
|
||||
* <i>k</i><sup>2</sup> must lie in (−∞, 1].
|
||||
* @param[in] alpha2 the parameter α<sup>2</sup>.
|
||||
* α<sup>2</sup> must lie in (−∞, 1].
|
||||
* @param[in] kp2 the complementary modulus squared <i>k'</i><sup>2</sup> =
|
||||
* 1 − <i>k</i><sup>2</sup>. This must lie in [0, ∞).
|
||||
* @param[in] alphap2 the complementary parameter α'<sup>2</sup> = 1
|
||||
* − α<sup>2</sup>. This must lie in [0, ∞).
|
||||
* @exception GeographicErr if \e k2, \e alpha2, \e kp2, or \e alphap2 is
|
||||
* out of its legal range.
|
||||
*
|
||||
* The arguments must satisfy \e k2 + \e kp2 = 1 and \e alpha2 + \e alphap2
|
||||
* = 1. (No checking is done that these conditions are met.) This
|
||||
* constructor is provided to enable accuracy to be maintained, e.g., when
|
||||
* \e k is very close to unity.
|
||||
**********************************************************************/
|
||||
EllipticFunction(real k2, real alpha2, real kp2, real alphap2)
|
||||
{ Reset(k2, alpha2, kp2, alphap2); }
|
||||
|
||||
/**
|
||||
* Reset the modulus and parameter.
|
||||
*
|
||||
* @param[in] k2 the new value of square of the modulus
|
||||
* <i>k</i><sup>2</sup> which must lie in (−∞, ].
|
||||
* done.)
|
||||
* @param[in] alpha2 the new value of parameter α<sup>2</sup>.
|
||||
* α<sup>2</sup> must lie in (−∞, 1].
|
||||
* @exception GeographicErr if \e k2 or \e alpha2 is out of its legal
|
||||
* range.
|
||||
**********************************************************************/
|
||||
void Reset(real k2 = 0, real alpha2 = 0)
|
||||
{ Reset(k2, alpha2, 1 - k2, 1 - alpha2); }
|
||||
|
||||
/**
|
||||
* Reset the modulus and parameter supplying also their complements.
|
||||
*
|
||||
* @param[in] k2 the square of the modulus <i>k</i><sup>2</sup>.
|
||||
* <i>k</i><sup>2</sup> must lie in (−∞, 1].
|
||||
* @param[in] alpha2 the parameter α<sup>2</sup>.
|
||||
* α<sup>2</sup> must lie in (−∞, 1].
|
||||
* @param[in] kp2 the complementary modulus squared <i>k'</i><sup>2</sup> =
|
||||
* 1 − <i>k</i><sup>2</sup>. This must lie in [0, ∞).
|
||||
* @param[in] alphap2 the complementary parameter α'<sup>2</sup> = 1
|
||||
* − α<sup>2</sup>. This must lie in [0, ∞).
|
||||
* @exception GeographicErr if \e k2, \e alpha2, \e kp2, or \e alphap2 is
|
||||
* out of its legal range.
|
||||
*
|
||||
* The arguments must satisfy \e k2 + \e kp2 = 1 and \e alpha2 + \e alphap2
|
||||
* = 1. (No checking is done that these conditions are met.) This
|
||||
* constructor is provided to enable accuracy to be maintained, e.g., when
|
||||
* is very small.
|
||||
**********************************************************************/
|
||||
void Reset(real k2, real alpha2, real kp2, real alphap2);
|
||||
|
||||
///@}
|
||||
|
||||
/** \name Inspector functions.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return the square of the modulus <i>k</i><sup>2</sup>.
|
||||
**********************************************************************/
|
||||
Math::real k2() const { return _k2; }
|
||||
|
||||
/**
|
||||
* @return the square of the complementary modulus <i>k'</i><sup>2</sup> =
|
||||
* 1 − <i>k</i><sup>2</sup>.
|
||||
**********************************************************************/
|
||||
Math::real kp2() const { return _kp2; }
|
||||
|
||||
/**
|
||||
* @return the parameter α<sup>2</sup>.
|
||||
**********************************************************************/
|
||||
Math::real alpha2() const { return _alpha2; }
|
||||
|
||||
/**
|
||||
* @return the complementary parameter α'<sup>2</sup> = 1 −
|
||||
* α<sup>2</sup>.
|
||||
**********************************************************************/
|
||||
Math::real alphap2() const { return _alphap2; }
|
||||
///@}
|
||||
|
||||
/** \name Complete elliptic integrals.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* The complete integral of the first kind.
|
||||
*
|
||||
* @return \e K(\e k).
|
||||
*
|
||||
* \e K(\e k) is defined in https://dlmf.nist.gov/19.2.E4
|
||||
* \f[
|
||||
* K(k) = \int_0^{\pi/2} \frac1{\sqrt{1-k^2\sin^2\phi}}\,d\phi.
|
||||
* \f]
|
||||
**********************************************************************/
|
||||
Math::real K() const { return _kKc; }
|
||||
|
||||
/**
|
||||
* The complete integral of the second kind.
|
||||
*
|
||||
* @return \e E(\e k).
|
||||
*
|
||||
* \e E(\e k) is defined in https://dlmf.nist.gov/19.2.E5
|
||||
* \f[
|
||||
* E(k) = \int_0^{\pi/2} \sqrt{1-k^2\sin^2\phi}\,d\phi.
|
||||
* \f]
|
||||
**********************************************************************/
|
||||
Math::real E() const { return _eEc; }
|
||||
|
||||
/**
|
||||
* Jahnke's complete integral.
|
||||
*
|
||||
* @return \e D(\e k).
|
||||
*
|
||||
* \e D(\e k) is defined in https://dlmf.nist.gov/19.2.E6
|
||||
* \f[
|
||||
* D(k) =
|
||||
* \int_0^{\pi/2} \frac{\sin^2\phi}{\sqrt{1-k^2\sin^2\phi}}\,d\phi.
|
||||
* \f]
|
||||
**********************************************************************/
|
||||
Math::real D() const { return _dDc; }
|
||||
|
||||
/**
|
||||
* The difference between the complete integrals of the first and second
|
||||
* kinds.
|
||||
*
|
||||
* @return \e K(\e k) − \e E(\e k).
|
||||
**********************************************************************/
|
||||
Math::real KE() const { return _k2 * _dDc; }
|
||||
|
||||
/**
|
||||
* The complete integral of the third kind.
|
||||
*
|
||||
* @return Π(α<sup>2</sup>, \e k).
|
||||
*
|
||||
* Π(α<sup>2</sup>, \e k) is defined in
|
||||
* https://dlmf.nist.gov/19.2.E7
|
||||
* \f[
|
||||
* \Pi(\alpha^2, k) = \int_0^{\pi/2}
|
||||
* \frac1{\sqrt{1-k^2\sin^2\phi}(1 - \alpha^2\sin^2\phi)}\,d\phi.
|
||||
* \f]
|
||||
**********************************************************************/
|
||||
Math::real Pi() const { return _pPic; }
|
||||
|
||||
/**
|
||||
* Legendre's complete geodesic longitude integral.
|
||||
*
|
||||
* @return \e G(α<sup>2</sup>, \e k).
|
||||
*
|
||||
* \e G(α<sup>2</sup>, \e k) is given by
|
||||
* \f[
|
||||
* G(\alpha^2, k) = \int_0^{\pi/2}
|
||||
* \frac{\sqrt{1-k^2\sin^2\phi}}{1 - \alpha^2\sin^2\phi}\,d\phi.
|
||||
* \f]
|
||||
**********************************************************************/
|
||||
Math::real G() const { return _gGc; }
|
||||
|
||||
/**
|
||||
* Cayley's complete geodesic longitude difference integral.
|
||||
*
|
||||
* @return \e H(α<sup>2</sup>, \e k).
|
||||
*
|
||||
* \e H(α<sup>2</sup>, \e k) is given by
|
||||
* \f[
|
||||
* H(\alpha^2, k) = \int_0^{\pi/2}
|
||||
* \frac{\cos^2\phi}{(1-\alpha^2\sin^2\phi)\sqrt{1-k^2\sin^2\phi}}
|
||||
* \,d\phi.
|
||||
* \f]
|
||||
**********************************************************************/
|
||||
Math::real H() const { return _hHc; }
|
||||
///@}
|
||||
|
||||
/** \name Incomplete elliptic integrals.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* The incomplete integral of the first kind.
|
||||
*
|
||||
* @param[in] phi
|
||||
* @return \e F(φ, \e k).
|
||||
*
|
||||
* \e F(φ, \e k) is defined in https://dlmf.nist.gov/19.2.E4
|
||||
* \f[
|
||||
* F(\phi, k) = \int_0^\phi \frac1{\sqrt{1-k^2\sin^2\theta}}\,d\theta.
|
||||
* \f]
|
||||
**********************************************************************/
|
||||
Math::real F(real phi) const;
|
||||
|
||||
/**
|
||||
* The incomplete integral of the second kind.
|
||||
*
|
||||
* @param[in] phi
|
||||
* @return \e E(φ, \e k).
|
||||
*
|
||||
* \e E(φ, \e k) is defined in https://dlmf.nist.gov/19.2.E5
|
||||
* \f[
|
||||
* E(\phi, k) = \int_0^\phi \sqrt{1-k^2\sin^2\theta}\,d\theta.
|
||||
* \f]
|
||||
**********************************************************************/
|
||||
Math::real E(real phi) const;
|
||||
|
||||
/**
|
||||
* The incomplete integral of the second kind with the argument given in
|
||||
* degrees.
|
||||
*
|
||||
* @param[in] ang in <i>degrees</i>.
|
||||
* @return \e E(π <i>ang</i>/180, \e k).
|
||||
**********************************************************************/
|
||||
Math::real Ed(real ang) const;
|
||||
|
||||
/**
|
||||
* The inverse of the incomplete integral of the second kind.
|
||||
*
|
||||
* @param[in] x
|
||||
* @return φ = <i>E</i><sup>−1</sup>(\e x, \e k); i.e., the
|
||||
* solution of such that \e E(φ, \e k) = \e x.
|
||||
**********************************************************************/
|
||||
Math::real Einv(real x) const;
|
||||
|
||||
/**
|
||||
* The incomplete integral of the third kind.
|
||||
*
|
||||
* @param[in] phi
|
||||
* @return Π(φ, α<sup>2</sup>, \e k).
|
||||
*
|
||||
* Π(φ, α<sup>2</sup>, \e k) is defined in
|
||||
* https://dlmf.nist.gov/19.2.E7
|
||||
* \f[
|
||||
* \Pi(\phi, \alpha^2, k) = \int_0^\phi
|
||||
* \frac1{\sqrt{1-k^2\sin^2\theta}(1 - \alpha^2\sin^2\theta)}\,d\theta.
|
||||
* \f]
|
||||
**********************************************************************/
|
||||
Math::real Pi(real phi) const;
|
||||
|
||||
/**
|
||||
* Jahnke's incomplete elliptic integral.
|
||||
*
|
||||
* @param[in] phi
|
||||
* @return \e D(φ, \e k).
|
||||
*
|
||||
* \e D(φ, \e k) is defined in https://dlmf.nist.gov/19.2.E4
|
||||
* \f[
|
||||
* D(\phi, k) = \int_0^\phi
|
||||
* \frac{\sin^2\theta}{\sqrt{1-k^2\sin^2\theta}}\,d\theta.
|
||||
* \f]
|
||||
**********************************************************************/
|
||||
Math::real D(real phi) const;
|
||||
|
||||
/**
|
||||
* Legendre's geodesic longitude integral.
|
||||
*
|
||||
* @param[in] phi
|
||||
* @return \e G(φ, α<sup>2</sup>, \e k).
|
||||
*
|
||||
* \e G(φ, α<sup>2</sup>, \e k) is defined by
|
||||
* \f[
|
||||
* \begin{align}
|
||||
* G(\phi, \alpha^2, k) &=
|
||||
* \frac{k^2}{\alpha^2} F(\phi, k) +
|
||||
* \biggl(1 - \frac{k^2}{\alpha^2}\biggr) \Pi(\phi, \alpha^2, k) \\
|
||||
* &= \int_0^\phi
|
||||
* \frac{\sqrt{1-k^2\sin^2\theta}}{1 - \alpha^2\sin^2\theta}\,d\theta.
|
||||
* \end{align}
|
||||
* \f]
|
||||
*
|
||||
* Legendre expresses the longitude of a point on the geodesic in terms of
|
||||
* this combination of elliptic integrals in Exercices de Calcul
|
||||
* Intégral, Vol. 1 (1811), p. 181,
|
||||
* https://books.google.com/books?id=riIOAAAAQAAJ&pg=PA181.
|
||||
*
|
||||
* See \ref geodellip for the expression for the longitude in terms of this
|
||||
* function.
|
||||
**********************************************************************/
|
||||
Math::real G(real phi) const;
|
||||
|
||||
/**
|
||||
* Cayley's geodesic longitude difference integral.
|
||||
*
|
||||
* @param[in] phi
|
||||
* @return \e H(φ, α<sup>2</sup>, \e k).
|
||||
*
|
||||
* \e H(φ, α<sup>2</sup>, \e k) is defined by
|
||||
* \f[
|
||||
* \begin{align}
|
||||
* H(\phi, \alpha^2, k) &=
|
||||
* \frac1{\alpha^2} F(\phi, k) +
|
||||
* \biggl(1 - \frac1{\alpha^2}\biggr) \Pi(\phi, \alpha^2, k) \\
|
||||
* &= \int_0^\phi
|
||||
* \frac{\cos^2\theta}
|
||||
* {(1-\alpha^2\sin^2\theta)\sqrt{1-k^2\sin^2\theta}}
|
||||
* \,d\theta.
|
||||
* \end{align}
|
||||
* \f]
|
||||
*
|
||||
* Cayley expresses the longitude difference of a point on the geodesic in
|
||||
* terms of this combination of elliptic integrals in Phil. Mag. <b>40</b>
|
||||
* (1870), p. 333, https://books.google.com/books?id=Zk0wAAAAIAAJ&pg=PA333.
|
||||
*
|
||||
* See \ref geodellip for the expression for the longitude in terms of this
|
||||
* function.
|
||||
**********************************************************************/
|
||||
Math::real H(real phi) const;
|
||||
///@}
|
||||
|
||||
/** \name Incomplete integrals in terms of Jacobi elliptic functions.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* The incomplete integral of the first kind in terms of Jacobi elliptic
|
||||
* functions.
|
||||
*
|
||||
* @param[in] sn = sinφ.
|
||||
* @param[in] cn = cosφ.
|
||||
* @param[in] dn = sqrt(1 − <i>k</i><sup>2</sup>
|
||||
* sin<sup>2</sup>φ).
|
||||
* @return \e F(φ, \e k) as though φ ∈ (−π, π].
|
||||
**********************************************************************/
|
||||
Math::real F(real sn, real cn, real dn) const;
|
||||
|
||||
/**
|
||||
* The incomplete integral of the second kind in terms of Jacobi elliptic
|
||||
* functions.
|
||||
*
|
||||
* @param[in] sn = sinφ.
|
||||
* @param[in] cn = cosφ.
|
||||
* @param[in] dn = sqrt(1 − <i>k</i><sup>2</sup>
|
||||
* sin<sup>2</sup>φ).
|
||||
* @return \e E(φ, \e k) as though φ ∈ (−π, π].
|
||||
**********************************************************************/
|
||||
Math::real E(real sn, real cn, real dn) const;
|
||||
|
||||
/**
|
||||
* The incomplete integral of the third kind in terms of Jacobi elliptic
|
||||
* functions.
|
||||
*
|
||||
* @param[in] sn = sinφ.
|
||||
* @param[in] cn = cosφ.
|
||||
* @param[in] dn = sqrt(1 − <i>k</i><sup>2</sup>
|
||||
* sin<sup>2</sup>φ).
|
||||
* @return Π(φ, α<sup>2</sup>, \e k) as though φ ∈
|
||||
* (−π, π].
|
||||
**********************************************************************/
|
||||
Math::real Pi(real sn, real cn, real dn) const;
|
||||
|
||||
/**
|
||||
* Jahnke's incomplete elliptic integral in terms of Jacobi elliptic
|
||||
* functions.
|
||||
*
|
||||
* @param[in] sn = sinφ.
|
||||
* @param[in] cn = cosφ.
|
||||
* @param[in] dn = sqrt(1 − <i>k</i><sup>2</sup>
|
||||
* sin<sup>2</sup>φ).
|
||||
* @return \e D(φ, \e k) as though φ ∈ (−π, π].
|
||||
**********************************************************************/
|
||||
Math::real D(real sn, real cn, real dn) const;
|
||||
|
||||
/**
|
||||
* Legendre's geodesic longitude integral in terms of Jacobi elliptic
|
||||
* functions.
|
||||
*
|
||||
* @param[in] sn = sinφ.
|
||||
* @param[in] cn = cosφ.
|
||||
* @param[in] dn = sqrt(1 − <i>k</i><sup>2</sup>
|
||||
* sin<sup>2</sup>φ).
|
||||
* @return \e G(φ, α<sup>2</sup>, \e k) as though φ ∈
|
||||
* (−π, π].
|
||||
**********************************************************************/
|
||||
Math::real G(real sn, real cn, real dn) const;
|
||||
|
||||
/**
|
||||
* Cayley's geodesic longitude difference integral in terms of Jacobi
|
||||
* elliptic functions.
|
||||
*
|
||||
* @param[in] sn = sinφ.
|
||||
* @param[in] cn = cosφ.
|
||||
* @param[in] dn = sqrt(1 − <i>k</i><sup>2</sup>
|
||||
* sin<sup>2</sup>φ).
|
||||
* @return \e H(φ, α<sup>2</sup>, \e k) as though φ ∈
|
||||
* (−π, π].
|
||||
**********************************************************************/
|
||||
Math::real H(real sn, real cn, real dn) const;
|
||||
///@}
|
||||
|
||||
/** \name Periodic versions of incomplete elliptic integrals.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* The periodic incomplete integral of the first kind.
|
||||
*
|
||||
* @param[in] sn = sinφ.
|
||||
* @param[in] cn = cosφ.
|
||||
* @param[in] dn = sqrt(1 − <i>k</i><sup>2</sup>
|
||||
* sin<sup>2</sup>φ).
|
||||
* @return the periodic function π \e F(φ, \e k) / (2 \e K(\e k)) -
|
||||
* φ.
|
||||
**********************************************************************/
|
||||
Math::real deltaF(real sn, real cn, real dn) const;
|
||||
|
||||
/**
|
||||
* The periodic incomplete integral of the second kind.
|
||||
*
|
||||
* @param[in] sn = sinφ.
|
||||
* @param[in] cn = cosφ.
|
||||
* @param[in] dn = sqrt(1 − <i>k</i><sup>2</sup>
|
||||
* sin<sup>2</sup>φ).
|
||||
* @return the periodic function π \e E(φ, \e k) / (2 \e E(\e k)) -
|
||||
* φ.
|
||||
**********************************************************************/
|
||||
Math::real deltaE(real sn, real cn, real dn) const;
|
||||
|
||||
/**
|
||||
* The periodic inverse of the incomplete integral of the second kind.
|
||||
*
|
||||
* @param[in] stau = sinτ.
|
||||
* @param[in] ctau = sinτ.
|
||||
* @return the periodic function <i>E</i><sup>−1</sup>(τ (2 \e
|
||||
* E(\e k)/π), \e k) - τ.
|
||||
**********************************************************************/
|
||||
Math::real deltaEinv(real stau, real ctau) const;
|
||||
|
||||
/**
|
||||
* The periodic incomplete integral of the third kind.
|
||||
*
|
||||
* @param[in] sn = sinφ.
|
||||
* @param[in] cn = cosφ.
|
||||
* @param[in] dn = sqrt(1 − <i>k</i><sup>2</sup>
|
||||
* sin<sup>2</sup>φ).
|
||||
* @return the periodic function π Π(φ, α<sup>2</sup>,
|
||||
* \e k) / (2 Π(α<sup>2</sup>, \e k)) - φ.
|
||||
**********************************************************************/
|
||||
Math::real deltaPi(real sn, real cn, real dn) const;
|
||||
|
||||
/**
|
||||
* The periodic Jahnke's incomplete elliptic integral.
|
||||
*
|
||||
* @param[in] sn = sinφ.
|
||||
* @param[in] cn = cosφ.
|
||||
* @param[in] dn = sqrt(1 − <i>k</i><sup>2</sup>
|
||||
* sin<sup>2</sup>φ).
|
||||
* @return the periodic function π \e D(φ, \e k) / (2 \e D(\e k)) -
|
||||
* φ.
|
||||
**********************************************************************/
|
||||
Math::real deltaD(real sn, real cn, real dn) const;
|
||||
|
||||
/**
|
||||
* Legendre's periodic geodesic longitude integral.
|
||||
*
|
||||
* @param[in] sn = sinφ.
|
||||
* @param[in] cn = cosφ.
|
||||
* @param[in] dn = sqrt(1 − <i>k</i><sup>2</sup>
|
||||
* sin<sup>2</sup>φ).
|
||||
* @return the periodic function π \e G(φ, \e k) / (2 \e G(\e k)) -
|
||||
* φ.
|
||||
**********************************************************************/
|
||||
Math::real deltaG(real sn, real cn, real dn) const;
|
||||
|
||||
/**
|
||||
* Cayley's periodic geodesic longitude difference integral.
|
||||
*
|
||||
* @param[in] sn = sinφ.
|
||||
* @param[in] cn = cosφ.
|
||||
* @param[in] dn = sqrt(1 − <i>k</i><sup>2</sup>
|
||||
* sin<sup>2</sup>φ).
|
||||
* @return the periodic function π \e H(φ, \e k) / (2 \e H(\e k)) -
|
||||
* φ.
|
||||
**********************************************************************/
|
||||
Math::real deltaH(real sn, real cn, real dn) const;
|
||||
///@}
|
||||
|
||||
/** \name Elliptic functions.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* The Jacobi amplitude function.
|
||||
*
|
||||
* @param[in] x the argument.
|
||||
* @return the value of am(\e x, \e k)
|
||||
**********************************************************************/
|
||||
Math::real am(real x) const;
|
||||
|
||||
/**
|
||||
* The Jacobi amplitude function and associated elliptic functions.
|
||||
*
|
||||
* @param[in] x the argument.
|
||||
* @param[out] sn sn(\e x, \e k).
|
||||
* @param[out] cn cn(\e x, \e k).
|
||||
* @param[out] dn dn(\e x, \e k).
|
||||
* @return the value of am(\e x, \e k)
|
||||
**********************************************************************/
|
||||
Math::real am(real x, real& sn, real& cn, real& dn) const;
|
||||
|
||||
/**
|
||||
* The Jacobi elliptic functions.
|
||||
*
|
||||
* @param[in] x the argument.
|
||||
* @param[out] sn sn(\e x, \e k).
|
||||
* @param[out] cn cn(\e x, \e k).
|
||||
* @param[out] dn dn(\e x, \e k).
|
||||
*
|
||||
* For this routine <i>k</i> is restricted to the interval [0, 1].
|
||||
**********************************************************************/
|
||||
void sncndn(real x, real& sn, real& cn, real& dn) const;
|
||||
|
||||
/**
|
||||
* The Δ amplitude function.
|
||||
*
|
||||
* @param[in] sn sinφ.
|
||||
* @param[in] cn cosφ.
|
||||
* @return Δ = sqrt(1 − <i>k</i><sup>2</sup>
|
||||
* sin<sup>2</sup>φ).
|
||||
**********************************************************************/
|
||||
Math::real Delta(real sn, real cn) const {
|
||||
using std::sqrt;
|
||||
return sqrt(_k2 < 0 ? 1 - _k2 * sn*sn : _kp2 + _k2 * cn*cn);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name Symmetric elliptic integrals.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Symmetric integral of the first kind <i>R</i><sub><i>F</i></sub>.
|
||||
*
|
||||
* @param[in] x
|
||||
* @param[in] y
|
||||
* @param[in] z
|
||||
* @return <i>R</i><sub><i>F</i></sub>(\e x, \e y, \e z).
|
||||
*
|
||||
* <i>R</i><sub><i>F</i></sub> is defined in https://dlmf.nist.gov/19.16.E1
|
||||
* \f[ R_F(x, y, z) = \frac12
|
||||
* \int_0^\infty\frac1{\sqrt{(t + x) (t + y) (t + z)}}\, dt, \f]
|
||||
* where at most one of arguments, \e x, \e y, \e z, can be zero and those
|
||||
* arguments that are nonzero must be positive. If one of the arguments is
|
||||
* zero, it is more efficient to call the two-argument version of this
|
||||
* function with the non-zero arguments.
|
||||
**********************************************************************/
|
||||
static real RF(real x, real y, real z);
|
||||
|
||||
/**
|
||||
* Complete symmetric integral of the first kind,
|
||||
* <i>R</i><sub><i>F</i></sub> with one argument zero.
|
||||
*
|
||||
* @param[in] x
|
||||
* @param[in] y
|
||||
* @return <i>R</i><sub><i>F</i></sub>(\e x, \e y, 0).
|
||||
*
|
||||
* The arguments \e x and \e y must be positive.
|
||||
**********************************************************************/
|
||||
static real RF(real x, real y);
|
||||
|
||||
/**
|
||||
* Degenerate symmetric integral of the first kind
|
||||
* <i>R</i><sub><i>C</i></sub>.
|
||||
*
|
||||
* @param[in] x
|
||||
* @param[in] y
|
||||
* @return <i>R</i><sub><i>C</i></sub>(\e x, \e y) =
|
||||
* <i>R</i><sub><i>F</i></sub>(\e x, \e y, \e y).
|
||||
*
|
||||
* <i>R</i><sub><i>C</i></sub> is defined in https://dlmf.nist.gov/19.2.E17
|
||||
* \f[ R_C(x, y) = \frac12
|
||||
* \int_0^\infty\frac1{\sqrt{t + x}(t + y)}\,dt, \f]
|
||||
* where \e x ≥ 0 and \e y > 0.
|
||||
**********************************************************************/
|
||||
static real RC(real x, real y);
|
||||
|
||||
/**
|
||||
* Symmetric integral of the second kind <i>R</i><sub><i>G</i></sub>.
|
||||
*
|
||||
* @param[in] x
|
||||
* @param[in] y
|
||||
* @param[in] z
|
||||
* @return <i>R</i><sub><i>G</i></sub>(\e x, \e y, \e z).
|
||||
*
|
||||
* <i>R</i><sub><i>G</i></sub> is defined in Carlson, eq 1.5
|
||||
* \f[ R_G(x, y, z) = \frac14
|
||||
* \int_0^\infty[(t + x) (t + y) (t + z)]^{-1/2}
|
||||
* \biggl(
|
||||
* \frac x{t + x} + \frac y{t + y} + \frac z{t + z}
|
||||
* \biggr)t\,dt, \f]
|
||||
* where at most one of arguments, \e x, \e y, \e z, can be zero and those
|
||||
* arguments that are nonzero must be positive. See also
|
||||
* https://dlmf.nist.gov/19.23.E6_5. If one of the arguments is zero, it
|
||||
* is more efficient to call the two-argument version of this function with
|
||||
* the non-zero arguments.
|
||||
**********************************************************************/
|
||||
static real RG(real x, real y, real z);
|
||||
|
||||
/**
|
||||
* Complete symmetric integral of the second kind,
|
||||
* <i>R</i><sub><i>G</i></sub> with one argument zero.
|
||||
*
|
||||
* @param[in] x
|
||||
* @param[in] y
|
||||
* @return <i>R</i><sub><i>G</i></sub>(\e x, \e y, 0).
|
||||
*
|
||||
* The arguments \e x and \e y must be positive.
|
||||
**********************************************************************/
|
||||
static real RG(real x, real y);
|
||||
|
||||
/**
|
||||
* Symmetric integral of the third kind <i>R</i><sub><i>J</i></sub>.
|
||||
*
|
||||
* @param[in] x
|
||||
* @param[in] y
|
||||
* @param[in] z
|
||||
* @param[in] p
|
||||
* @return <i>R</i><sub><i>J</i></sub>(\e x, \e y, \e z, \e p).
|
||||
*
|
||||
* <i>R</i><sub><i>J</i></sub> is defined in https://dlmf.nist.gov/19.16.E2
|
||||
* \f[ R_J(x, y, z, p) = \frac32
|
||||
* \int_0^\infty
|
||||
* [(t + x) (t + y) (t + z)]^{-1/2} (t + p)^{-1}\, dt, \f]
|
||||
* where \e p > 0, and \e x, \e y, \e z are nonnegative with at most one of
|
||||
* them being 0.
|
||||
**********************************************************************/
|
||||
static real RJ(real x, real y, real z, real p);
|
||||
|
||||
/**
|
||||
* Degenerate symmetric integral of the third kind
|
||||
* <i>R</i><sub><i>D</i></sub>.
|
||||
*
|
||||
* @param[in] x
|
||||
* @param[in] y
|
||||
* @param[in] z
|
||||
* @return <i>R</i><sub><i>D</i></sub>(\e x, \e y, \e z) =
|
||||
* <i>R</i><sub><i>J</i></sub>(\e x, \e y, \e z, \e z).
|
||||
*
|
||||
* <i>R</i><sub><i>D</i></sub> is defined in https://dlmf.nist.gov/19.16.E5
|
||||
* \f[ R_D(x, y, z) = \frac32
|
||||
* \int_0^\infty[(t + x) (t + y)]^{-1/2} (t + z)^{-3/2}\, dt, \f]
|
||||
* where \e x, \e y, \e z are positive except that at most one of \e x and
|
||||
* \e y can be 0.
|
||||
**********************************************************************/
|
||||
static real RD(real x, real y, real z);
|
||||
///@}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_ELLIPTICFUNCTION_HPP
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* \file GARS.hpp
|
||||
* \brief Header for GeographicLib::GARS class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2015-2024) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GARS_HPP)
|
||||
#define GEOGRAPHICLIB_GARS_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Conversions for the Global Area Reference System (GARS)
|
||||
*
|
||||
* The Global Area Reference System is described in
|
||||
* - https://en.wikipedia.org/wiki/Global_Area_Reference_System
|
||||
* - https://earth-info.nga.mil/index.php?dir=coordsys&action=coordsys#tab_gars
|
||||
* .
|
||||
* It provides a compact string representation of a geographic area
|
||||
* (expressed as latitude and longitude). The classes Georef and Geohash
|
||||
* implement similar compact representations.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-GARS.cpp
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT GARS {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
static const char* const digits_;
|
||||
static const char* const letters_;
|
||||
static constexpr int lonorig_ = -Math::hd; // Origin for longitude
|
||||
static constexpr int latorig_ = -Math::qd; // Origin for latitude
|
||||
static constexpr int baselon_ = 10; // Base for longitude tiles
|
||||
static constexpr int baselat_ = 24; // Base for latitude tiles
|
||||
static constexpr int lonlen_ = 3;
|
||||
static constexpr int latlen_ = 2;
|
||||
static constexpr int baselen_ = lonlen_ + latlen_;
|
||||
static constexpr int mult1_ = 2; // base precision = 1/2 degree
|
||||
static constexpr int mult2_ = 2; // 6th char gives 2x more precision
|
||||
static constexpr int mult3_ = 3; // 7th char gives 3x more precision
|
||||
static constexpr int m_ = mult1_ * mult2_ * mult3_;
|
||||
static constexpr int maxprec_ = 2;
|
||||
static constexpr int maxlen_ = baselen_ + maxprec_;
|
||||
GARS() = delete; // Disable constructor
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Convert from geographic coordinates to GARS.
|
||||
*
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[in] prec the precision of the resulting GARS.
|
||||
* @param[out] gars the GARS string.
|
||||
* @exception GeographicErr if \e lat is not in [−90°,
|
||||
* 90°].
|
||||
* @exception std::bad_alloc if memory for \e gars can't be allocated.
|
||||
*
|
||||
* \e prec specifies the precision of \e gars as follows:
|
||||
* - \e prec = 0 (min), 30' precision, e.g., 006AG;
|
||||
* - \e prec = 1, 15' precision, e.g., 006AG3;
|
||||
* - \e prec = 2 (max), 5' precision, e.g., 006AG39.
|
||||
*
|
||||
* If \e lat or \e lon is NaN, then \e gars is set to "INVALID".
|
||||
**********************************************************************/
|
||||
static void Forward(real lat, real lon, int prec, std::string& gars);
|
||||
|
||||
/**
|
||||
* Convert from GARS to geographic coordinates.
|
||||
*
|
||||
* @param[in] gars the GARS.
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] prec the precision of \e gars.
|
||||
* @param[in] centerp if true (the default) return the center of the
|
||||
* \e gars, otherwise return the south-west corner.
|
||||
* @exception GeographicErr if \e gars is illegal.
|
||||
*
|
||||
* The case of the letters in \e gars is ignored. \e prec is in the range
|
||||
* [0, 2] and gives the precision of \e gars as follows:
|
||||
* - \e prec = 0 (min), 30' precision, e.g., 006AG;
|
||||
* - \e prec = 1, 15' precision, e.g., 006AG3;
|
||||
* - \e prec = 2 (max), 5' precision, e.g., 006AG39.
|
||||
*
|
||||
* If the first 3 characters of \e gars are "INV", then \e lat and \e lon
|
||||
* are set to NaN and \e prec is unchanged.
|
||||
**********************************************************************/
|
||||
static void Reverse(const std::string& gars, real& lat, real& lon,
|
||||
int& prec, bool centerp = true);
|
||||
|
||||
/**
|
||||
* The angular resolution of a GARS.
|
||||
*
|
||||
* @param[in] prec the precision of the GARS.
|
||||
* @return the latitude-longitude resolution (degrees).
|
||||
*
|
||||
* Internally, \e prec is first put in the range [0, 2].
|
||||
**********************************************************************/
|
||||
static Math::real Resolution(int prec) {
|
||||
return 1/real(prec <= 0 ? mult1_ : (prec == 1 ? mult1_ * mult2_ :
|
||||
mult1_ * mult2_ * mult3_));
|
||||
}
|
||||
|
||||
/**
|
||||
* The GARS precision required to meet a given geographic resolution.
|
||||
*
|
||||
* @param[in] res the minimum of resolution in latitude and longitude
|
||||
* (degrees).
|
||||
* @return GARS precision.
|
||||
*
|
||||
* The returned length is in the range [0, 2].
|
||||
**********************************************************************/
|
||||
static int Precision(real res) {
|
||||
using std::fabs; res = fabs(res);
|
||||
for (int prec = 0; prec < maxprec_; ++prec)
|
||||
if (Resolution(prec) <= res)
|
||||
return prec;
|
||||
return maxprec_;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_GARS_HPP
|
||||
+545
@@ -0,0 +1,545 @@
|
||||
/**
|
||||
* \file GeoCoords.hpp
|
||||
* \brief Header for GeographicLib::GeoCoords class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2008-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GEOCOORDS_HPP)
|
||||
#define GEOGRAPHICLIB_GEOCOORDS_HPP 1
|
||||
|
||||
#include <GeographicLib/UTMUPS.hpp>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Conversion between geographic coordinates
|
||||
*
|
||||
* This class stores a geographic position which may be set via the
|
||||
* constructors or Reset via
|
||||
* - latitude and longitude
|
||||
* - UTM or UPS coordinates
|
||||
* - a string representation of these or an MGRS coordinate string
|
||||
*
|
||||
* The state consists of the latitude and longitude and the supplied UTM or
|
||||
* UPS coordinates (possibly derived from the MGRS coordinates). If latitude
|
||||
* and longitude were given then the UTM/UPS coordinates follows the standard
|
||||
* conventions.
|
||||
*
|
||||
* The mutable state consists of the UTM or UPS coordinates for a alternate
|
||||
* zone. A method SetAltZone is provided to set the alternate UPS/UTM zone.
|
||||
*
|
||||
* Methods are provided to return the geographic coordinates, the input UTM
|
||||
* or UPS coordinates (and associated meridian convergence and scale), or
|
||||
* alternate UTM or UPS coordinates (and their associated meridian
|
||||
* convergence and scale).
|
||||
*
|
||||
* Once the input string has been parsed, you can print the result out in any
|
||||
* of the formats, decimal degrees, degrees minutes seconds, MGRS, UTM/UPS.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-GeoCoords.cpp
|
||||
*
|
||||
* <a href="GeoConvert.1.html">GeoConvert</a> is a command-line utility
|
||||
* providing access to the functionality of GeoCoords.
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT GeoCoords {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
real _lat, _long, _easting, _northing, _gamma, _k;
|
||||
bool _northp;
|
||||
int _zone; // See UTMUPS::zonespec
|
||||
mutable real _alt_easting, _alt_northing, _alt_gamma, _alt_k;
|
||||
mutable int _alt_zone;
|
||||
|
||||
void CopyToAlt() const {
|
||||
_alt_easting = _easting;
|
||||
_alt_northing = _northing;
|
||||
_alt_gamma = _gamma;
|
||||
_alt_k = _k;
|
||||
_alt_zone = _zone;
|
||||
}
|
||||
static void UTMUPSString(int zone, bool northp,
|
||||
real easting, real northing,
|
||||
int prec, bool abbrev, std::string& utm);
|
||||
void FixHemisphere();
|
||||
public:
|
||||
|
||||
/** \name Initializing the GeoCoords object
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* The default constructor sets the coordinate as undefined.
|
||||
**********************************************************************/
|
||||
GeoCoords()
|
||||
: _lat(Math::NaN())
|
||||
, _long(Math::NaN())
|
||||
, _easting(Math::NaN())
|
||||
, _northing(Math::NaN())
|
||||
, _gamma(Math::NaN())
|
||||
, _k(Math::NaN())
|
||||
, _northp(false)
|
||||
, _zone(UTMUPS::INVALID)
|
||||
{ CopyToAlt(); }
|
||||
|
||||
/**
|
||||
* Construct from a string.
|
||||
*
|
||||
* @param[in] s 1-element, 2-element, or 3-element string representation of
|
||||
* the position.
|
||||
* @param[in] centerp governs the interpretation of MGRS coordinates (see
|
||||
* below).
|
||||
* @param[in] longfirst governs the interpretation of geographic
|
||||
* coordinates (see below).
|
||||
* @exception GeographicErr if the \e s is malformed (see below).
|
||||
*
|
||||
* Parse as a string and interpret it as a geographic position. The input
|
||||
* string is broken into space (or comma) separated pieces and Basic
|
||||
* decision on which format is based on number of components
|
||||
* -# MGRS
|
||||
* -# "Lat Long" or "Long Lat"
|
||||
* -# "Zone Easting Northing" or "Easting Northing Zone"
|
||||
*
|
||||
* The following inputs are approximately the same (Ar Ramadi Bridge, Iraq)
|
||||
* - Latitude and Longitude
|
||||
* - 33.44 43.27
|
||||
* - N33d26.4' E43d16.2'
|
||||
* - 43d16'12"E 33d26'24"N
|
||||
* - 43:16:12E 33:26:24
|
||||
* - MGRS
|
||||
* - 38SLC30
|
||||
* - 38SLC391014
|
||||
* - 38SLC3918701405
|
||||
* - 37SHT9708
|
||||
* - UTM
|
||||
* - 38n 339188 3701405
|
||||
* - 897039 3708229 37n
|
||||
*
|
||||
* <b>Latitude and Longitude parsing</b>: Latitude precedes longitude,
|
||||
* unless a N, S, E, W hemisphere designator is used on one or both
|
||||
* coordinates. If \e longfirst = true (default is false), then
|
||||
* longitude precedes latitude in the absence of a hemisphere designator.
|
||||
* Thus (with \e longfirst = false)
|
||||
* - 40 -75
|
||||
* - N40 W75
|
||||
* - -75 N40
|
||||
* - 75W 40N
|
||||
* - E-75 -40S
|
||||
* .
|
||||
* are all the same position. The coordinates may be given in
|
||||
* decimal degrees, degrees and decimal minutes, degrees, minutes,
|
||||
* seconds, etc. Use d, ', and " to mark off the degrees,
|
||||
* minutes and seconds. Various alternative symbols for degrees, minutes,
|
||||
* and seconds are allowed. Alternatively, use : to separate these
|
||||
* components. A single addition or subtraction is allowed. (See
|
||||
* DMS::Decode for details.) Thus
|
||||
* - 40d30'30"
|
||||
* - 40d30'30
|
||||
* - 40°30'30
|
||||
* - 40d30.5'
|
||||
* - 40d30.5
|
||||
* - 40:30:30
|
||||
* - 40:30.5
|
||||
* - 40.508333333
|
||||
* - 40:30+0:0:30
|
||||
* - 40:31-0:0.5
|
||||
* .
|
||||
* all specify the same angle. The leading sign applies to the following
|
||||
* components so -1d30 is -(1+30/60) = −1.5. However, note
|
||||
* that -1:30-0:0:15 is parsed as (-1:30) + (-0:0:15) = −(1+30/60)
|
||||
* − (15/3600). Latitudes must be in the range [−90°,
|
||||
* 90°]. Internally longitudes are reduced to the range
|
||||
* [−180°, 180°].
|
||||
*
|
||||
* <b>UTM/UPS parsing</b>: For UTM zones (−80° ≤ Lat <
|
||||
* 84°), the zone designator is made up of a zone number (for 1 to 60)
|
||||
* and a hemisphere letter (n or s), e.g., 38n (38north can also be used).
|
||||
* The latitude band designer ([C--M] in the southern hemisphere and [N--X]
|
||||
* in the northern) should NOT be used. (This is part of the MGRS
|
||||
* coordinate.) The zone designator for the poles (where UPS is employed)
|
||||
* is a hemisphere letter by itself, i.e., n or s (north or south can also
|
||||
* be used).
|
||||
*
|
||||
* <b>MGRS parsing</b> interprets the grid references as square area at the
|
||||
* specified precision (1m, 10m, 100m, etc.). If \e centerp = true (the
|
||||
* default), the center of this square is then taken to be the precise
|
||||
* position; thus:
|
||||
* - 38SMB = 38n 450000 3650000
|
||||
* - 38SMB4484 = 38n 444500 3684500
|
||||
* - 38SMB44148470 = 38n 444145 3684705
|
||||
* .
|
||||
* Otherwise, the "south-west" corner of the square is used, i.e.,
|
||||
* - 38SMB = 38n 400000 3600000
|
||||
* - 38SMB4484 = 38n 444000 3684000
|
||||
* - 38SMB44148470 = 38n 444140 3684700
|
||||
**********************************************************************/
|
||||
explicit GeoCoords(const std::string& s,
|
||||
bool centerp = true, bool longfirst = false)
|
||||
{ Reset(s, centerp, longfirst); }
|
||||
|
||||
/**
|
||||
* Construct from geographic coordinates.
|
||||
*
|
||||
* @param[in] latitude (degrees).
|
||||
* @param[in] longitude (degrees).
|
||||
* @param[in] zone if specified, force the UTM/UPS representation to use a
|
||||
* specified zone using the rules given in UTMUPS::zonespec.
|
||||
* @exception GeographicErr if \e latitude is not in [−90°,
|
||||
* 90°].
|
||||
* @exception GeographicErr if \e zone cannot be used for this location.
|
||||
**********************************************************************/
|
||||
GeoCoords(real latitude, real longitude, int zone = UTMUPS::STANDARD) {
|
||||
Reset(latitude, longitude, zone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct from UTM/UPS coordinates.
|
||||
*
|
||||
* @param[in] zone UTM zone (zero means UPS).
|
||||
* @param[in] northp hemisphere (true means north, false means south).
|
||||
* @param[in] easting (meters).
|
||||
* @param[in] northing (meters).
|
||||
* @exception GeographicErr if \e zone, \e easting, or \e northing is
|
||||
* outside its allowed range.
|
||||
**********************************************************************/
|
||||
GeoCoords(int zone, bool northp, real easting, real northing) {
|
||||
Reset(zone, northp, easting, northing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the location from a string. See
|
||||
* GeoCoords(const std::string& s, bool centerp, bool longfirst).
|
||||
*
|
||||
* @param[in] s 1-element, 2-element, or 3-element string representation of
|
||||
* the position.
|
||||
* @param[in] centerp governs the interpretation of MGRS coordinates.
|
||||
* @param[in] longfirst governs the interpretation of geographic
|
||||
* coordinates.
|
||||
* @exception GeographicErr if the \e s is malformed.
|
||||
**********************************************************************/
|
||||
void Reset(const std::string& s,
|
||||
bool centerp = true, bool longfirst = false);
|
||||
|
||||
/**
|
||||
* Reset the location in terms of geographic coordinates. See
|
||||
* GeoCoords(real latitude, real longitude, int zone).
|
||||
*
|
||||
* @param[in] latitude (degrees).
|
||||
* @param[in] longitude (degrees).
|
||||
* @param[in] zone if specified, force the UTM/UPS representation to use a
|
||||
* specified zone using the rules given in UTMUPS::zonespec.
|
||||
* @exception GeographicErr if \e latitude is not in [−90°,
|
||||
* 90°].
|
||||
* @exception GeographicErr if \e zone cannot be used for this location.
|
||||
**********************************************************************/
|
||||
void Reset(real latitude, real longitude, int zone = UTMUPS::STANDARD) {
|
||||
UTMUPS::Forward(latitude, longitude,
|
||||
_zone, _northp, _easting, _northing, _gamma, _k,
|
||||
zone);
|
||||
_lat = latitude;
|
||||
_long = Math::AngNormalize(longitude);
|
||||
CopyToAlt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the location in terms of UPS/UPS coordinates. See
|
||||
* GeoCoords(int zone, bool northp, real easting, real northing).
|
||||
*
|
||||
* @param[in] zone UTM zone (zero means UPS).
|
||||
* @param[in] northp hemisphere (true means north, false means south).
|
||||
* @param[in] easting (meters).
|
||||
* @param[in] northing (meters).
|
||||
* @exception GeographicErr if \e zone, \e easting, or \e northing is
|
||||
* outside its allowed range.
|
||||
**********************************************************************/
|
||||
void Reset(int zone, bool northp, real easting, real northing) {
|
||||
UTMUPS::Reverse(zone, northp, easting, northing,
|
||||
_lat, _long, _gamma, _k);
|
||||
_zone = zone;
|
||||
_northp = northp;
|
||||
_easting = easting;
|
||||
_northing = northing;
|
||||
FixHemisphere();
|
||||
CopyToAlt();
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name Querying the GeoCoords object
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return latitude (degrees)
|
||||
**********************************************************************/
|
||||
Math::real Latitude() const { return _lat; }
|
||||
|
||||
/**
|
||||
* @return longitude (degrees)
|
||||
**********************************************************************/
|
||||
Math::real Longitude() const { return _long; }
|
||||
|
||||
/**
|
||||
* @return easting (meters)
|
||||
**********************************************************************/
|
||||
Math::real Easting() const { return _easting; }
|
||||
|
||||
/**
|
||||
* @return northing (meters)
|
||||
**********************************************************************/
|
||||
Math::real Northing() const { return _northing; }
|
||||
|
||||
/**
|
||||
* @return meridian convergence (degrees) for the UTM/UPS projection.
|
||||
**********************************************************************/
|
||||
Math::real Convergence() const { return _gamma; }
|
||||
|
||||
/**
|
||||
* @return scale for the UTM/UPS projection.
|
||||
**********************************************************************/
|
||||
Math::real Scale() const { return _k; }
|
||||
|
||||
/**
|
||||
* @return hemisphere (false means south, true means north).
|
||||
**********************************************************************/
|
||||
bool Northp() const { return _northp; }
|
||||
|
||||
/**
|
||||
* @return hemisphere letter n or s.
|
||||
**********************************************************************/
|
||||
char Hemisphere() const { return _northp ? 'n' : 's'; }
|
||||
|
||||
/**
|
||||
* @return the zone corresponding to the input (return 0 for UPS).
|
||||
**********************************************************************/
|
||||
int Zone() const { return _zone; }
|
||||
|
||||
///@}
|
||||
|
||||
/** \name Setting and querying the alternate zone
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Specify alternate zone number.
|
||||
*
|
||||
* @param[in] zone zone number for the alternate representation.
|
||||
* @exception GeographicErr if \e zone cannot be used for this location.
|
||||
*
|
||||
* See UTMUPS::zonespec for more information on the interpretation of \e
|
||||
* zone. Note that \e zone == UTMUPS::STANDARD (the default) use the
|
||||
* standard UPS or UTM zone, UTMUPS::MATCH does nothing retaining the
|
||||
* existing alternate representation. Before this is called the alternate
|
||||
* zone is the input zone.
|
||||
**********************************************************************/
|
||||
void SetAltZone(int zone = UTMUPS::STANDARD) const {
|
||||
if (zone == UTMUPS::MATCH)
|
||||
return;
|
||||
zone = UTMUPS::StandardZone(_lat, _long, zone);
|
||||
if (zone == _zone)
|
||||
CopyToAlt();
|
||||
else {
|
||||
bool northp;
|
||||
UTMUPS::Forward(_lat, _long,
|
||||
_alt_zone, northp,
|
||||
_alt_easting, _alt_northing, _alt_gamma, _alt_k,
|
||||
zone);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return current alternate zone (return 0 for UPS).
|
||||
**********************************************************************/
|
||||
int AltZone() const { return _alt_zone; }
|
||||
|
||||
/**
|
||||
* @return easting (meters) for alternate zone.
|
||||
**********************************************************************/
|
||||
Math::real AltEasting() const { return _alt_easting; }
|
||||
|
||||
/**
|
||||
* @return northing (meters) for alternate zone.
|
||||
**********************************************************************/
|
||||
Math::real AltNorthing() const { return _alt_northing; }
|
||||
|
||||
/**
|
||||
* @return meridian convergence (degrees) for alternate zone.
|
||||
**********************************************************************/
|
||||
Math::real AltConvergence() const { return _alt_gamma; }
|
||||
|
||||
/**
|
||||
* @return scale for alternate zone.
|
||||
**********************************************************************/
|
||||
Math::real AltScale() const { return _alt_k; }
|
||||
///@}
|
||||
|
||||
/** \name String representations of the GeoCoords object
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* String representation with latitude and longitude as signed decimal
|
||||
* degrees.
|
||||
*
|
||||
* @param[in] prec precision (relative to about 1m).
|
||||
* @param[in] longfirst if true give longitude first (default = false)
|
||||
* @exception std::bad_alloc if memory for the string can't be allocated.
|
||||
* @return decimal latitude/longitude string representation.
|
||||
*
|
||||
* Precision specifies accuracy of representation as follows:
|
||||
* - prec = −5 (min), 1°
|
||||
* - prec = 0, 10<sup>−5</sup>° (about 1m)
|
||||
* - prec = 3, 10<sup>−8</sup>°
|
||||
* - prec = 9 (max), 10<sup>−14</sup>°
|
||||
**********************************************************************/
|
||||
std::string GeoRepresentation(int prec = 0, bool longfirst = false) const;
|
||||
|
||||
/**
|
||||
* String representation with latitude and longitude as degrees, minutes,
|
||||
* seconds, and hemisphere.
|
||||
*
|
||||
* @param[in] prec precision (relative to about 1m)
|
||||
* @param[in] longfirst if true give longitude first (default = false)
|
||||
* @param[in] dmssep if non-null, use as the DMS separator character
|
||||
* (instead of d, ', " delimiters).
|
||||
* @exception std::bad_alloc if memory for the string can't be allocated.
|
||||
* @return DMS latitude/longitude string representation.
|
||||
*
|
||||
* Precision specifies accuracy of representation as follows:
|
||||
* - prec = −5 (min), 1°
|
||||
* - prec = −4, 0.1°
|
||||
* - prec = −3, 1'
|
||||
* - prec = −2, 0.1'
|
||||
* - prec = −1, 1"
|
||||
* - prec = 0, 0.1" (about 3m)
|
||||
* - prec = 1, 0.01"
|
||||
* - prec = 10 (max), 10<sup>−11</sup>"
|
||||
**********************************************************************/
|
||||
std::string DMSRepresentation(int prec = 0, bool longfirst = false,
|
||||
char dmssep = char(0))
|
||||
const;
|
||||
|
||||
/**
|
||||
* MGRS string.
|
||||
*
|
||||
* @param[in] prec precision (relative to about 1m).
|
||||
* @exception std::bad_alloc if memory for the string can't be allocated.
|
||||
* @return MGRS string.
|
||||
*
|
||||
* This gives the coordinates of the enclosing grid square with size given
|
||||
* by the precision. Thus 38n 444180 3684790 converted to a MGRS
|
||||
* coordinate at precision −2 (100m) is 38SMB441847 and not
|
||||
* 38SMB442848. \e prec specifies the precision of the MGRS string as
|
||||
* follows:
|
||||
* - prec = −6 (min), only the grid zone is returned, e.g., 38S
|
||||
* - prec = −5, 100km, e.g., 38SMB
|
||||
* - prec = −4, 10km
|
||||
* - prec = −3, 1km
|
||||
* - prec = −2, 100m
|
||||
* - prec = −1, 10m
|
||||
* - prec = 0, 1m
|
||||
* - prec = 1, 0.1m
|
||||
* - prec = 6 (max), 1μm
|
||||
**********************************************************************/
|
||||
std::string MGRSRepresentation(int prec = 0) const;
|
||||
|
||||
/**
|
||||
* UTM/UPS string.
|
||||
*
|
||||
* @param[in] prec precision (relative to about 1m)
|
||||
* @param[in] abbrev if true (the default) use abbreviated (n/s) notation
|
||||
* for hemisphere; otherwise spell out the hemisphere (north/south)
|
||||
* @exception std::bad_alloc if memory for the string can't be allocated.
|
||||
* @return UTM/UPS string representation: zone designator, easting, and
|
||||
* northing.
|
||||
*
|
||||
* Precision specifies accuracy of representation as follows:
|
||||
* - prec = −5 (min), 100km
|
||||
* - prec = −3, 1km
|
||||
* - prec = 0, 1m
|
||||
* - prec = 3, 1mm
|
||||
* - prec = 6, 1μm
|
||||
* - prec = 9 (max), 1nm
|
||||
**********************************************************************/
|
||||
std::string UTMUPSRepresentation(int prec = 0, bool abbrev = true) const;
|
||||
|
||||
/**
|
||||
* UTM/UPS string with hemisphere override.
|
||||
*
|
||||
* @param[in] northp hemisphere override
|
||||
* @param[in] prec precision (relative to about 1m)
|
||||
* @param[in] abbrev if true (the default) use abbreviated (n/s) notation
|
||||
* for hemisphere; otherwise spell out the hemisphere (north/south)
|
||||
* @exception GeographicErr if the hemisphere override attempts to change
|
||||
* UPS N to UPS S or vice versa.
|
||||
* @exception std::bad_alloc if memory for the string can't be allocated.
|
||||
* @return UTM/UPS string representation: zone designator, easting, and
|
||||
* northing.
|
||||
**********************************************************************/
|
||||
std::string UTMUPSRepresentation(bool northp, int prec = 0,
|
||||
bool abbrev = true) const;
|
||||
|
||||
/**
|
||||
* MGRS string for the alternate zone. See GeoCoords::MGRSRepresentation.
|
||||
*
|
||||
* @param[in] prec precision (relative to about 1m).
|
||||
* @exception std::bad_alloc if memory for the string can't be allocated.
|
||||
* @return MGRS string.
|
||||
**********************************************************************/
|
||||
std::string AltMGRSRepresentation(int prec = 0) const;
|
||||
|
||||
/**
|
||||
* UTM/UPS string for the alternate zone. See
|
||||
* GeoCoords::UTMUPSRepresentation.
|
||||
*
|
||||
* @param[in] prec precision (relative to about 1m)
|
||||
* @param[in] abbrev if true (the default) use abbreviated (n/s) notation
|
||||
* for hemisphere; otherwise spell out the hemisphere (north/south)
|
||||
* @exception std::bad_alloc if memory for the string can't be allocated.
|
||||
* @return UTM/UPS string representation: zone designator, easting, and
|
||||
* northing.
|
||||
**********************************************************************/
|
||||
std::string AltUTMUPSRepresentation(int prec = 0, bool abbrev = true)
|
||||
const;
|
||||
|
||||
/**
|
||||
* UTM/UPS string for the alternate zone, with hemisphere override.
|
||||
*
|
||||
* @param[in] northp hemisphere override
|
||||
* @param[in] prec precision (relative to about 1m)
|
||||
* @param[in] abbrev if true (the default) use abbreviated (n/s) notation
|
||||
* for hemisphere; otherwise spell out the hemisphere (north/south)
|
||||
* @exception GeographicErr if the hemisphere override attempts to change
|
||||
* UPS n to UPS s or vice verse.
|
||||
* @exception std::bad_alloc if memory for the string can't be allocated.
|
||||
* @return UTM/UPS string representation: zone designator, easting, and
|
||||
* northing.
|
||||
**********************************************************************/
|
||||
std::string AltUTMUPSRepresentation(bool northp, int prec = 0,
|
||||
bool abbrev = true) const;
|
||||
///@}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return \e a the equatorial radius of the WGS84 ellipsoid (meters).
|
||||
*
|
||||
* (The WGS84 value is returned because the UTM and UPS projections are
|
||||
* based on this ellipsoid.)
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return UTMUPS::EquatorialRadius(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the WGS84 ellipsoid.
|
||||
*
|
||||
* (The WGS84 value is returned because the UTM and UPS projections are
|
||||
* based on this ellipsoid.)
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return UTMUPS::Flattening(); }
|
||||
///@}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_GEOCOORDS_HPP
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
/**
|
||||
* \file Geocentric.hpp
|
||||
* \brief Header for GeographicLib::Geocentric class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2008-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GEOCENTRIC_HPP)
|
||||
#define GEOGRAPHICLIB_GEOCENTRIC_HPP 1
|
||||
|
||||
#include <vector>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief %Geocentric coordinates
|
||||
*
|
||||
* Convert between geodetic coordinates latitude = \e lat, longitude = \e
|
||||
* lon, height = \e h (measured vertically from the surface of the ellipsoid)
|
||||
* to geocentric coordinates (\e X, \e Y, \e Z). The origin of geocentric
|
||||
* coordinates is at the center of the earth. The \e Z axis goes thru the
|
||||
* north pole, \e lat = 90°. The \e X axis goes thru \e lat = 0,
|
||||
* \e lon = 0. %Geocentric coordinates are also known as earth centered,
|
||||
* earth fixed (ECEF) coordinates.
|
||||
*
|
||||
* The conversion from geographic to geocentric coordinates is
|
||||
* straightforward. For the reverse transformation we use
|
||||
* - H. Vermeille,
|
||||
* <a href="https://doi.org/10.1007/s00190-002-0273-6"> Direct
|
||||
* transformation from geocentric coordinates to geodetic coordinates</a>,
|
||||
* J. Geodesy 76, 451--454 (2002).
|
||||
* .
|
||||
* Several changes have been made to ensure that the method returns accurate
|
||||
* results for all finite inputs (even if \e h is infinite). The changes are
|
||||
* described in Appendix B of
|
||||
* - C. F. F. Karney,
|
||||
* <a href="https://arxiv.org/abs/1102.1215v1">Geodesics
|
||||
* on an ellipsoid of revolution</a>,
|
||||
* Feb. 2011;
|
||||
* preprint
|
||||
* <a href="https://arxiv.org/abs/1102.1215v1">arxiv:1102.1215v1</a>.
|
||||
* .
|
||||
* Vermeille similarly updated his method in
|
||||
* - H. Vermeille,
|
||||
* <a href="https://doi.org/10.1007/s00190-010-0419-x">
|
||||
* An analytical method to transform geocentric into
|
||||
* geodetic coordinates</a>, J. Geodesy 85, 105--117 (2011).
|
||||
* .
|
||||
* See \ref geocentric for more information.
|
||||
*
|
||||
* The errors in these routines are close to round-off. Specifically, for
|
||||
* points within 5000 km of the surface of the ellipsoid (either inside or
|
||||
* outside the ellipsoid), the error is bounded by 7 nm (7 nanometers) for
|
||||
* the WGS84 ellipsoid. See \ref geocentric for further information on the
|
||||
* errors.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Geocentric.cpp
|
||||
*
|
||||
* <a href="CartConvert.1.html">CartConvert</a> is a command-line utility
|
||||
* providing access to the functionality of Geocentric and LocalCartesian.
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT Geocentric {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
friend class LocalCartesian;
|
||||
friend class MagneticCircle; // MagneticCircle uses Rotation
|
||||
friend class MagneticModel; // MagneticModel uses IntForward
|
||||
friend class GravityCircle; // GravityCircle uses Rotation
|
||||
friend class GravityModel; // GravityModel uses IntForward
|
||||
friend class NormalGravity; // NormalGravity uses IntForward
|
||||
static const size_t dim_ = 3;
|
||||
static const size_t dim2_ = dim_ * dim_;
|
||||
real _a, _f, _e2, _e2m, _e2a, _e4a, _maxrad;
|
||||
static void Rotation(real sphi, real cphi, real slam, real clam,
|
||||
real M[dim2_]);
|
||||
static void Rotate(const real M[dim2_], real x, real y, real z,
|
||||
real& X, real& Y, real& Z) {
|
||||
// Perform [X,Y,Z]^t = M.[x,y,z]^t
|
||||
// (typically local cartesian to geocentric)
|
||||
X = M[0] * x + M[1] * y + M[2] * z;
|
||||
Y = M[3] * x + M[4] * y + M[5] * z;
|
||||
Z = M[6] * x + M[7] * y + M[8] * z;
|
||||
}
|
||||
static void Unrotate(const real M[dim2_], real X, real Y, real Z,
|
||||
real& x, real& y, real& z) {
|
||||
// Perform [x,y,z]^t = M^t.[X,Y,Z]^t
|
||||
// (typically geocentric to local cartesian)
|
||||
x = M[0] * X + M[3] * Y + M[6] * Z;
|
||||
y = M[1] * X + M[4] * Y + M[7] * Z;
|
||||
z = M[2] * X + M[5] * Y + M[8] * Z;
|
||||
}
|
||||
void IntForward(real lat, real lon, real h, real& X, real& Y, real& Z,
|
||||
real M[dim2_]) const;
|
||||
void IntReverse(real X, real Y, real Z, real& lat, real& lon, real& h,
|
||||
real M[dim2_]) const;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for an ellipsoid with
|
||||
*
|
||||
* @param[in] a equatorial radius (meters).
|
||||
* @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
|
||||
* Negative \e f gives a prolate ellipsoid.
|
||||
* @exception GeographicErr if \e a or (1 − \e f) \e a is not
|
||||
* positive.
|
||||
**********************************************************************/
|
||||
Geocentric(real a, real f);
|
||||
|
||||
/**
|
||||
* A default constructor (for use by NormalGravity).
|
||||
**********************************************************************/
|
||||
Geocentric() : _a(-1) {}
|
||||
|
||||
/**
|
||||
* Convert from geodetic to geocentric coordinates.
|
||||
*
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[in] h height of point above the ellipsoid (meters).
|
||||
* @param[out] X geocentric coordinate (meters).
|
||||
* @param[out] Y geocentric coordinate (meters).
|
||||
* @param[out] Z geocentric coordinate (meters).
|
||||
*
|
||||
* \e lat should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
void Forward(real lat, real lon, real h, real& X, real& Y, real& Z)
|
||||
const {
|
||||
if (Init())
|
||||
IntForward(lat, lon, h, X, Y, Z, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from geodetic to geocentric coordinates and return rotation
|
||||
* matrix.
|
||||
*
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[in] h height of point above the ellipsoid (meters).
|
||||
* @param[out] X geocentric coordinate (meters).
|
||||
* @param[out] Y geocentric coordinate (meters).
|
||||
* @param[out] Z geocentric coordinate (meters).
|
||||
* @param[out] M if the length of the vector is 9, fill with the rotation
|
||||
* matrix in row-major order.
|
||||
*
|
||||
* Let \e v be a unit vector located at (\e lat, \e lon, \e h). We can
|
||||
* express \e v as \e column vectors in one of two ways
|
||||
* - in east, north, up coordinates (where the components are relative to a
|
||||
* local coordinate system at (\e lat, \e lon, \e h)); call this
|
||||
* representation \e v1.
|
||||
* - in geocentric \e X, \e Y, \e Z coordinates; call this representation
|
||||
* \e v0.
|
||||
* .
|
||||
* Then we have \e v0 = \e M ⋅ \e v1.
|
||||
**********************************************************************/
|
||||
void Forward(real lat, real lon, real h, real& X, real& Y, real& Z,
|
||||
std::vector<real>& M)
|
||||
const {
|
||||
if (!Init())
|
||||
return;
|
||||
if (M.end() == M.begin() + dim2_) {
|
||||
real t[dim2_];
|
||||
IntForward(lat, lon, h, X, Y, Z, t);
|
||||
std::copy(t, t + dim2_, M.begin());
|
||||
} else
|
||||
IntForward(lat, lon, h, X, Y, Z, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from geocentric to geodetic to coordinates.
|
||||
*
|
||||
* @param[in] X geocentric coordinate (meters).
|
||||
* @param[in] Y geocentric coordinate (meters).
|
||||
* @param[in] Z geocentric coordinate (meters).
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] h height of point above the ellipsoid (meters).
|
||||
*
|
||||
* In general, there are multiple solutions and the result which minimizes
|
||||
* |<i>h</i> |is returned, i.e., (<i>lat</i>, <i>lon</i>) corresponds to
|
||||
* the closest point on the ellipsoid. If there are still multiple
|
||||
* solutions with different latitudes (applies only if \e Z = 0), then the
|
||||
* solution with \e lat > 0 is returned. If there are still multiple
|
||||
* solutions with different longitudes (applies only if \e X = \e Y = 0)
|
||||
* then \e lon = 0 is returned. The value of \e h returned satisfies \e h
|
||||
* ≥ − \e a (1 − <i>e</i><sup>2</sup>) / sqrt(1 −
|
||||
* <i>e</i><sup>2</sup> sin<sup>2</sup>\e lat). The value of \e lon
|
||||
* returned is in the range [−180°, 180°].
|
||||
**********************************************************************/
|
||||
void Reverse(real X, real Y, real Z, real& lat, real& lon, real& h)
|
||||
const {
|
||||
if (Init())
|
||||
IntReverse(X, Y, Z, lat, lon, h, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from geocentric to geodetic to coordinates.
|
||||
*
|
||||
* @param[in] X geocentric coordinate (meters).
|
||||
* @param[in] Y geocentric coordinate (meters).
|
||||
* @param[in] Z geocentric coordinate (meters).
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] h height of point above the ellipsoid (meters).
|
||||
* @param[out] M if the length of the vector is 9, fill with the rotation
|
||||
* matrix in row-major order.
|
||||
*
|
||||
* Let \e v be a unit vector located at (\e lat, \e lon, \e h). We can
|
||||
* express \e v as \e column vectors in one of two ways
|
||||
* - in east, north, up coordinates (where the components are relative to a
|
||||
* local coordinate system at (\e lat, \e lon, \e h)); call this
|
||||
* representation \e v1.
|
||||
* - in geocentric \e X, \e Y, \e Z coordinates; call this representation
|
||||
* \e v0.
|
||||
* .
|
||||
* Then we have \e v1 = <i>M</i><sup>T</sup> ⋅ \e v0, where
|
||||
* <i>M</i><sup>T</sup> is the transpose of \e M.
|
||||
**********************************************************************/
|
||||
void Reverse(real X, real Y, real Z, real& lat, real& lon, real& h,
|
||||
std::vector<real>& M)
|
||||
const {
|
||||
if (!Init())
|
||||
return;
|
||||
if (M.end() == M.begin() + dim2_) {
|
||||
real t[dim2_];
|
||||
IntReverse(X, Y, Z, lat, lon, h, t);
|
||||
std::copy(t, t + dim2_, M.begin());
|
||||
} else
|
||||
IntReverse(X, Y, Z, lat, lon, h, NULL);
|
||||
}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return true if the object has been initialized.
|
||||
**********************************************************************/
|
||||
bool Init() const { return _a > 0; }
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const
|
||||
{ return Init() ? _a : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the
|
||||
* value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const
|
||||
{ return Init() ? _f : Math::NaN(); }
|
||||
///@}
|
||||
|
||||
/**
|
||||
* A global instantiation of Geocentric with the parameters for the WGS84
|
||||
* ellipsoid.
|
||||
**********************************************************************/
|
||||
static const Geocentric& WGS84();
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_GEOCENTRIC_HPP
|
||||
+997
@@ -0,0 +1,997 @@
|
||||
/**
|
||||
* \file Geodesic.hpp
|
||||
* \brief Header for GeographicLib::Geodesic class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2009-2024) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GEODESIC_HPP)
|
||||
#define GEOGRAPHICLIB_GEODESIC_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/GeodesicExact.hpp>
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GEODESIC_ORDER)
|
||||
/**
|
||||
* The order of the expansions used by Geodesic.
|
||||
* GEOGRAPHICLIB_GEODESIC_ORDER can be set to any integer in [3, 8].
|
||||
**********************************************************************/
|
||||
# define GEOGRAPHICLIB_GEODESIC_ORDER \
|
||||
(GEOGRAPHICLIB_PRECISION == 2 ? 6 : \
|
||||
(GEOGRAPHICLIB_PRECISION == 1 ? 3 : \
|
||||
(GEOGRAPHICLIB_PRECISION == 3 ? 7 : 8)))
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
class GeodesicLine;
|
||||
|
||||
/**
|
||||
* \brief %Geodesic calculations
|
||||
*
|
||||
* The shortest path between two points on an ellipsoid at (\e lat1, \e lon1)
|
||||
* and (\e lat2, \e lon2) is called the geodesic. Its length is \e s12 and
|
||||
* the geodesic from point 1 to point 2 has azimuths \e azi1 and \e azi2 at
|
||||
* the two end points. (The azimuth is the heading measured clockwise from
|
||||
* north. \e azi2 is the "forward" azimuth, i.e., the heading that takes you
|
||||
* beyond point 2 not back to point 1.) In the figure below, latitude is
|
||||
* labeled φ, longitude λ (with λ<sub>12</sub> =
|
||||
* λ<sub>2</sub> − λ<sub>1</sub>), and azimuth α.
|
||||
*
|
||||
* <img src="https://upload.wikimedia.org/wikipedia/commons/c/cb/Geodesic_problem_on_an_ellipsoid.svg" width=250 alt="spheroidal triangle">
|
||||
*
|
||||
* Given \e lat1, \e lon1, \e azi1, and \e s12, we can determine \e lat2, \e
|
||||
* lon2, and \e azi2. This is the \e direct geodesic problem and its
|
||||
* solution is given by the function Geodesic::Direct. (If \e s12 is
|
||||
* sufficiently large that the geodesic wraps more than halfway around the
|
||||
* earth, there will be another geodesic between the points with a smaller \e
|
||||
* s12.)
|
||||
*
|
||||
* Given \e lat1, \e lon1, \e lat2, and \e lon2, we can determine \e azi1, \e
|
||||
* azi2, and \e s12. This is the \e inverse geodesic problem, whose solution
|
||||
* is given by Geodesic::Inverse. Usually, the solution to the inverse
|
||||
* problem is unique. In cases where there are multiple solutions (all with
|
||||
* the same \e s12, of course), all the solutions can be easily generated
|
||||
* once a particular solution is provided.
|
||||
*
|
||||
* The standard way of specifying the direct problem is the specify the
|
||||
* distance \e s12 to the second point. However it is sometimes useful
|
||||
* instead to specify the arc length \e a12 (in degrees) on the auxiliary
|
||||
* sphere. This is a mathematical construct used in solving the geodesic
|
||||
* problems. The solution of the direct problem in this form is provided by
|
||||
* Geodesic::ArcDirect. An arc length in excess of 180° indicates that
|
||||
* the geodesic is not a shortest path. In addition, the arc length between
|
||||
* an equatorial crossing and the next extremum of latitude for a geodesic is
|
||||
* 90°.
|
||||
*
|
||||
* This class can also calculate several other quantities related to
|
||||
* geodesics. These are:
|
||||
* - <i>reduced length</i>. If we fix the first point and increase \e azi1
|
||||
* by \e dazi1 (radians), the second point is displaced \e m12 \e dazi1 in
|
||||
* the direction \e azi2 + 90°. The quantity \e m12 is called
|
||||
* the "reduced length" and is symmetric under interchange of the two
|
||||
* points. On a curved surface the reduced length obeys a symmetry
|
||||
* relation, \e m12 + \e m21 = 0. On a flat surface, we have \e m12 = \e
|
||||
* s12. The ratio <i>s12</i>/\e m12 gives the azimuthal scale for an
|
||||
* azimuthal equidistant projection.
|
||||
* - <i>geodesic scale</i>. Consider a reference geodesic and a second
|
||||
* geodesic parallel to this one at point 1 and separated by a small
|
||||
* distance \e dt. The separation of the two geodesics at point 2 is \e
|
||||
* M12 \e dt where \e M12 is called the "geodesic scale". \e M21 is
|
||||
* defined similarly (with the geodesics being parallel at point 2). On a
|
||||
* flat surface, we have \e M12 = \e M21 = 1. The quantity 1/\e M12 gives
|
||||
* the scale of the Cassini-Soldner projection.
|
||||
* - <i>area</i>. The area between the geodesic from point 1 to point 2 and
|
||||
* the equation is represented by \e S12; it is the area, measured
|
||||
* counterclockwise, of the geodesic quadrilateral with corners
|
||||
* (<i>lat1</i>,<i>lon1</i>), (0,<i>lon1</i>), (0,<i>lon2</i>), and
|
||||
* (<i>lat2</i>,<i>lon2</i>). It can be used to compute the area of any
|
||||
* geodesic polygon.
|
||||
*
|
||||
* Overloaded versions of Geodesic::Direct, Geodesic::ArcDirect, and
|
||||
* Geodesic::Inverse allow these quantities to be returned. In addition
|
||||
* there are general functions Geodesic::GenDirect, and Geodesic::GenInverse
|
||||
* which allow an arbitrary set of results to be computed. The quantities \e
|
||||
* m12, \e M12, \e M21 which all specify the behavior of nearby geodesics
|
||||
* obey addition rules. If points 1, 2, and 3 all lie on a single geodesic,
|
||||
* then the following rules hold:
|
||||
* - \e s13 = \e s12 + \e s23
|
||||
* - \e a13 = \e a12 + \e a23
|
||||
* - \e S13 = \e S12 + \e S23
|
||||
* - \e m13 = \e m12 \e M23 + \e m23 \e M21
|
||||
* - \e M13 = \e M12 \e M23 − (1 − \e M12 \e M21) \e m23 / \e m12
|
||||
* - \e M31 = \e M32 \e M21 − (1 − \e M23 \e M32) \e m12 / \e m23
|
||||
*
|
||||
* Additional functionality is provided by the GeodesicLine class, which
|
||||
* allows a sequence of points along a geodesic to be computed.
|
||||
*
|
||||
* The shortest distance returned by the solution of the inverse problem is
|
||||
* (obviously) uniquely defined. However, in a few special cases there are
|
||||
* multiple azimuths which yield the same shortest distance. Here is a
|
||||
* catalog of those cases:
|
||||
* - \e lat1 = −\e lat2 (with neither point at a pole). If \e azi1 =
|
||||
* \e azi2, the geodesic is unique. Otherwise there are two geodesics and
|
||||
* the second one is obtained by setting [\e azi1, \e azi2] → [\e
|
||||
* azi2, \e azi1], [\e M12, \e M21] → [\e M21, \e M12], \e S12 →
|
||||
* −\e S12. (This occurs when the longitude difference is near
|
||||
* ±180° for oblate ellipsoids.)
|
||||
* - \e lon2 = \e lon1 ± 180° (with neither point at a pole). If
|
||||
* \e azi1 = 0° or ±180°, the geodesic is unique. Otherwise
|
||||
* there are two geodesics and the second one is obtained by setting [\e
|
||||
* azi1, \e azi2] → [−\e azi1, −\e azi2], \e S12 →
|
||||
* −\e S12. (This occurs when \e lat2 is near −\e lat1 for
|
||||
* prolate ellipsoids.)
|
||||
* - Points 1 and 2 at opposite poles. There are infinitely many geodesics
|
||||
* which can be generated by setting [\e azi1, \e azi2] → [\e azi1, \e
|
||||
* azi2] + [\e d, −\e d], for arbitrary \e d. (For spheres, this
|
||||
* prescription applies when points 1 and 2 are antipodal.)
|
||||
* - \e s12 = 0 (coincident points). There are infinitely many geodesics
|
||||
* which can be generated by setting [\e azi1, \e azi2] →
|
||||
* [\e azi1, \e azi2] + [\e d, \e d], for arbitrary \e d.
|
||||
*
|
||||
* The calculations are accurate to better than 15 nm (15 nanometers) for the
|
||||
* WGS84 ellipsoid. See Sec. 9 of
|
||||
* <a href="https://arxiv.org/abs/1102.1215v1">arXiv:1102.1215v1</a> for
|
||||
* details. With \e exact = false (the default) in the constructor,
|
||||
* the algorithms used by this class are based on series expansions
|
||||
* using the flattening \e f as a small parameter. These are only accurate
|
||||
* for |<i>f</i>| < 0.02; however reasonably accurate results will be
|
||||
* obtained for |<i>f</i>| < 0.2. Here is a table of the approximate
|
||||
* maximum error (expressed as a distance) for an ellipsoid with the same
|
||||
* equatorial radius as the WGS84 ellipsoid and different values of the
|
||||
* flattening.<pre>
|
||||
* |f| error
|
||||
* 0.01 25 nm
|
||||
* 0.02 30 nm
|
||||
* 0.05 10 um
|
||||
* 0.1 1.5 mm
|
||||
* 0.2 300 mm
|
||||
* </pre>
|
||||
* For very eccentric ellipsoids, set \e exact to true in the constructor;
|
||||
* this will delegate the calculations to the GeodesicExact class.
|
||||
*
|
||||
* The algorithms are described in
|
||||
* - C. F. F. Karney,
|
||||
* <a href="https://doi.org/10.1007/s00190-012-0578-z">
|
||||
* <i>Algorithms for geodesics</i></a>,
|
||||
* J. Geodesy <b>87</b>, 43--55 (2013);
|
||||
* DOI: <a href="https://doi.org/10.1007/s00190-012-0578-z">
|
||||
* 10.1007/s00190-012-0578-z</a>;
|
||||
* addenda:
|
||||
* <a href="https://geographiclib.sourceforge.io/geod-addenda.html">
|
||||
* geod-addenda.html</a>.
|
||||
* .
|
||||
* For more information on geodesics see \ref geodesic.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Geodesic.cpp
|
||||
*
|
||||
* <a href="GeodSolve.1.html">GeodSolve</a> is a command-line utility
|
||||
* providing access to the functionality of Geodesic and GeodesicLine.
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT Geodesic {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
friend class GeodesicLine;
|
||||
static const int nA1_ = GEOGRAPHICLIB_GEODESIC_ORDER;
|
||||
static const int nC1_ = GEOGRAPHICLIB_GEODESIC_ORDER;
|
||||
static const int nC1p_ = GEOGRAPHICLIB_GEODESIC_ORDER;
|
||||
static const int nA2_ = GEOGRAPHICLIB_GEODESIC_ORDER;
|
||||
static const int nC2_ = GEOGRAPHICLIB_GEODESIC_ORDER;
|
||||
static const int nA3_ = GEOGRAPHICLIB_GEODESIC_ORDER;
|
||||
static const int nA3x_ = nA3_;
|
||||
static const int nC3_ = GEOGRAPHICLIB_GEODESIC_ORDER;
|
||||
static const int nC3x_ = (nC3_ * (nC3_ - 1)) / 2;
|
||||
static const int nC4_ = GEOGRAPHICLIB_GEODESIC_ORDER;
|
||||
static const int nC4x_ = (nC4_ * (nC4_ + 1)) / 2;
|
||||
// Size for temporary array
|
||||
// nC = max(max(nC1_, nC1p_, nC2_) + 1, max(nC3_, nC4_))
|
||||
static const int nC_ = GEOGRAPHICLIB_GEODESIC_ORDER + 1;
|
||||
static const unsigned maxit1_ = 20;
|
||||
unsigned maxit2_;
|
||||
real tiny_, tol0_, tol1_, tol2_, tolb_, xthresh_;
|
||||
|
||||
static constexpr unsigned CAP_NONE = 0U;
|
||||
static constexpr unsigned CAP_C1 = 1U<<0;
|
||||
static constexpr unsigned CAP_C1p = 1U<<1;
|
||||
static constexpr unsigned CAP_C2 = 1U<<2;
|
||||
static constexpr unsigned CAP_C3 = 1U<<3;
|
||||
static constexpr unsigned CAP_C4 = 1U<<4;
|
||||
static constexpr unsigned CAP_ALL = 0x1FU;
|
||||
static constexpr unsigned CAP_MASK = CAP_ALL;
|
||||
static constexpr unsigned OUT_ALL = 0x7F80U;
|
||||
static constexpr unsigned OUT_MASK = 0xFF80U; // Includes LONG_UNROLL
|
||||
|
||||
static real SinCosSeries(bool sinp,
|
||||
real sinx, real cosx, const real c[], int n);
|
||||
static real Astroid(real x, real y);
|
||||
|
||||
real _a, _f;
|
||||
bool _exact;
|
||||
real _f1, _e2, _ep2, _n, _b, _c2, _etol2;
|
||||
real _aA3x[nA3x_], _cC3x[nC3x_], _cC4x[nC4x_];
|
||||
GeodesicExact _geodexact;
|
||||
|
||||
void Lengths(real eps, real sig12,
|
||||
real ssig1, real csig1, real dn1,
|
||||
real ssig2, real csig2, real dn2,
|
||||
real cbet1, real cbet2, unsigned outmask,
|
||||
real& s12s, real& m12a, real& m0,
|
||||
real& M12, real& M21, real Ca[]) const;
|
||||
real InverseStart(real sbet1, real cbet1, real dn1,
|
||||
real sbet2, real cbet2, real dn2,
|
||||
real lam12, real slam12, real clam12,
|
||||
real& salp1, real& calp1,
|
||||
real& salp2, real& calp2, real& dnm,
|
||||
real Ca[]) const;
|
||||
real Lambda12(real sbet1, real cbet1, real dn1,
|
||||
real sbet2, real cbet2, real dn2,
|
||||
real salp1, real calp1, real slam120, real clam120,
|
||||
real& salp2, real& calp2, real& sig12,
|
||||
real& ssig1, real& csig1, real& ssig2, real& csig2,
|
||||
real& eps, real& domg12,
|
||||
bool diffp, real& dlam12, real Ca[]) const;
|
||||
real GenInverse(real lat1, real lon1, real lat2, real lon2,
|
||||
unsigned outmask, real& s12,
|
||||
real& salp1, real& calp1, real& salp2, real& calp2,
|
||||
real& m12, real& M12, real& M21, real& S12) const;
|
||||
|
||||
// These are Maxima generated functions to provide series approximations to
|
||||
// the integrals for the ellipsoidal geodesic.
|
||||
static real A1m1f(real eps);
|
||||
static void C1f(real eps, real c[]);
|
||||
static void C1pf(real eps, real c[]);
|
||||
static real A2m1f(real eps);
|
||||
static void C2f(real eps, real c[]);
|
||||
|
||||
void A3coeff();
|
||||
real A3f(real eps) const;
|
||||
void C3coeff();
|
||||
void C3f(real eps, real c[]) const;
|
||||
void C4coeff();
|
||||
void C4f(real k2, real c[]) const;
|
||||
public:
|
||||
|
||||
/**
|
||||
* Bit masks for what calculations to do. These masks do double duty.
|
||||
* They signify to the GeodesicLine constructor and to
|
||||
* Geodesic::Line what capabilities should be included in the GeodesicLine
|
||||
* object. They also specify which results to return in the general
|
||||
* routines Geodesic::GenDirect and Geodesic::GenInverse routines.
|
||||
* GeodesicLine::mask is a duplication of this enum.
|
||||
**********************************************************************/
|
||||
enum mask {
|
||||
/**
|
||||
* No capabilities, no output.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
NONE = 0U,
|
||||
/**
|
||||
* Calculate latitude \e lat2. (It's not necessary to include this as a
|
||||
* capability to GeodesicLine because this is included by default.)
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LATITUDE = 1U<<7 | CAP_NONE,
|
||||
/**
|
||||
* Calculate longitude \e lon2.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LONGITUDE = 1U<<8 | CAP_C3,
|
||||
/**
|
||||
* Calculate azimuths \e azi1 and \e azi2. (It's not necessary to
|
||||
* include this as a capability to GeodesicLine because this is included
|
||||
* by default.)
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
AZIMUTH = 1U<<9 | CAP_NONE,
|
||||
/**
|
||||
* Calculate distance \e s12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
DISTANCE = 1U<<10 | CAP_C1,
|
||||
/**
|
||||
* A combination of the common capabilities: Geodesic::LATITUDE,
|
||||
* Geodesic::LONGITUDE, Geodesic::AZIMUTH, Geodesic::DISTANCE.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
STANDARD = LATITUDE | LONGITUDE | AZIMUTH | DISTANCE,
|
||||
/**
|
||||
* Allow distance \e s12 to be used as input in the direct geodesic
|
||||
* problem.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
DISTANCE_IN = 1U<<11 | CAP_C1 | CAP_C1p,
|
||||
/**
|
||||
* Calculate reduced length \e m12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
REDUCEDLENGTH = 1U<<12 | CAP_C1 | CAP_C2,
|
||||
/**
|
||||
* Calculate geodesic scales \e M12 and \e M21.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
GEODESICSCALE = 1U<<13 | CAP_C1 | CAP_C2,
|
||||
/**
|
||||
* Calculate area \e S12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
AREA = 1U<<14 | CAP_C4,
|
||||
/**
|
||||
* Unroll \e lon2 in the direct calculation.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LONG_UNROLL = 1U<<15,
|
||||
/**
|
||||
* All capabilities, calculate everything. (Geodesic::LONG_UNROLL is not
|
||||
* included in this mask.)
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
ALL = OUT_ALL| CAP_ALL,
|
||||
};
|
||||
|
||||
/** \name Constructor
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Constructor for an ellipsoid with
|
||||
*
|
||||
* @param[in] a equatorial radius (meters).
|
||||
* @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
|
||||
* Negative \e f gives a prolate ellipsoid.
|
||||
* @param[in] exact if true use exact formulation in terms of elliptic
|
||||
* integrals instead of series expansions (default false).
|
||||
* @exception GeographicErr if \e a or (1 − \e f) \e a is not
|
||||
* positive.
|
||||
*
|
||||
* With \e exact = true, this class delegates the calculations to the
|
||||
* GeodesicExact and GeodesicLineExact classes which solve the geodesic
|
||||
* problems in terms of elliptic integrals.
|
||||
**********************************************************************/
|
||||
Geodesic(real a, real f, bool exact = false);
|
||||
///@}
|
||||
|
||||
/** \name Direct geodesic problem specified in terms of distance.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Solve the direct geodesic problem where the length of the geodesic
|
||||
* is specified in terms of distance.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] s12 distance between point 1 and point 2 (meters); it can be
|
||||
* negative.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees).
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] m12 reduced length of geodesic (meters).
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless).
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless).
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>).
|
||||
* @return \e a12 arc length of between point 1 and point 2 (degrees).
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°]. The values of
|
||||
* \e lon2 and \e azi2 returned are in the range [−180°,
|
||||
* 180°].
|
||||
*
|
||||
* If either point is at a pole, the azimuth is defined by keeping the
|
||||
* longitude fixed, writing \e lat = ±(90° − ε),
|
||||
* and taking the limit ε → 0+. An arc length greater that
|
||||
* 180° signifies a geodesic which is not a shortest path. (For a
|
||||
* prolate ellipsoid, an additional condition is necessary for a shortest
|
||||
* path: the longitudinal extent must not exceed of 180°.)
|
||||
*
|
||||
* The following functions are overloaded versions of Geodesic::Direct
|
||||
* which omit some of the output parameters. Note, however, that the arc
|
||||
* length is always computed and returned as the function value.
|
||||
**********************************************************************/
|
||||
Math::real Direct(real lat1, real lon1, real azi1, real s12,
|
||||
real& lat2, real& lon2, real& azi2,
|
||||
real& m12, real& M12, real& M21, real& S12)
|
||||
const {
|
||||
real t;
|
||||
return GenDirect(lat1, lon1, azi1, false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH |
|
||||
REDUCEDLENGTH | GEODESICSCALE | AREA,
|
||||
lat2, lon2, azi2, t, m12, M12, M21, S12);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::Direct.
|
||||
**********************************************************************/
|
||||
Math::real Direct(real lat1, real lon1, real azi1, real s12,
|
||||
real& lat2, real& lon2)
|
||||
const {
|
||||
real t;
|
||||
return GenDirect(lat1, lon1, azi1, false, s12,
|
||||
LATITUDE | LONGITUDE,
|
||||
lat2, lon2, t, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::Direct.
|
||||
**********************************************************************/
|
||||
Math::real Direct(real lat1, real lon1, real azi1, real s12,
|
||||
real& lat2, real& lon2, real& azi2)
|
||||
const {
|
||||
real t;
|
||||
return GenDirect(lat1, lon1, azi1, false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH,
|
||||
lat2, lon2, azi2, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::Direct.
|
||||
**********************************************************************/
|
||||
Math::real Direct(real lat1, real lon1, real azi1, real s12,
|
||||
real& lat2, real& lon2, real& azi2, real& m12)
|
||||
const {
|
||||
real t;
|
||||
return GenDirect(lat1, lon1, azi1, false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | REDUCEDLENGTH,
|
||||
lat2, lon2, azi2, t, m12, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::Direct.
|
||||
**********************************************************************/
|
||||
Math::real Direct(real lat1, real lon1, real azi1, real s12,
|
||||
real& lat2, real& lon2, real& azi2,
|
||||
real& M12, real& M21)
|
||||
const {
|
||||
real t;
|
||||
return GenDirect(lat1, lon1, azi1, false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | GEODESICSCALE,
|
||||
lat2, lon2, azi2, t, t, M12, M21, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::Direct.
|
||||
**********************************************************************/
|
||||
Math::real Direct(real lat1, real lon1, real azi1, real s12,
|
||||
real& lat2, real& lon2, real& azi2,
|
||||
real& m12, real& M12, real& M21)
|
||||
const {
|
||||
real t;
|
||||
return GenDirect(lat1, lon1, azi1, false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH |
|
||||
REDUCEDLENGTH | GEODESICSCALE,
|
||||
lat2, lon2, azi2, t, m12, M12, M21, t);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name Direct geodesic problem specified in terms of arc length.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Solve the direct geodesic problem where the length of the geodesic
|
||||
* is specified in terms of arc length.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] a12 arc length between point 1 and point 2 (degrees); it can
|
||||
* be negative.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees).
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] s12 distance between point 1 and point 2 (meters).
|
||||
* @param[out] m12 reduced length of geodesic (meters).
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless).
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless).
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>).
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°]. The values of
|
||||
* \e lon2 and \e azi2 returned are in the range [−180°,
|
||||
* 180°].
|
||||
*
|
||||
* If either point is at a pole, the azimuth is defined by keeping the
|
||||
* longitude fixed, writing \e lat = ±(90° − ε),
|
||||
* and taking the limit ε → 0+. An arc length greater that
|
||||
* 180° signifies a geodesic which is not a shortest path. (For a
|
||||
* prolate ellipsoid, an additional condition is necessary for a shortest
|
||||
* path: the longitudinal extent must not exceed of 180°.)
|
||||
*
|
||||
* The following functions are overloaded versions of Geodesic::Direct
|
||||
* which omit some of the output parameters.
|
||||
**********************************************************************/
|
||||
void ArcDirect(real lat1, real lon1, real azi1, real a12,
|
||||
real& lat2, real& lon2, real& azi2, real& s12,
|
||||
real& m12, real& M12, real& M21, real& S12)
|
||||
const {
|
||||
GenDirect(lat1, lon1, azi1, true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | DISTANCE |
|
||||
REDUCEDLENGTH | GEODESICSCALE | AREA,
|
||||
lat2, lon2, azi2, s12, m12, M12, M21, S12);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::ArcDirect.
|
||||
**********************************************************************/
|
||||
void ArcDirect(real lat1, real lon1, real azi1, real a12,
|
||||
real& lat2, real& lon2) const {
|
||||
real t;
|
||||
GenDirect(lat1, lon1, azi1, true, a12,
|
||||
LATITUDE | LONGITUDE,
|
||||
lat2, lon2, t, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::ArcDirect.
|
||||
**********************************************************************/
|
||||
void ArcDirect(real lat1, real lon1, real azi1, real a12,
|
||||
real& lat2, real& lon2, real& azi2) const {
|
||||
real t;
|
||||
GenDirect(lat1, lon1, azi1, true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH,
|
||||
lat2, lon2, azi2, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::ArcDirect.
|
||||
**********************************************************************/
|
||||
void ArcDirect(real lat1, real lon1, real azi1, real a12,
|
||||
real& lat2, real& lon2, real& azi2, real& s12)
|
||||
const {
|
||||
real t;
|
||||
GenDirect(lat1, lon1, azi1, true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | DISTANCE,
|
||||
lat2, lon2, azi2, s12, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::ArcDirect.
|
||||
**********************************************************************/
|
||||
void ArcDirect(real lat1, real lon1, real azi1, real a12,
|
||||
real& lat2, real& lon2, real& azi2,
|
||||
real& s12, real& m12) const {
|
||||
real t;
|
||||
GenDirect(lat1, lon1, azi1, true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | DISTANCE |
|
||||
REDUCEDLENGTH,
|
||||
lat2, lon2, azi2, s12, m12, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::ArcDirect.
|
||||
**********************************************************************/
|
||||
void ArcDirect(real lat1, real lon1, real azi1, real a12,
|
||||
real& lat2, real& lon2, real& azi2, real& s12,
|
||||
real& M12, real& M21) const {
|
||||
real t;
|
||||
GenDirect(lat1, lon1, azi1, true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | DISTANCE |
|
||||
GEODESICSCALE,
|
||||
lat2, lon2, azi2, s12, t, M12, M21, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::ArcDirect.
|
||||
**********************************************************************/
|
||||
void ArcDirect(real lat1, real lon1, real azi1, real a12,
|
||||
real& lat2, real& lon2, real& azi2, real& s12,
|
||||
real& m12, real& M12, real& M21) const {
|
||||
real t;
|
||||
GenDirect(lat1, lon1, azi1, true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | DISTANCE |
|
||||
REDUCEDLENGTH | GEODESICSCALE,
|
||||
lat2, lon2, azi2, s12, m12, M12, M21, t);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name General version of the direct geodesic solution.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* The general direct geodesic problem. Geodesic::Direct and
|
||||
* Geodesic::ArcDirect are defined in terms of this function.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] arcmode boolean flag determining the meaning of the \e
|
||||
* s12_a12.
|
||||
* @param[in] s12_a12 if \e arcmode is false, this is the distance between
|
||||
* point 1 and point 2 (meters); otherwise it is the arc length between
|
||||
* point 1 and point 2 (degrees); it can be negative.
|
||||
* @param[in] outmask a bitor'ed combination of Geodesic::mask values
|
||||
* specifying which of the following parameters should be set.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees).
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] s12 distance between point 1 and point 2 (meters).
|
||||
* @param[out] m12 reduced length of geodesic (meters).
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless).
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless).
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>).
|
||||
* @return \e a12 arc length of between point 1 and point 2 (degrees).
|
||||
*
|
||||
* The Geodesic::mask values possible for \e outmask are
|
||||
* - \e outmask |= Geodesic::LATITUDE for the latitude \e lat2;
|
||||
* - \e outmask |= Geodesic::LONGITUDE for the latitude \e lon2;
|
||||
* - \e outmask |= Geodesic::AZIMUTH for the latitude \e azi2;
|
||||
* - \e outmask |= Geodesic::DISTANCE for the distance \e s12;
|
||||
* - \e outmask |= Geodesic::REDUCEDLENGTH for the reduced length \e
|
||||
* m12;
|
||||
* - \e outmask |= Geodesic::GEODESICSCALE for the geodesic scales \e
|
||||
* M12 and \e M21;
|
||||
* - \e outmask |= Geodesic::AREA for the area \e S12;
|
||||
* - \e outmask |= Geodesic::ALL for all of the above;
|
||||
* - \e outmask |= Geodesic::LONG_UNROLL to unroll \e lon2 instead of
|
||||
* wrapping it into the range [−180°, 180°].
|
||||
* .
|
||||
* The function value \e a12 is always computed and returned and this
|
||||
* equals \e s12_a12 is \e arcmode is true. If \e outmask includes
|
||||
* Geodesic::DISTANCE and \e arcmode is false, then \e s12 = \e s12_a12.
|
||||
* It is not necessary to include Geodesic::DISTANCE_IN in \e outmask; this
|
||||
* is automatically included is \e arcmode is false.
|
||||
*
|
||||
* With the Geodesic::LONG_UNROLL bit set, the quantity \e lon2 − \e
|
||||
* lon1 indicates how many times and in what sense the geodesic encircles
|
||||
* the ellipsoid.
|
||||
**********************************************************************/
|
||||
Math::real GenDirect(real lat1, real lon1, real azi1,
|
||||
bool arcmode, real s12_a12, unsigned outmask,
|
||||
real& lat2, real& lon2, real& azi2,
|
||||
real& s12, real& m12, real& M12, real& M21,
|
||||
real& S12) const;
|
||||
///@}
|
||||
|
||||
/** \name Inverse geodesic problem.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Solve the inverse geodesic problem.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] lat2 latitude of point 2 (degrees).
|
||||
* @param[in] lon2 longitude of point 2 (degrees).
|
||||
* @param[out] s12 distance between point 1 and point 2 (meters).
|
||||
* @param[out] azi1 azimuth at point 1 (degrees).
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] m12 reduced length of geodesic (meters).
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless).
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless).
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>).
|
||||
* @return \e a12 arc length of between point 1 and point 2 (degrees).
|
||||
*
|
||||
* \e lat1 and \e lat2 should be in the range [−90°, 90°].
|
||||
* The values of \e azi1 and \e azi2 returned are in the range
|
||||
* [−180°, 180°].
|
||||
*
|
||||
* If either point is at a pole, the azimuth is defined by keeping the
|
||||
* longitude fixed, writing \e lat = ±(90° − ε),
|
||||
* and taking the limit ε → 0+.
|
||||
*
|
||||
* The solution to the inverse problem is found using Newton's method. If
|
||||
* this fails to converge (this is very unlikely in geodetic applications
|
||||
* but does occur for very eccentric ellipsoids), then the bisection method
|
||||
* is used to refine the solution.
|
||||
*
|
||||
* The following functions are overloaded versions of Geodesic::Inverse
|
||||
* which omit some of the output parameters. Note, however, that the arc
|
||||
* length is always computed and returned as the function value.
|
||||
**********************************************************************/
|
||||
Math::real Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& s12, real& azi1, real& azi2, real& m12,
|
||||
real& M12, real& M21, real& S12) const {
|
||||
return GenInverse(lat1, lon1, lat2, lon2,
|
||||
DISTANCE | AZIMUTH |
|
||||
REDUCEDLENGTH | GEODESICSCALE | AREA,
|
||||
s12, azi1, azi2, m12, M12, M21, S12);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::Inverse.
|
||||
**********************************************************************/
|
||||
Math::real Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& s12) const {
|
||||
real t;
|
||||
return GenInverse(lat1, lon1, lat2, lon2,
|
||||
DISTANCE,
|
||||
s12, t, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::Inverse.
|
||||
**********************************************************************/
|
||||
Math::real Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& azi1, real& azi2) const {
|
||||
real t;
|
||||
return GenInverse(lat1, lon1, lat2, lon2,
|
||||
AZIMUTH,
|
||||
t, azi1, azi2, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::Inverse.
|
||||
**********************************************************************/
|
||||
Math::real Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& s12, real& azi1, real& azi2)
|
||||
const {
|
||||
real t;
|
||||
return GenInverse(lat1, lon1, lat2, lon2,
|
||||
DISTANCE | AZIMUTH,
|
||||
s12, azi1, azi2, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::Inverse.
|
||||
**********************************************************************/
|
||||
Math::real Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& s12, real& azi1, real& azi2, real& m12)
|
||||
const {
|
||||
real t;
|
||||
return GenInverse(lat1, lon1, lat2, lon2,
|
||||
DISTANCE | AZIMUTH | REDUCEDLENGTH,
|
||||
s12, azi1, azi2, m12, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::Inverse.
|
||||
**********************************************************************/
|
||||
Math::real Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& s12, real& azi1, real& azi2,
|
||||
real& M12, real& M21) const {
|
||||
real t;
|
||||
return GenInverse(lat1, lon1, lat2, lon2,
|
||||
DISTANCE | AZIMUTH | GEODESICSCALE,
|
||||
s12, azi1, azi2, t, M12, M21, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for Geodesic::Inverse.
|
||||
**********************************************************************/
|
||||
Math::real Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& s12, real& azi1, real& azi2, real& m12,
|
||||
real& M12, real& M21) const {
|
||||
real t;
|
||||
return GenInverse(lat1, lon1, lat2, lon2,
|
||||
DISTANCE | AZIMUTH |
|
||||
REDUCEDLENGTH | GEODESICSCALE,
|
||||
s12, azi1, azi2, m12, M12, M21, t);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name General version of inverse geodesic solution.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* The general inverse geodesic calculation. Geodesic::Inverse is defined
|
||||
* in terms of this function.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] lat2 latitude of point 2 (degrees).
|
||||
* @param[in] lon2 longitude of point 2 (degrees).
|
||||
* @param[in] outmask a bitor'ed combination of Geodesic::mask values
|
||||
* specifying which of the following parameters should be set.
|
||||
* @param[out] s12 distance between point 1 and point 2 (meters).
|
||||
* @param[out] azi1 azimuth at point 1 (degrees).
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] m12 reduced length of geodesic (meters).
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless).
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless).
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>).
|
||||
* @return \e a12 arc length of between point 1 and point 2 (degrees).
|
||||
*
|
||||
* The Geodesic::mask values possible for \e outmask are
|
||||
* - \e outmask |= Geodesic::DISTANCE for the distance \e s12;
|
||||
* - \e outmask |= Geodesic::AZIMUTH for the latitude \e azi2;
|
||||
* - \e outmask |= Geodesic::REDUCEDLENGTH for the reduced length \e
|
||||
* m12;
|
||||
* - \e outmask |= Geodesic::GEODESICSCALE for the geodesic scales \e
|
||||
* M12 and \e M21;
|
||||
* - \e outmask |= Geodesic::AREA for the area \e S12;
|
||||
* - \e outmask |= Geodesic::ALL for all of the above.
|
||||
* .
|
||||
* The arc length is always computed and returned as the function value.
|
||||
**********************************************************************/
|
||||
Math::real GenInverse(real lat1, real lon1, real lat2, real lon2,
|
||||
unsigned outmask,
|
||||
real& s12, real& azi1, real& azi2,
|
||||
real& m12, real& M12, real& M21, real& S12) const;
|
||||
///@}
|
||||
|
||||
/** \name Interface to GeodesicLine.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Typedef for the class for computing multiple points on a geodesic.
|
||||
**********************************************************************/
|
||||
typedef GeodesicLine LineClass;
|
||||
|
||||
/**
|
||||
* Set up to compute several points on a single geodesic.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] caps bitor'ed combination of Geodesic::mask values
|
||||
* specifying the capabilities the GeodesicLine object should possess,
|
||||
* i.e., which quantities can be returned in calls to
|
||||
* GeodesicLine::Position.
|
||||
* @return a GeodesicLine object.
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°].
|
||||
*
|
||||
* The Geodesic::mask values are
|
||||
* - \e caps |= Geodesic::LATITUDE for the latitude \e lat2; this is
|
||||
* added automatically;
|
||||
* - \e caps |= Geodesic::LONGITUDE for the latitude \e lon2;
|
||||
* - \e caps |= Geodesic::AZIMUTH for the azimuth \e azi2; this is
|
||||
* added automatically;
|
||||
* - \e caps |= Geodesic::DISTANCE for the distance \e s12;
|
||||
* - \e caps |= Geodesic::REDUCEDLENGTH for the reduced length \e m12;
|
||||
* - \e caps |= Geodesic::GEODESICSCALE for the geodesic scales \e M12
|
||||
* and \e M21;
|
||||
* - \e caps |= Geodesic::AREA for the area \e S12;
|
||||
* - \e caps |= Geodesic::DISTANCE_IN permits the length of the
|
||||
* geodesic to be given in terms of \e s12; without this capability the
|
||||
* length can only be specified in terms of arc length;
|
||||
* - \e caps |= Geodesic::ALL for all of the above.
|
||||
* .
|
||||
* The default value of \e caps is Geodesic::ALL.
|
||||
*
|
||||
* If the point is at a pole, the azimuth is defined by keeping \e lon1
|
||||
* fixed, writing \e lat1 = ±(90 − ε), and taking the
|
||||
* limit ε → 0+.
|
||||
**********************************************************************/
|
||||
GeodesicLine Line(real lat1, real lon1, real azi1, unsigned caps = ALL)
|
||||
const;
|
||||
|
||||
/**
|
||||
* Define a GeodesicLine in terms of the inverse geodesic problem.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] lat2 latitude of point 2 (degrees).
|
||||
* @param[in] lon2 longitude of point 2 (degrees).
|
||||
* @param[in] caps bitor'ed combination of Geodesic::mask values
|
||||
* specifying the capabilities the GeodesicLine object should possess,
|
||||
* i.e., which quantities can be returned in calls to
|
||||
* GeodesicLine::Position.
|
||||
* @return a GeodesicLine object.
|
||||
*
|
||||
* This function sets point 3 of the GeodesicLine to correspond to point 2
|
||||
* of the inverse geodesic problem.
|
||||
*
|
||||
* \e lat1 and \e lat2 should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
GeodesicLine InverseLine(real lat1, real lon1, real lat2, real lon2,
|
||||
unsigned caps = ALL) const;
|
||||
|
||||
/**
|
||||
* Define a GeodesicLine in terms of the direct geodesic problem specified
|
||||
* in terms of distance.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] s12 distance between point 1 and point 2 (meters); it can be
|
||||
* negative.
|
||||
* @param[in] caps bitor'ed combination of Geodesic::mask values
|
||||
* specifying the capabilities the GeodesicLine object should possess,
|
||||
* i.e., which quantities can be returned in calls to
|
||||
* GeodesicLine::Position.
|
||||
* @return a GeodesicLine object.
|
||||
*
|
||||
* This function sets point 3 of the GeodesicLine to correspond to point 2
|
||||
* of the direct geodesic problem.
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
GeodesicLine DirectLine(real lat1, real lon1, real azi1, real s12,
|
||||
unsigned caps = ALL) const;
|
||||
|
||||
/**
|
||||
* Define a GeodesicLine in terms of the direct geodesic problem specified
|
||||
* in terms of arc length.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] a12 arc length between point 1 and point 2 (degrees); it can
|
||||
* be negative.
|
||||
* @param[in] caps bitor'ed combination of Geodesic::mask values
|
||||
* specifying the capabilities the GeodesicLine object should possess,
|
||||
* i.e., which quantities can be returned in calls to
|
||||
* GeodesicLine::Position.
|
||||
* @return a GeodesicLine object.
|
||||
*
|
||||
* This function sets point 3 of the GeodesicLine to correspond to point 2
|
||||
* of the direct geodesic problem.
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
GeodesicLine ArcDirectLine(real lat1, real lon1, real azi1, real a12,
|
||||
unsigned caps = ALL) const;
|
||||
|
||||
/**
|
||||
* Define a GeodesicLine in terms of the direct geodesic problem specified
|
||||
* in terms of either distance or arc length.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] arcmode boolean flag determining the meaning of the \e
|
||||
* s12_a12.
|
||||
* @param[in] s12_a12 if \e arcmode is false, this is the distance between
|
||||
* point 1 and point 2 (meters); otherwise it is the arc length between
|
||||
* point 1 and point 2 (degrees); it can be negative.
|
||||
* @param[in] caps bitor'ed combination of Geodesic::mask values
|
||||
* specifying the capabilities the GeodesicLine object should possess,
|
||||
* i.e., which quantities can be returned in calls to
|
||||
* GeodesicLine::Position.
|
||||
* @return a GeodesicLine object.
|
||||
*
|
||||
* This function sets point 3 of the GeodesicLine to correspond to point 2
|
||||
* of the direct geodesic problem.
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
GeodesicLine GenDirectLine(real lat1, real lon1, real azi1,
|
||||
bool arcmode, real s12_a12,
|
||||
unsigned caps = ALL) const;
|
||||
///@}
|
||||
|
||||
/** \name Inspector functions.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _a; }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the
|
||||
* value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _f; }
|
||||
|
||||
/**
|
||||
* @return \e exact whether the exact formulation is used. This is the
|
||||
* value used in the constructor.
|
||||
**********************************************************************/
|
||||
bool Exact() const { return _exact; }
|
||||
|
||||
/**
|
||||
* @return total area of ellipsoid in meters<sup>2</sup>. The area of a
|
||||
* polygon encircling a pole can be found by adding
|
||||
* Geodesic::EllipsoidArea()/2 to the sum of \e S12 for each side of the
|
||||
* polygon.
|
||||
**********************************************************************/
|
||||
Math::real EllipsoidArea() const
|
||||
{ return 4 * Math::pi() * _c2; }
|
||||
///@}
|
||||
|
||||
/**
|
||||
* A global instantiation of Geodesic with the parameters for the WGS84
|
||||
* ellipsoid.
|
||||
**********************************************************************/
|
||||
static const Geodesic& WGS84();
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_GEODESIC_HPP
|
||||
+883
@@ -0,0 +1,883 @@
|
||||
/**
|
||||
* \file GeodesicExact.hpp
|
||||
* \brief Header for GeographicLib::GeodesicExact class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2012-2024) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GEODESICEXACT_HPP)
|
||||
#define GEOGRAPHICLIB_GEODESICEXACT_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/EllipticFunction.hpp>
|
||||
#include <GeographicLib/DST.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
class GeodesicLineExact;
|
||||
// Visual Studio needs this forward declaration...
|
||||
class GEOGRAPHICLIB_EXPORT DST;
|
||||
|
||||
/**
|
||||
* \brief Exact geodesic calculations
|
||||
*
|
||||
* The equations for geodesics on an ellipsoid can be expressed in terms of
|
||||
* incomplete elliptic integrals. The Geodesic class expands these integrals
|
||||
* in a series in the flattening \e f and this provides an accurate solution
|
||||
* for \e f ∈ [-0.01, 0.01]. The GeodesicExact class computes the
|
||||
* elliptic integrals directly and so provides a solution which is valid for
|
||||
* all \e f. However, in practice, its use should be limited to about
|
||||
* <i>b</i>/\e a ∈ [0.01, 100] or \e f ∈ [−99, 0.99].
|
||||
*
|
||||
* For the WGS84 ellipsoid, these classes are 2--3 times \e slower than the
|
||||
* series solution and 2--3 times \e less \e accurate (because it's less easy
|
||||
* to control round-off errors with the elliptic integral formulation); i.e.,
|
||||
* the error is about 40 nm (40 nanometers) instead of 15 nm. However the
|
||||
* error in the series solution scales as <i>f</i><sup>7</sup> while the
|
||||
* error in the elliptic integral solution depends weakly on \e f. If the
|
||||
* quarter meridian distance is 10000 km and the ratio <i>b</i>/\e a = 1
|
||||
* − \e f is varied then the approximate maximum error (expressed as a
|
||||
* distance) is <pre>
|
||||
* 1 - f error (nm)
|
||||
* 1/128 387
|
||||
* 1/64 345
|
||||
* 1/32 269
|
||||
* 1/16 210
|
||||
* 1/8 115
|
||||
* 1/4 69
|
||||
* 1/2 36
|
||||
* 1 15
|
||||
* 2 25
|
||||
* 4 96
|
||||
* 8 318
|
||||
* 16 985
|
||||
* 32 2352
|
||||
* 64 6008
|
||||
* 128 19024
|
||||
* </pre>
|
||||
*
|
||||
* The area in this classes is computing by finding an accurate approximation
|
||||
* to the area integrand using a discrete sine transform fitting \e N equally
|
||||
* spaced points in σ. \e N chosen to ensure full accuracy for
|
||||
* <i>b</i>/\e a ∈ [0.01, 100] or \e f ∈ [−99, 0.99].
|
||||
*
|
||||
* The algorithms are described in
|
||||
* - C. F. F. Karney,
|
||||
* <a href="https://doi.org/10.1007/s00190-023-01813-2">
|
||||
* Geodesics on an arbitrary ellipsoid of revolution</a>,
|
||||
* J. Geodesy <b>98</b>, 4:1--14 (2024);
|
||||
* DOI: <a href="https://doi.org/10.1007/s00190-023-01813-2">
|
||||
* 10.1007/s00190-023-01813-2</a>.
|
||||
* .
|
||||
* See \ref geodellip for the formulation. See the documentation on the
|
||||
* Geodesic class for additional information on the geodesic problems.
|
||||
*
|
||||
* \note Instead of using this class directly, it is recommended to use the
|
||||
* Geodesic class, specifying \e exact = true in the constructor.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-GeodesicExact.cpp
|
||||
*
|
||||
* <a href="GeodSolve.1.html">GeodSolve</a> is a command-line utility
|
||||
* providing access to the functionality of GeodesicExact and
|
||||
* GeodesicLineExact (via the -E option).
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT GeodesicExact {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
friend class GeodesicLineExact;
|
||||
friend class Geodesic; // Allow Geodesic to call the default constructor
|
||||
// Private default constructor to support Geodesic(a, f, exact)
|
||||
GeodesicExact() {}; // Do nothing; used with exact = false.
|
||||
|
||||
static const unsigned maxit1_ = 20;
|
||||
unsigned maxit2_;
|
||||
real tiny_, tol0_, tol1_, tol2_, tolb_, xthresh_;
|
||||
|
||||
static constexpr unsigned CAP_NONE = 0U;
|
||||
static constexpr unsigned CAP_E = 1U<<0;
|
||||
// Skip 1U<<1 for compatibility with Geodesic (not required)
|
||||
static constexpr unsigned CAP_D = 1U<<2;
|
||||
static constexpr unsigned CAP_H = 1U<<3;
|
||||
static constexpr unsigned CAP_C4 = 1U<<4;
|
||||
static constexpr unsigned CAP_ALL = 0x1FU;
|
||||
static constexpr unsigned CAP_MASK = CAP_ALL;
|
||||
static constexpr unsigned OUT_ALL = 0x7F80U;
|
||||
static constexpr unsigned OUT_MASK = 0xFF80U; // Includes LONG_UNROLL
|
||||
|
||||
static real Astroid(real x, real y);
|
||||
|
||||
real _a, _f, _f1, _e2, _ep2, _n, _b, _c2, _etol2;
|
||||
int _nC4;
|
||||
DST _fft;
|
||||
|
||||
void Lengths(const EllipticFunction& E,
|
||||
real sig12,
|
||||
real ssig1, real csig1, real dn1,
|
||||
real ssig2, real csig2, real dn2,
|
||||
real cbet1, real cbet2, unsigned outmask,
|
||||
real& s12s, real& m12a, real& m0,
|
||||
real& M12, real& M21) const;
|
||||
real InverseStart(EllipticFunction& E,
|
||||
real sbet1, real cbet1, real dn1,
|
||||
real sbet2, real cbet2, real dn2,
|
||||
real lam12, real slam12, real clam12,
|
||||
real& salp1, real& calp1,
|
||||
real& salp2, real& calp2, real& dnm) const;
|
||||
real Lambda12(real sbet1, real cbet1, real dn1,
|
||||
real sbet2, real cbet2, real dn2,
|
||||
real salp1, real calp1, real slam120, real clam120,
|
||||
real& salp2, real& calp2, real& sig12,
|
||||
real& ssig1, real& csig1, real& ssig2, real& csig2,
|
||||
EllipticFunction& E,
|
||||
real& domg12, bool diffp, real& dlam12) const;
|
||||
real GenInverse(real lat1, real lon1, real lat2, real lon2,
|
||||
unsigned outmask, real& s12,
|
||||
real& salp1, real& calp1, real& salp2, real& calp2,
|
||||
real& m12, real& M12, real& M21, real& S12) const;
|
||||
|
||||
class I4Integrand {
|
||||
private:
|
||||
real X, tX, tdX, sX, sX1, sXX1, asinhsX, _k2;
|
||||
static real asinhsqrt(real x);
|
||||
static real t(real x);
|
||||
static real td(real x);
|
||||
// static real Dt(real x, real y);
|
||||
real DtX(real y) const;
|
||||
public:
|
||||
I4Integrand(real ep2, real k2);
|
||||
real operator()(real sig) const;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Bit masks for what calculations to do. These masks do double duty.
|
||||
* They signify to the GeodesicLineExact::GeodesicLineExact constructor and
|
||||
* to GeodesicExact::Line what capabilities should be included in the
|
||||
* GeodesicLineExact object. They also specify which results to return in
|
||||
* the general routines GeodesicExact::GenDirect and
|
||||
* GeodesicExact::GenInverse routines. GeodesicLineExact::mask is a
|
||||
* duplication of this enum.
|
||||
**********************************************************************/
|
||||
enum mask {
|
||||
/**
|
||||
* No capabilities, no output.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
NONE = 0U,
|
||||
/**
|
||||
* Calculate latitude \e lat2. (It's not necessary to include this as a
|
||||
* capability to GeodesicLineExact because this is included by default.)
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LATITUDE = 1U<<7 | CAP_NONE,
|
||||
/**
|
||||
* Calculate longitude \e lon2.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LONGITUDE = 1U<<8 | CAP_H,
|
||||
/**
|
||||
* Calculate azimuths \e azi1 and \e azi2. (It's not necessary to
|
||||
* include this as a capability to GeodesicLineExact because this is
|
||||
* included by default.)
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
AZIMUTH = 1U<<9 | CAP_NONE,
|
||||
/**
|
||||
* Calculate distance \e s12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
DISTANCE = 1U<<10 | CAP_E,
|
||||
/**
|
||||
* A combination of the common capabilities: GeodesicExact::LATITUDE,
|
||||
* GeodesicExact::LONGITUDE, GeodesicExact::AZIMUTH,
|
||||
* GeodesicExact::DISTANCE.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
STANDARD = LATITUDE | LONGITUDE | AZIMUTH | DISTANCE,
|
||||
/**
|
||||
* Allow distance \e s12 to be used as input in the direct geodesic
|
||||
* problem.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
DISTANCE_IN = 1U<<11 | CAP_E,
|
||||
/**
|
||||
* Calculate reduced length \e m12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
REDUCEDLENGTH = 1U<<12 | CAP_D,
|
||||
/**
|
||||
* Calculate geodesic scales \e M12 and \e M21.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
GEODESICSCALE = 1U<<13 | CAP_D,
|
||||
/**
|
||||
* Calculate area \e S12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
AREA = 1U<<14 | CAP_C4,
|
||||
/**
|
||||
* Unroll \e lon2 in the direct calculation.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LONG_UNROLL = 1U<<15,
|
||||
/**
|
||||
* All capabilities, calculate everything. (GeodesicExact::LONG_UNROLL
|
||||
* is not included in this mask.)
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
ALL = OUT_ALL| CAP_ALL,
|
||||
};
|
||||
|
||||
/** \name Constructor
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Constructor for an ellipsoid with
|
||||
*
|
||||
* @param[in] a equatorial radius (meters).
|
||||
* @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
|
||||
* Negative \e f gives a prolate ellipsoid.
|
||||
* @exception GeographicErr if \e a or (1 − \e f) \e a is not
|
||||
* positive.
|
||||
**********************************************************************/
|
||||
GeodesicExact(real a, real f);
|
||||
///@}
|
||||
|
||||
/** \name Direct geodesic problem specified in terms of distance.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Perform the direct geodesic calculation where the length of the geodesic
|
||||
* is specified in terms of distance.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] s12 distance between point 1 and point 2 (meters); it can be
|
||||
* signed.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees).
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] m12 reduced length of geodesic (meters).
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless).
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless).
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>).
|
||||
* @return \e a12 arc length of between point 1 and point 2 (degrees).
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°]. The values of
|
||||
* \e lon2 and \e azi2 returned are in the range [−180°,
|
||||
* 180°].
|
||||
*
|
||||
* If either point is at a pole, the azimuth is defined by keeping the
|
||||
* longitude fixed, writing \e lat = ±(90° − ε),
|
||||
* and taking the limit ε → 0+. An arc length greater that
|
||||
* 180° signifies a geodesic which is not a shortest path. (For a
|
||||
* prolate ellipsoid, an additional condition is necessary for a shortest
|
||||
* path: the longitudinal extent must not exceed of 180°.)
|
||||
*
|
||||
* The following functions are overloaded versions of GeodesicExact::Direct
|
||||
* which omit some of the output parameters. Note, however, that the arc
|
||||
* length is always computed and returned as the function value.
|
||||
**********************************************************************/
|
||||
Math::real Direct(real lat1, real lon1, real azi1, real s12,
|
||||
real& lat2, real& lon2, real& azi2,
|
||||
real& m12, real& M12, real& M21, real& S12)
|
||||
const {
|
||||
real t;
|
||||
return GenDirect(lat1, lon1, azi1, false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH |
|
||||
REDUCEDLENGTH | GEODESICSCALE | AREA,
|
||||
lat2, lon2, azi2, t, m12, M12, M21, S12);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::Direct.
|
||||
**********************************************************************/
|
||||
Math::real Direct(real lat1, real lon1, real azi1, real s12,
|
||||
real& lat2, real& lon2)
|
||||
const {
|
||||
real t;
|
||||
return GenDirect(lat1, lon1, azi1, false, s12,
|
||||
LATITUDE | LONGITUDE,
|
||||
lat2, lon2, t, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::Direct.
|
||||
**********************************************************************/
|
||||
Math::real Direct(real lat1, real lon1, real azi1, real s12,
|
||||
real& lat2, real& lon2, real& azi2)
|
||||
const {
|
||||
real t;
|
||||
return GenDirect(lat1, lon1, azi1, false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH,
|
||||
lat2, lon2, azi2, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::Direct.
|
||||
**********************************************************************/
|
||||
Math::real Direct(real lat1, real lon1, real azi1, real s12,
|
||||
real& lat2, real& lon2, real& azi2, real& m12)
|
||||
const {
|
||||
real t;
|
||||
return GenDirect(lat1, lon1, azi1, false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | REDUCEDLENGTH,
|
||||
lat2, lon2, azi2, t, m12, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::Direct.
|
||||
**********************************************************************/
|
||||
Math::real Direct(real lat1, real lon1, real azi1, real s12,
|
||||
real& lat2, real& lon2, real& azi2,
|
||||
real& M12, real& M21)
|
||||
const {
|
||||
real t;
|
||||
return GenDirect(lat1, lon1, azi1, false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | GEODESICSCALE,
|
||||
lat2, lon2, azi2, t, t, M12, M21, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::Direct.
|
||||
**********************************************************************/
|
||||
Math::real Direct(real lat1, real lon1, real azi1, real s12,
|
||||
real& lat2, real& lon2, real& azi2,
|
||||
real& m12, real& M12, real& M21)
|
||||
const {
|
||||
real t;
|
||||
return GenDirect(lat1, lon1, azi1, false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH |
|
||||
REDUCEDLENGTH | GEODESICSCALE,
|
||||
lat2, lon2, azi2, t, m12, M12, M21, t);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name Direct geodesic problem specified in terms of arc length.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Perform the direct geodesic calculation where the length of the geodesic
|
||||
* is specified in terms of arc length.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] a12 arc length between point 1 and point 2 (degrees); it can
|
||||
* be signed.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees).
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] s12 distance between point 1 and point 2 (meters).
|
||||
* @param[out] m12 reduced length of geodesic (meters).
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless).
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless).
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>).
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°]. The values of
|
||||
* \e lon2 and \e azi2 returned are in the range [−180°,
|
||||
* 180°].
|
||||
*
|
||||
* If either point is at a pole, the azimuth is defined by keeping the
|
||||
* longitude fixed, writing \e lat = ±(90° − ε),
|
||||
* and taking the limit ε → 0+. An arc length greater that
|
||||
* 180° signifies a geodesic which is not a shortest path. (For a
|
||||
* prolate ellipsoid, an additional condition is necessary for a shortest
|
||||
* path: the longitudinal extent must not exceed of 180°.)
|
||||
*
|
||||
* The following functions are overloaded versions of GeodesicExact::Direct
|
||||
* which omit some of the output parameters.
|
||||
**********************************************************************/
|
||||
void ArcDirect(real lat1, real lon1, real azi1, real a12,
|
||||
real& lat2, real& lon2, real& azi2, real& s12,
|
||||
real& m12, real& M12, real& M21, real& S12)
|
||||
const {
|
||||
GenDirect(lat1, lon1, azi1, true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | DISTANCE |
|
||||
REDUCEDLENGTH | GEODESICSCALE | AREA,
|
||||
lat2, lon2, azi2, s12, m12, M12, M21, S12);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::ArcDirect.
|
||||
**********************************************************************/
|
||||
void ArcDirect(real lat1, real lon1, real azi1, real a12,
|
||||
real& lat2, real& lon2) const {
|
||||
real t;
|
||||
GenDirect(lat1, lon1, azi1, true, a12,
|
||||
LATITUDE | LONGITUDE,
|
||||
lat2, lon2, t, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::ArcDirect.
|
||||
**********************************************************************/
|
||||
void ArcDirect(real lat1, real lon1, real azi1, real a12,
|
||||
real& lat2, real& lon2, real& azi2) const {
|
||||
real t;
|
||||
GenDirect(lat1, lon1, azi1, true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH,
|
||||
lat2, lon2, azi2, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::ArcDirect.
|
||||
**********************************************************************/
|
||||
void ArcDirect(real lat1, real lon1, real azi1, real a12,
|
||||
real& lat2, real& lon2, real& azi2, real& s12)
|
||||
const {
|
||||
real t;
|
||||
GenDirect(lat1, lon1, azi1, true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | DISTANCE,
|
||||
lat2, lon2, azi2, s12, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::ArcDirect.
|
||||
**********************************************************************/
|
||||
void ArcDirect(real lat1, real lon1, real azi1, real a12,
|
||||
real& lat2, real& lon2, real& azi2,
|
||||
real& s12, real& m12) const {
|
||||
real t;
|
||||
GenDirect(lat1, lon1, azi1, true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | DISTANCE |
|
||||
REDUCEDLENGTH,
|
||||
lat2, lon2, azi2, s12, m12, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::ArcDirect.
|
||||
**********************************************************************/
|
||||
void ArcDirect(real lat1, real lon1, real azi1, real a12,
|
||||
real& lat2, real& lon2, real& azi2, real& s12,
|
||||
real& M12, real& M21) const {
|
||||
real t;
|
||||
GenDirect(lat1, lon1, azi1, true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | DISTANCE |
|
||||
GEODESICSCALE,
|
||||
lat2, lon2, azi2, s12, t, M12, M21, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::ArcDirect.
|
||||
**********************************************************************/
|
||||
void ArcDirect(real lat1, real lon1, real azi1, real a12,
|
||||
real& lat2, real& lon2, real& azi2, real& s12,
|
||||
real& m12, real& M12, real& M21) const {
|
||||
real t;
|
||||
GenDirect(lat1, lon1, azi1, true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | DISTANCE |
|
||||
REDUCEDLENGTH | GEODESICSCALE,
|
||||
lat2, lon2, azi2, s12, m12, M12, M21, t);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name General version of the direct geodesic solution.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* The general direct geodesic calculation. GeodesicExact::Direct and
|
||||
* GeodesicExact::ArcDirect are defined in terms of this function.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] arcmode boolean flag determining the meaning of the second
|
||||
* parameter.
|
||||
* @param[in] s12_a12 if \e arcmode is false, this is the distance between
|
||||
* point 1 and point 2 (meters); otherwise it is the arc length between
|
||||
* point 1 and point 2 (degrees); it can be signed.
|
||||
* @param[in] outmask a bitor'ed combination of GeodesicExact::mask values
|
||||
* specifying which of the following parameters should be set.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees).
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] s12 distance between point 1 and point 2 (meters).
|
||||
* @param[out] m12 reduced length of geodesic (meters).
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless).
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless).
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>).
|
||||
* @return \e a12 arc length of between point 1 and point 2 (degrees).
|
||||
*
|
||||
* The GeodesicExact::mask values possible for \e outmask are
|
||||
* - \e outmask |= GeodesicExact::LATITUDE for the latitude \e lat2;
|
||||
* - \e outmask |= GeodesicExact::LONGITUDE for the latitude \e lon2;
|
||||
* - \e outmask |= GeodesicExact::AZIMUTH for the latitude \e azi2;
|
||||
* - \e outmask |= GeodesicExact::DISTANCE for the distance \e s12;
|
||||
* - \e outmask |= GeodesicExact::REDUCEDLENGTH for the reduced length \e
|
||||
* m12;
|
||||
* - \e outmask |= GeodesicExact::GEODESICSCALE for the geodesic scales \e
|
||||
* M12 and \e M21;
|
||||
* - \e outmask |= GeodesicExact::AREA for the area \e S12;
|
||||
* - \e outmask |= GeodesicExact::ALL for all of the above;
|
||||
* - \e outmask |= GeodesicExact::LONG_UNROLL to unroll \e lon2 instead of
|
||||
* wrapping it into the range [−180°, 180°].
|
||||
* .
|
||||
* The function value \e a12 is always computed and returned and this
|
||||
* equals \e s12_a12 is \e arcmode is true. If \e outmask includes
|
||||
* GeodesicExact::DISTANCE and \e arcmode is false, then \e s12 = \e
|
||||
* s12_a12. It is not necessary to include GeodesicExact::DISTANCE_IN in
|
||||
* \e outmask; this is automatically included is \e arcmode is false.
|
||||
*
|
||||
* With the GeodesicExact::LONG_UNROLL bit set, the quantity \e lon2
|
||||
* − \e lon1 indicates how many times and in what sense the geodesic
|
||||
* encircles the ellipsoid.
|
||||
**********************************************************************/
|
||||
Math::real GenDirect(real lat1, real lon1, real azi1,
|
||||
bool arcmode, real s12_a12, unsigned outmask,
|
||||
real& lat2, real& lon2, real& azi2,
|
||||
real& s12, real& m12, real& M12, real& M21,
|
||||
real& S12) const;
|
||||
///@}
|
||||
|
||||
/** \name Inverse geodesic problem.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Perform the inverse geodesic calculation.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] lat2 latitude of point 2 (degrees).
|
||||
* @param[in] lon2 longitude of point 2 (degrees).
|
||||
* @param[out] s12 distance between point 1 and point 2 (meters).
|
||||
* @param[out] azi1 azimuth at point 1 (degrees).
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] m12 reduced length of geodesic (meters).
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless).
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless).
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>).
|
||||
* @return \e a12 arc length of between point 1 and point 2 (degrees).
|
||||
*
|
||||
* \e lat1 and \e lat2 should be in the range [−90°, 90°].
|
||||
* The values of \e azi1 and \e azi2 returned are in the range
|
||||
* [−180°, 180°].
|
||||
*
|
||||
* If either point is at a pole, the azimuth is defined by keeping the
|
||||
* longitude fixed, writing \e lat = ±(90° − ε),
|
||||
* and taking the limit ε → 0+.
|
||||
*
|
||||
* The following functions are overloaded versions of
|
||||
* GeodesicExact::Inverse which omit some of the output parameters. Note,
|
||||
* however, that the arc length is always computed and returned as the
|
||||
* function value.
|
||||
**********************************************************************/
|
||||
Math::real Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& s12, real& azi1, real& azi2, real& m12,
|
||||
real& M12, real& M21, real& S12) const {
|
||||
return GenInverse(lat1, lon1, lat2, lon2,
|
||||
DISTANCE | AZIMUTH |
|
||||
REDUCEDLENGTH | GEODESICSCALE | AREA,
|
||||
s12, azi1, azi2, m12, M12, M21, S12);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::Inverse.
|
||||
**********************************************************************/
|
||||
Math::real Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& s12) const {
|
||||
real t;
|
||||
return GenInverse(lat1, lon1, lat2, lon2,
|
||||
DISTANCE,
|
||||
s12, t, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::Inverse.
|
||||
**********************************************************************/
|
||||
Math::real Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& azi1, real& azi2) const {
|
||||
real t;
|
||||
return GenInverse(lat1, lon1, lat2, lon2,
|
||||
AZIMUTH,
|
||||
t, azi1, azi2, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::Inverse.
|
||||
**********************************************************************/
|
||||
Math::real Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& s12, real& azi1, real& azi2)
|
||||
const {
|
||||
real t;
|
||||
return GenInverse(lat1, lon1, lat2, lon2,
|
||||
DISTANCE | AZIMUTH,
|
||||
s12, azi1, azi2, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::Inverse.
|
||||
**********************************************************************/
|
||||
Math::real Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& s12, real& azi1, real& azi2, real& m12)
|
||||
const {
|
||||
real t;
|
||||
return GenInverse(lat1, lon1, lat2, lon2,
|
||||
DISTANCE | AZIMUTH | REDUCEDLENGTH,
|
||||
s12, azi1, azi2, m12, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::Inverse.
|
||||
**********************************************************************/
|
||||
Math::real Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& s12, real& azi1, real& azi2,
|
||||
real& M12, real& M21) const {
|
||||
real t;
|
||||
return GenInverse(lat1, lon1, lat2, lon2,
|
||||
DISTANCE | AZIMUTH | GEODESICSCALE,
|
||||
s12, azi1, azi2, t, M12, M21, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicExact::Inverse.
|
||||
**********************************************************************/
|
||||
Math::real Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& s12, real& azi1, real& azi2, real& m12,
|
||||
real& M12, real& M21) const {
|
||||
real t;
|
||||
return GenInverse(lat1, lon1, lat2, lon2,
|
||||
DISTANCE | AZIMUTH |
|
||||
REDUCEDLENGTH | GEODESICSCALE,
|
||||
s12, azi1, azi2, m12, M12, M21, t);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name General version of inverse geodesic solution.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* The general inverse geodesic calculation. GeodesicExact::Inverse is
|
||||
* defined in terms of this function.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] lat2 latitude of point 2 (degrees).
|
||||
* @param[in] lon2 longitude of point 2 (degrees).
|
||||
* @param[in] outmask a bitor'ed combination of GeodesicExact::mask values
|
||||
* specifying which of the following parameters should be set.
|
||||
* @param[out] s12 distance between point 1 and point 2 (meters).
|
||||
* @param[out] azi1 azimuth at point 1 (degrees).
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] m12 reduced length of geodesic (meters).
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless).
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless).
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>).
|
||||
* @return \e a12 arc length of between point 1 and point 2 (degrees).
|
||||
*
|
||||
* The GeodesicExact::mask values possible for \e outmask are
|
||||
* - \e outmask |= GeodesicExact::DISTANCE for the distance \e s12;
|
||||
* - \e outmask |= GeodesicExact::AZIMUTH for the latitude \e azi2;
|
||||
* - \e outmask |= GeodesicExact::REDUCEDLENGTH for the reduced length \e
|
||||
* m12;
|
||||
* - \e outmask |= GeodesicExact::GEODESICSCALE for the geodesic scales \e
|
||||
* M12 and \e M21;
|
||||
* - \e outmask |= GeodesicExact::AREA for the area \e S12;
|
||||
* - \e outmask |= GeodesicExact::ALL for all of the above.
|
||||
* .
|
||||
* The arc length is always computed and returned as the function value.
|
||||
**********************************************************************/
|
||||
Math::real GenInverse(real lat1, real lon1, real lat2, real lon2,
|
||||
unsigned outmask,
|
||||
real& s12, real& azi1, real& azi2,
|
||||
real& m12, real& M12, real& M21, real& S12) const;
|
||||
///@}
|
||||
|
||||
/** \name Interface to GeodesicLineExact.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Typedef for the class for computing multiple points on a geodesic.
|
||||
**********************************************************************/
|
||||
typedef GeodesicLineExact LineClass;
|
||||
|
||||
/**
|
||||
* Set up to compute several points on a single geodesic.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] caps bitor'ed combination of GeodesicExact::mask values
|
||||
* specifying the capabilities the GeodesicLineExact object should
|
||||
* possess, i.e., which quantities can be returned in calls to
|
||||
* GeodesicLineExact::Position.
|
||||
* @return a GeodesicLineExact object.
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°].
|
||||
*
|
||||
* The GeodesicExact::mask values are
|
||||
* - \e caps |= GeodesicExact::LATITUDE for the latitude \e lat2; this is
|
||||
* added automatically;
|
||||
* - \e caps |= GeodesicExact::LONGITUDE for the latitude \e lon2;
|
||||
* - \e caps |= GeodesicExact::AZIMUTH for the azimuth \e azi2; this is
|
||||
* added automatically;
|
||||
* - \e caps |= GeodesicExact::DISTANCE for the distance \e s12;
|
||||
* - \e caps |= GeodesicExact::REDUCEDLENGTH for the reduced length \e m12;
|
||||
* - \e caps |= GeodesicExact::GEODESICSCALE for the geodesic scales \e M12
|
||||
* and \e M21;
|
||||
* - \e caps |= GeodesicExact::AREA for the area \e S12;
|
||||
* - \e caps |= GeodesicExact::DISTANCE_IN permits the length of the
|
||||
* geodesic to be given in terms of \e s12; without this capability the
|
||||
* length can only be specified in terms of arc length;
|
||||
* - \e caps |= GeodesicExact::ALL for all of the above.
|
||||
* .
|
||||
* The default value of \e caps is GeodesicExact::ALL which turns on all
|
||||
* the capabilities.
|
||||
*
|
||||
* If the point is at a pole, the azimuth is defined by keeping \e lon1
|
||||
* fixed, writing \e lat1 = ±(90 − ε), and taking the
|
||||
* limit ε → 0+.
|
||||
**********************************************************************/
|
||||
GeodesicLineExact Line(real lat1, real lon1, real azi1,
|
||||
unsigned caps = ALL) const;
|
||||
|
||||
/**
|
||||
* Define a GeodesicLineExact in terms of the inverse geodesic problem.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] lat2 latitude of point 2 (degrees).
|
||||
* @param[in] lon2 longitude of point 2 (degrees).
|
||||
* @param[in] caps bitor'ed combination of GeodesicExact::mask values
|
||||
* specifying the capabilities the GeodesicLineExact object should
|
||||
* possess, i.e., which quantities can be returned in calls to
|
||||
* GeodesicLineExact::Position.
|
||||
* @return a GeodesicLineExact object.
|
||||
*
|
||||
* This function sets point 3 of the GeodesicLineExact to correspond to
|
||||
* point 2 of the inverse geodesic problem.
|
||||
*
|
||||
* \e lat1 and \e lat2 should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
GeodesicLineExact InverseLine(real lat1, real lon1, real lat2, real lon2,
|
||||
unsigned caps = ALL) const;
|
||||
|
||||
/**
|
||||
* Define a GeodesicLineExact in terms of the direct geodesic problem
|
||||
* specified in terms of distance.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] s12 distance between point 1 and point 2 (meters); it can be
|
||||
* negative.
|
||||
* @param[in] caps bitor'ed combination of GeodesicExact::mask values
|
||||
* specifying the capabilities the GeodesicLineExact object should
|
||||
* possess, i.e., which quantities can be returned in calls to
|
||||
* GeodesicLineExact::Position.
|
||||
* @return a GeodesicLineExact object.
|
||||
*
|
||||
* This function sets point 3 of the GeodesicLineExact to correspond to
|
||||
* point 2 of the direct geodesic problem.
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
GeodesicLineExact DirectLine(real lat1, real lon1, real azi1, real s12,
|
||||
unsigned caps = ALL) const;
|
||||
|
||||
/**
|
||||
* Define a GeodesicLineExact in terms of the direct geodesic problem
|
||||
* specified in terms of arc length.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] a12 arc length between point 1 and point 2 (degrees); it can
|
||||
* be negative.
|
||||
* @param[in] caps bitor'ed combination of GeodesicExact::mask values
|
||||
* specifying the capabilities the GeodesicLineExact object should
|
||||
* possess, i.e., which quantities can be returned in calls to
|
||||
* GeodesicLineExact::Position.
|
||||
* @return a GeodesicLineExact object.
|
||||
*
|
||||
* This function sets point 3 of the GeodesicLineExact to correspond to
|
||||
* point 2 of the direct geodesic problem.
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
GeodesicLineExact ArcDirectLine(real lat1, real lon1, real azi1, real a12,
|
||||
unsigned caps = ALL) const;
|
||||
|
||||
/**
|
||||
* Define a GeodesicLineExact in terms of the direct geodesic problem
|
||||
* specified in terms of either distance or arc length.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] arcmode boolean flag determining the meaning of the \e
|
||||
* s12_a12.
|
||||
* @param[in] s12_a12 if \e arcmode is false, this is the distance between
|
||||
* point 1 and point 2 (meters); otherwise it is the arc length between
|
||||
* point 1 and point 2 (degrees); it can be negative.
|
||||
* @param[in] caps bitor'ed combination of GeodesicExact::mask values
|
||||
* specifying the capabilities the GeodesicLineExact object should
|
||||
* possess, i.e., which quantities can be returned in calls to
|
||||
* GeodesicLineExact::Position.
|
||||
* @return a GeodesicLineExact object.
|
||||
*
|
||||
* This function sets point 3 of the GeodesicLineExact to correspond to
|
||||
* point 2 of the direct geodesic problem.
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
GeodesicLineExact GenDirectLine(real lat1, real lon1, real azi1,
|
||||
bool arcmode, real s12_a12,
|
||||
unsigned caps = ALL) const;
|
||||
///@}
|
||||
|
||||
/** \name Inspector functions.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _a; }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the
|
||||
* value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _f; }
|
||||
|
||||
/**
|
||||
* @return total area of ellipsoid in meters<sup>2</sup>. The area of a
|
||||
* polygon encircling a pole can be found by adding
|
||||
* GeodesicExact::EllipsoidArea()/2 to the sum of \e S12 for each side of
|
||||
* the polygon.
|
||||
**********************************************************************/
|
||||
Math::real EllipsoidArea() const
|
||||
{ return 4 * Math::pi() * _c2; }
|
||||
///@}
|
||||
|
||||
/**
|
||||
* A global instantiation of GeodesicExact with the parameters for the
|
||||
* WGS84 ellipsoid.
|
||||
**********************************************************************/
|
||||
static const GeodesicExact& WGS84();
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_GEODESICEXACT_HPP
|
||||
+724
@@ -0,0 +1,724 @@
|
||||
/**
|
||||
* \file GeodesicLine.hpp
|
||||
* \brief Header for GeographicLib::GeodesicLine class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2009-2024) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GEODESICLINE_HPP)
|
||||
#define GEOGRAPHICLIB_GEODESICLINE_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/Geodesic.hpp>
|
||||
#include <GeographicLib/GeodesicLineExact.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief A geodesic line
|
||||
*
|
||||
* GeodesicLine facilitates the determination of a series of points on a
|
||||
* single geodesic. The starting point (\e lat1, \e lon1) and the azimuth \e
|
||||
* azi1 are specified in the constructor; alternatively, the Geodesic::Line
|
||||
* method can be used to create a GeodesicLine. GeodesicLine.Position
|
||||
* returns the location of point 2 a distance \e s12 along the geodesic. In
|
||||
* addition, GeodesicLine.ArcPosition gives the position of point 2 an arc
|
||||
* length \e a12 along the geodesic.
|
||||
*
|
||||
* You can register the position of a reference point 3 a distance (arc
|
||||
* length), \e s13 (\e a13) along the geodesic with the
|
||||
* GeodesicLine.SetDistance (GeodesicLine.SetArc) functions. Points a
|
||||
* fractional distance along the line can be found by providing, for example,
|
||||
* 0.5 * Distance() as an argument to GeodesicLine.Position. The
|
||||
* Geodesic::InverseLine or Geodesic::DirectLine methods return GeodesicLine
|
||||
* objects with point 3 set to the point 2 of the corresponding geodesic
|
||||
* problem. GeodesicLine objects created with the public constructor or with
|
||||
* Geodesic::Line have \e s13 and \e a13 set to NaNs.
|
||||
*
|
||||
* The default copy constructor and assignment operators work with this
|
||||
* class. Similarly, a vector can be used to hold GeodesicLine objects.
|
||||
*
|
||||
* The calculations are accurate to better than 15 nm (15 nanometers). See
|
||||
* Sec. 9 of
|
||||
* <a href="https://arxiv.org/abs/1102.1215v1">arXiv:1102.1215v1</a> for
|
||||
* details. With \e exact = false (the default) in the constructor for the
|
||||
* Geodesic object, the algorithms used by this class are based on series
|
||||
* expansions using the flattening \e f as a small parameter. These are only
|
||||
* accurate for |<i>f</i>| < 0.02; however reasonably accurate results
|
||||
* will be obtained for |<i>f</i>| < 0.2. For very eccentric ellipsoids,
|
||||
* set \e exact = true in the constructor for the Geodesic object; this will
|
||||
* delegate the calculations to GeodesicLineExact.
|
||||
*
|
||||
* The algorithms are described in
|
||||
* - C. F. F. Karney,
|
||||
* <a href="https://doi.org/10.1007/s00190-012-0578-z">
|
||||
* Algorithms for geodesics</a>,
|
||||
* J. Geodesy <b>87</b>, 43--55 (2013);
|
||||
* DOI: <a href="https://doi.org/10.1007/s00190-012-0578-z">
|
||||
* 10.1007/s00190-012-0578-z</a>;
|
||||
* addenda:
|
||||
* <a href="https://geographiclib.sourceforge.io/geod-addenda.html">
|
||||
* geod-addenda.html</a>.
|
||||
* .
|
||||
* For more information on geodesics see \ref geodesic.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-GeodesicLine.cpp
|
||||
*
|
||||
* <a href="GeodSolve.1.html">GeodSolve</a> is a command-line utility
|
||||
* providing access to the functionality of Geodesic and GeodesicLine.
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT GeodesicLine {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
friend class Geodesic;
|
||||
static const int nC1_ = Geodesic::nC1_;
|
||||
static const int nC1p_ = Geodesic::nC1p_;
|
||||
static const int nC2_ = Geodesic::nC2_;
|
||||
static const int nC3_ = Geodesic::nC3_;
|
||||
static const int nC4_ = Geodesic::nC4_;
|
||||
|
||||
real tiny_;
|
||||
real _lat1, _lon1, _azi1;
|
||||
real _a, _f;
|
||||
bool _exact;
|
||||
real _b, _c2, _f1, _salp0, _calp0, _k2,
|
||||
_salp1, _calp1, _ssig1, _csig1, _dn1, _stau1, _ctau1, _somg1, _comg1,
|
||||
_aA1m1, _aA2m1, _aA3c, _bB11, _bB21, _bB31, _aA4, _bB41;
|
||||
real _a13, _s13;
|
||||
// index zero elements of _cC1a, _cC1pa, _cC2a, _cC3a are unused
|
||||
real _cC1a[nC1_ + 1], _cC1pa[nC1p_ + 1], _cC2a[nC2_ + 1], _cC3a[nC3_],
|
||||
_cC4a[nC4_]; // all the elements of _cC4a are used
|
||||
unsigned _caps;
|
||||
GeodesicLineExact _lineexact;
|
||||
|
||||
void LineInit(const Geodesic& g,
|
||||
real lat1, real lon1,
|
||||
real azi1, real salp1, real calp1,
|
||||
unsigned caps);
|
||||
GeodesicLine(const Geodesic& g,
|
||||
real lat1, real lon1,
|
||||
real azi1, real salp1, real calp1,
|
||||
unsigned caps, bool arcmode, real s13_a13);
|
||||
|
||||
static constexpr unsigned CAP_NONE = Geodesic::CAP_NONE;
|
||||
static constexpr unsigned CAP_C1 = Geodesic::CAP_C1;
|
||||
static constexpr unsigned CAP_C1p = Geodesic::CAP_C1p;
|
||||
static constexpr unsigned CAP_C2 = Geodesic::CAP_C2;
|
||||
static constexpr unsigned CAP_C3 = Geodesic::CAP_C3;
|
||||
static constexpr unsigned CAP_C4 = Geodesic::CAP_C4;
|
||||
static constexpr unsigned CAP_ALL = Geodesic::CAP_ALL;
|
||||
static constexpr unsigned CAP_MASK = Geodesic::CAP_MASK;
|
||||
static constexpr unsigned OUT_ALL = Geodesic::OUT_ALL;
|
||||
static constexpr unsigned OUT_MASK = Geodesic::OUT_MASK;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Bit masks for what calculations to do. They signify to the
|
||||
* GeodesicLine::GeodesicLine constructor and to Geodesic::Line what
|
||||
* capabilities should be included in the GeodesicLine object. This is
|
||||
* merely a duplication of Geodesic::mask.
|
||||
**********************************************************************/
|
||||
enum mask {
|
||||
/**
|
||||
* No capabilities, no output.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
NONE = Geodesic::NONE,
|
||||
/**
|
||||
* Calculate latitude \e lat2. (It's not necessary to include this as a
|
||||
* capability to GeodesicLine because this is included by default.)
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LATITUDE = Geodesic::LATITUDE,
|
||||
/**
|
||||
* Calculate longitude \e lon2.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LONGITUDE = Geodesic::LONGITUDE,
|
||||
/**
|
||||
* Calculate azimuths \e azi1 and \e azi2. (It's not necessary to
|
||||
* include this as a capability to GeodesicLine because this is included
|
||||
* by default.)
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
AZIMUTH = Geodesic::AZIMUTH,
|
||||
/**
|
||||
* Calculate distance \e s12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
DISTANCE = Geodesic::DISTANCE,
|
||||
/**
|
||||
* A combination of the common capabilities: GeodesicLine::LATITUDE,
|
||||
* GeodesicLine::LONGITUDE, GeodesicLine::AZIMUTH, GeodesicLine::DISTANCE.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
STANDARD = Geodesic::STANDARD,
|
||||
/**
|
||||
* Allow distance \e s12 to be used as input in the direct geodesic
|
||||
* problem.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
DISTANCE_IN = Geodesic::DISTANCE_IN,
|
||||
/**
|
||||
* Calculate reduced length \e m12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
REDUCEDLENGTH = Geodesic::REDUCEDLENGTH,
|
||||
/**
|
||||
* Calculate geodesic scales \e M12 and \e M21.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
GEODESICSCALE = Geodesic::GEODESICSCALE,
|
||||
/**
|
||||
* Calculate area \e S12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
AREA = Geodesic::AREA,
|
||||
/**
|
||||
* Unroll \e lon2 in the direct calculation.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LONG_UNROLL = Geodesic::LONG_UNROLL,
|
||||
/**
|
||||
* All capabilities, calculate everything. (GeodesicLine::LONG_UNROLL is
|
||||
* not included in this mask.)
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
ALL = Geodesic::ALL,
|
||||
};
|
||||
|
||||
/**
|
||||
* Typedef for the base class implementing geodesics.
|
||||
**********************************************************************/
|
||||
typedef Geodesic BaseClass;
|
||||
|
||||
/** \name Constructors
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Constructor for a geodesic line staring at latitude \e lat1, longitude
|
||||
* \e lon1, and azimuth \e azi1 (all in degrees).
|
||||
*
|
||||
* @param[in] g A Geodesic object used to compute the necessary information
|
||||
* about the GeodesicLine.
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] caps bitor'ed combination of GeodesicLine::mask values
|
||||
* specifying the capabilities the GeodesicLine object should possess,
|
||||
* i.e., which quantities can be returned in calls to
|
||||
* GeodesicLine::Position.
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°].
|
||||
*
|
||||
* The GeodesicLine::mask values are
|
||||
* - \e caps |= GeodesicLine::LATITUDE for the latitude \e lat2; this is
|
||||
* added automatically;
|
||||
* - \e caps |= GeodesicLine::LONGITUDE for the latitude \e lon2;
|
||||
* - \e caps |= GeodesicLine::AZIMUTH for the latitude \e azi2; this is
|
||||
* added automatically;
|
||||
* - \e caps |= GeodesicLine::DISTANCE for the distance \e s12;
|
||||
* - \e caps |= GeodesicLine::REDUCEDLENGTH for the reduced length \e m12;
|
||||
* - \e caps |= GeodesicLine::GEODESICSCALE for the geodesic scales \e M12
|
||||
* and \e M21;
|
||||
* - \e caps |= GeodesicLine::AREA for the area \e S12;
|
||||
* - \e caps |= GeodesicLine::DISTANCE_IN permits the length of the
|
||||
* geodesic to be given in terms of \e s12; without this capability the
|
||||
* length can only be specified in terms of arc length;
|
||||
* - \e caps |= GeodesicLine::ALL for all of the above.
|
||||
* .
|
||||
* The default value of \e caps is GeodesicLine::ALL.
|
||||
*
|
||||
* If the point is at a pole, the azimuth is defined by keeping \e lon1
|
||||
* fixed, writing \e lat1 = ±(90° − ε), and taking
|
||||
* the limit ε → 0+.
|
||||
**********************************************************************/
|
||||
GeodesicLine(const Geodesic& g, real lat1, real lon1, real azi1,
|
||||
unsigned caps = ALL);
|
||||
|
||||
/**
|
||||
* A default constructor. If GeodesicLine::Position is called on the
|
||||
* resulting object, it returns immediately (without doing any
|
||||
* calculations). The object can be set with a call to Geodesic::Line.
|
||||
* Use Init() to test whether object is still in this uninitialized state.
|
||||
**********************************************************************/
|
||||
GeodesicLine() : _caps(0U) {}
|
||||
///@}
|
||||
|
||||
/** \name Position in terms of distance
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Compute the position of point 2 which is a distance \e s12 (meters) from
|
||||
* point 1.
|
||||
*
|
||||
* @param[in] s12 distance from point 1 to point 2 (meters); it can be
|
||||
* negative.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees); requires that the
|
||||
* GeodesicLine object was constructed with \e caps |=
|
||||
* GeodesicLine::LONGITUDE.
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] m12 reduced length of geodesic (meters); requires that the
|
||||
* GeodesicLine object was constructed with \e caps |=
|
||||
* GeodesicLine::REDUCEDLENGTH.
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless); requires that the GeodesicLine object was constructed
|
||||
* with \e caps |= GeodesicLine::GEODESICSCALE.
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless); requires that the GeodesicLine object was constructed
|
||||
* with \e caps |= GeodesicLine::GEODESICSCALE.
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>); requires
|
||||
* that the GeodesicLine object was constructed with \e caps |=
|
||||
* GeodesicLine::AREA.
|
||||
* @return \e a12 arc length from point 1 to point 2 (degrees).
|
||||
*
|
||||
* The values of \e lon2 and \e azi2 returned are in the range
|
||||
* [−180°, 180°].
|
||||
*
|
||||
* The GeodesicLine object \e must have been constructed with \e caps |=
|
||||
* GeodesicLine::DISTANCE_IN; otherwise Math::NaN() is returned and no
|
||||
* parameters are set. Requesting a value which the GeodesicLine object is
|
||||
* not capable of computing is not an error; the corresponding argument
|
||||
* will not be altered.
|
||||
*
|
||||
* The following functions are overloaded versions of
|
||||
* GeodesicLine::Position which omit some of the output parameters. Note,
|
||||
* however, that the arc length is always computed and returned as the
|
||||
* function value.
|
||||
**********************************************************************/
|
||||
Math::real Position(real s12,
|
||||
real& lat2, real& lon2, real& azi2,
|
||||
real& m12, real& M12, real& M21,
|
||||
real& S12) const {
|
||||
real t;
|
||||
return GenPosition(false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH |
|
||||
REDUCEDLENGTH | GEODESICSCALE | AREA,
|
||||
lat2, lon2, azi2, t, m12, M12, M21, S12);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLine::Position.
|
||||
**********************************************************************/
|
||||
Math::real Position(real s12, real& lat2, real& lon2) const {
|
||||
real t;
|
||||
return GenPosition(false, s12,
|
||||
LATITUDE | LONGITUDE,
|
||||
lat2, lon2, t, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLine::Position.
|
||||
**********************************************************************/
|
||||
Math::real Position(real s12, real& lat2, real& lon2,
|
||||
real& azi2) const {
|
||||
real t;
|
||||
return GenPosition(false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH,
|
||||
lat2, lon2, azi2, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLine::Position.
|
||||
**********************************************************************/
|
||||
Math::real Position(real s12, real& lat2, real& lon2,
|
||||
real& azi2, real& m12) const {
|
||||
real t;
|
||||
return GenPosition(false, s12,
|
||||
LATITUDE | LONGITUDE |
|
||||
AZIMUTH | REDUCEDLENGTH,
|
||||
lat2, lon2, azi2, t, m12, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLine::Position.
|
||||
**********************************************************************/
|
||||
Math::real Position(real s12, real& lat2, real& lon2,
|
||||
real& azi2, real& M12, real& M21)
|
||||
const {
|
||||
real t;
|
||||
return GenPosition(false, s12,
|
||||
LATITUDE | LONGITUDE |
|
||||
AZIMUTH | GEODESICSCALE,
|
||||
lat2, lon2, azi2, t, t, M12, M21, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLine::Position.
|
||||
**********************************************************************/
|
||||
Math::real Position(real s12,
|
||||
real& lat2, real& lon2, real& azi2,
|
||||
real& m12, real& M12, real& M21)
|
||||
const {
|
||||
real t;
|
||||
return GenPosition(false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH |
|
||||
REDUCEDLENGTH | GEODESICSCALE,
|
||||
lat2, lon2, azi2, t, m12, M12, M21, t);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name Position in terms of arc length
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Compute the position of point 2 which is an arc length \e a12 (degrees)
|
||||
* from point 1.
|
||||
*
|
||||
* @param[in] a12 arc length from point 1 to point 2 (degrees); it can
|
||||
* be negative.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees); requires that the
|
||||
* GeodesicLine object was constructed with \e caps |=
|
||||
* GeodesicLine::LONGITUDE.
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] s12 distance from point 1 to point 2 (meters); requires
|
||||
* that the GeodesicLine object was constructed with \e caps |=
|
||||
* GeodesicLine::DISTANCE.
|
||||
* @param[out] m12 reduced length of geodesic (meters); requires that the
|
||||
* GeodesicLine object was constructed with \e caps |=
|
||||
* GeodesicLine::REDUCEDLENGTH.
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless); requires that the GeodesicLine object was constructed
|
||||
* with \e caps |= GeodesicLine::GEODESICSCALE.
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless); requires that the GeodesicLine object was constructed
|
||||
* with \e caps |= GeodesicLine::GEODESICSCALE.
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>); requires
|
||||
* that the GeodesicLine object was constructed with \e caps |=
|
||||
* GeodesicLine::AREA.
|
||||
*
|
||||
* The values of \e lon2 and \e azi2 returned are in the range
|
||||
* [−180°, 180°].
|
||||
*
|
||||
* Requesting a value which the GeodesicLine object is not capable of
|
||||
* computing is not an error; the corresponding argument will not be
|
||||
* altered.
|
||||
*
|
||||
* The following functions are overloaded versions of
|
||||
* GeodesicLine::ArcPosition which omit some of the output parameters.
|
||||
**********************************************************************/
|
||||
void ArcPosition(real a12, real& lat2, real& lon2, real& azi2,
|
||||
real& s12, real& m12, real& M12, real& M21,
|
||||
real& S12) const {
|
||||
GenPosition(true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | DISTANCE |
|
||||
REDUCEDLENGTH | GEODESICSCALE | AREA,
|
||||
lat2, lon2, azi2, s12, m12, M12, M21, S12);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLine::ArcPosition.
|
||||
**********************************************************************/
|
||||
void ArcPosition(real a12, real& lat2, real& lon2)
|
||||
const {
|
||||
real t;
|
||||
GenPosition(true, a12,
|
||||
LATITUDE | LONGITUDE,
|
||||
lat2, lon2, t, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLine::ArcPosition.
|
||||
**********************************************************************/
|
||||
void ArcPosition(real a12,
|
||||
real& lat2, real& lon2, real& azi2)
|
||||
const {
|
||||
real t;
|
||||
GenPosition(true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH,
|
||||
lat2, lon2, azi2, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLine::ArcPosition.
|
||||
**********************************************************************/
|
||||
void ArcPosition(real a12, real& lat2, real& lon2, real& azi2,
|
||||
real& s12) const {
|
||||
real t;
|
||||
GenPosition(true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | DISTANCE,
|
||||
lat2, lon2, azi2, s12, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLine::ArcPosition.
|
||||
**********************************************************************/
|
||||
void ArcPosition(real a12, real& lat2, real& lon2, real& azi2,
|
||||
real& s12, real& m12) const {
|
||||
real t;
|
||||
GenPosition(true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH |
|
||||
DISTANCE | REDUCEDLENGTH,
|
||||
lat2, lon2, azi2, s12, m12, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLine::ArcPosition.
|
||||
**********************************************************************/
|
||||
void ArcPosition(real a12, real& lat2, real& lon2, real& azi2,
|
||||
real& s12, real& M12, real& M21)
|
||||
const {
|
||||
real t;
|
||||
GenPosition(true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH |
|
||||
DISTANCE | GEODESICSCALE,
|
||||
lat2, lon2, azi2, s12, t, M12, M21, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLine::ArcPosition.
|
||||
**********************************************************************/
|
||||
void ArcPosition(real a12, real& lat2, real& lon2, real& azi2,
|
||||
real& s12, real& m12, real& M12, real& M21)
|
||||
const {
|
||||
real t;
|
||||
GenPosition(true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH |
|
||||
DISTANCE | REDUCEDLENGTH | GEODESICSCALE,
|
||||
lat2, lon2, azi2, s12, m12, M12, M21, t);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name The general position function.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* The general position function. GeodesicLine::Position and
|
||||
* GeodesicLine::ArcPosition are defined in terms of this function.
|
||||
*
|
||||
* @param[in] arcmode boolean flag determining the meaning of the second
|
||||
* parameter; if \e arcmode is false, then the GeodesicLine object must
|
||||
* have been constructed with \e caps |= GeodesicLine::DISTANCE_IN.
|
||||
* @param[in] s12_a12 if \e arcmode is false, this is the distance between
|
||||
* point 1 and point 2 (meters); otherwise it is the arc length between
|
||||
* point 1 and point 2 (degrees); it can be negative.
|
||||
* @param[in] outmask a bitor'ed combination of GeodesicLine::mask values
|
||||
* specifying which of the following parameters should be set.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees); requires that the
|
||||
* GeodesicLine object was constructed with \e caps |=
|
||||
* GeodesicLine::LONGITUDE.
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] s12 distance from point 1 to point 2 (meters); requires
|
||||
* that the GeodesicLine object was constructed with \e caps |=
|
||||
* GeodesicLine::DISTANCE.
|
||||
* @param[out] m12 reduced length of geodesic (meters); requires that the
|
||||
* GeodesicLine object was constructed with \e caps |=
|
||||
* GeodesicLine::REDUCEDLENGTH.
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless); requires that the GeodesicLine object was constructed
|
||||
* with \e caps |= GeodesicLine::GEODESICSCALE.
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless); requires that the GeodesicLine object was constructed
|
||||
* with \e caps |= GeodesicLine::GEODESICSCALE.
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>); requires
|
||||
* that the GeodesicLine object was constructed with \e caps |=
|
||||
* GeodesicLine::AREA.
|
||||
* @return \e a12 arc length from point 1 to point 2 (degrees).
|
||||
*
|
||||
* The GeodesicLine::mask values possible for \e outmask are
|
||||
* - \e outmask |= GeodesicLine::LATITUDE for the latitude \e lat2;
|
||||
* - \e outmask |= GeodesicLine::LONGITUDE for the latitude \e lon2;
|
||||
* - \e outmask |= GeodesicLine::AZIMUTH for the latitude \e azi2;
|
||||
* - \e outmask |= GeodesicLine::DISTANCE for the distance \e s12;
|
||||
* - \e outmask |= GeodesicLine::REDUCEDLENGTH for the reduced length \e
|
||||
* m12;
|
||||
* - \e outmask |= GeodesicLine::GEODESICSCALE for the geodesic scales \e
|
||||
* M12 and \e M21;
|
||||
* - \e outmask |= GeodesicLine::AREA for the area \e S12;
|
||||
* - \e outmask |= GeodesicLine::ALL for all of the above;
|
||||
* - \e outmask |= GeodesicLine::LONG_UNROLL to unroll \e lon2 instead of
|
||||
* reducing it into the range [−180°, 180°].
|
||||
* .
|
||||
* Requesting a value which the GeodesicLine object is not capable of
|
||||
* computing is not an error; the corresponding argument will not be
|
||||
* altered. Note, however, that the arc length is always computed and
|
||||
* returned as the function value.
|
||||
*
|
||||
* With the GeodesicLine::LONG_UNROLL bit set, the quantity \e lon2 −
|
||||
* \e lon1 indicates how many times and in what sense the geodesic
|
||||
* encircles the ellipsoid.
|
||||
**********************************************************************/
|
||||
Math::real GenPosition(bool arcmode, real s12_a12, unsigned outmask,
|
||||
real& lat2, real& lon2, real& azi2,
|
||||
real& s12, real& m12, real& M12, real& M21,
|
||||
real& S12) const;
|
||||
///@}
|
||||
|
||||
/** \name Setting point 3
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Specify position of point 3 in terms of distance.
|
||||
*
|
||||
* @param[in] s13 the distance from point 1 to point 3 (meters); it
|
||||
* can be negative.
|
||||
*
|
||||
* This is only useful if the GeodesicLine object has been constructed
|
||||
* with \e caps |= GeodesicLine::DISTANCE_IN.
|
||||
**********************************************************************/
|
||||
void SetDistance(real s13);
|
||||
|
||||
/**
|
||||
* Specify position of point 3 in terms of arc length.
|
||||
*
|
||||
* @param[in] a13 the arc length from point 1 to point 3 (degrees); it
|
||||
* can be negative.
|
||||
*
|
||||
* The distance \e s13 is only set if the GeodesicLine object has been
|
||||
* constructed with \e caps |= GeodesicLine::DISTANCE.
|
||||
**********************************************************************/
|
||||
void SetArc(real a13);
|
||||
|
||||
/**
|
||||
* Specify position of point 3 in terms of either distance or arc length.
|
||||
*
|
||||
* @param[in] arcmode boolean flag determining the meaning of the second
|
||||
* parameter; if \e arcmode is false, then the GeodesicLine object must
|
||||
* have been constructed with \e caps |= GeodesicLine::DISTANCE_IN.
|
||||
* @param[in] s13_a13 if \e arcmode is false, this is the distance from
|
||||
* point 1 to point 3 (meters); otherwise it is the arc length from
|
||||
* point 1 to point 3 (degrees); it can be negative.
|
||||
**********************************************************************/
|
||||
void GenSetDistance(bool arcmode, real s13_a13);
|
||||
///@}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @return true if the object has been initialized.
|
||||
**********************************************************************/
|
||||
bool Init() const { return _caps != 0U; }
|
||||
|
||||
/**
|
||||
* @return \e lat1 the latitude of point 1 (degrees).
|
||||
**********************************************************************/
|
||||
Math::real Latitude() const
|
||||
{ return Init() ? _lat1 : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return \e lon1 the longitude of point 1 (degrees).
|
||||
**********************************************************************/
|
||||
Math::real Longitude() const
|
||||
{ return Init() ? _lon1 : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return \e azi1 the azimuth (degrees) of the geodesic line at point 1.
|
||||
**********************************************************************/
|
||||
Math::real Azimuth() const
|
||||
{ return Init() ? _azi1 : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* The sine and cosine of \e azi1.
|
||||
*
|
||||
* @param[out] sazi1 the sine of \e azi1.
|
||||
* @param[out] cazi1 the cosine of \e azi1.
|
||||
**********************************************************************/
|
||||
void Azimuth(real& sazi1, real& cazi1) const
|
||||
{ if (Init()) { sazi1 = _salp1; cazi1 = _calp1; } }
|
||||
|
||||
/**
|
||||
* @return \e azi0 the azimuth (degrees) of the geodesic line as it crosses
|
||||
* the equator in a northward direction.
|
||||
*
|
||||
* The result lies in [−90°, 90°].
|
||||
**********************************************************************/
|
||||
Math::real EquatorialAzimuth() const
|
||||
{ return Init() ? Math::atan2d(_salp0, _calp0) : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* The sine and cosine of \e azi0.
|
||||
*
|
||||
* @param[out] sazi0 the sine of \e azi0.
|
||||
* @param[out] cazi0 the cosine of \e azi0.
|
||||
**********************************************************************/
|
||||
void EquatorialAzimuth(real& sazi0, real& cazi0) const
|
||||
{ if (Init()) { sazi0 = _salp0; cazi0 = _calp0; } }
|
||||
|
||||
/**
|
||||
* @return \e a1 the arc length (degrees) between the northward equatorial
|
||||
* crossing and point 1.
|
||||
*
|
||||
* The result lies in [−180°, 180°].
|
||||
**********************************************************************/
|
||||
Math::real EquatorialArc() const {
|
||||
return Init() ? Math::atan2d(_ssig1, _csig1) : Math::NaN();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value inherited from the Geodesic object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const
|
||||
{ return Init() ? _a : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the value
|
||||
* inherited from the Geodesic object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const
|
||||
{ return Init() ? _f : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return \e exact whether the exact formulation is used. This is the
|
||||
* value returned by the Geodesic object used in the constructor.
|
||||
**********************************************************************/
|
||||
bool Exact() const { return _exact; }
|
||||
|
||||
/**
|
||||
* @return \e caps the computational capabilities that this object was
|
||||
* constructed with. LATITUDE and AZIMUTH are always included.
|
||||
**********************************************************************/
|
||||
unsigned Capabilities() const { return _caps; }
|
||||
|
||||
/**
|
||||
* Test what capabilities are available.
|
||||
*
|
||||
* @param[in] testcaps a set of bitor'ed GeodesicLine::mask values.
|
||||
* @return true if the GeodesicLine object has all these capabilities.
|
||||
**********************************************************************/
|
||||
bool Capabilities(unsigned testcaps) const {
|
||||
testcaps &= OUT_ALL;
|
||||
return (_caps & testcaps) == testcaps;
|
||||
}
|
||||
|
||||
/**
|
||||
* The distance or arc length to point 3.
|
||||
*
|
||||
* @param[in] arcmode boolean flag determining the meaning of returned
|
||||
* value.
|
||||
* @return \e s13 if \e arcmode is false; \e a13 if \e arcmode is true.
|
||||
**********************************************************************/
|
||||
Math::real GenDistance(bool arcmode) const
|
||||
{ return Init() ? (arcmode ? _a13 : _s13) : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return \e s13, the distance to point 3 (meters).
|
||||
**********************************************************************/
|
||||
Math::real Distance() const { return GenDistance(false); }
|
||||
|
||||
/**
|
||||
* @return \e a13, the arc length to point 3 (degrees).
|
||||
**********************************************************************/
|
||||
Math::real Arc() const { return GenDistance(true); }
|
||||
///@}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_GEODESICLINE_HPP
|
||||
+682
@@ -0,0 +1,682 @@
|
||||
/**
|
||||
* \file GeodesicLineExact.hpp
|
||||
* \brief Header for GeographicLib::GeodesicLineExact class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2012-2024) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GEODESICLINEEXACT_HPP)
|
||||
#define GEOGRAPHICLIB_GEODESICLINEEXACT_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/GeodesicExact.hpp>
|
||||
#include <GeographicLib/EllipticFunction.hpp>
|
||||
#include <vector>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Squelch warnings about dll vs vector
|
||||
# pragma warning (push)
|
||||
# pragma warning (disable: 4251)
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief An exact geodesic line
|
||||
*
|
||||
* GeodesicLineExact facilitates the determination of a series of points on a
|
||||
* single geodesic. This is a companion to the GeodesicExact class. For
|
||||
* additional information on this class see the documentation on the
|
||||
* GeodesicLine class.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-GeodesicLineExact.cpp
|
||||
*
|
||||
* <a href="GeodSolve.1.html">GeodSolve</a> is a command-line utility
|
||||
* providing access to the functionality of GeodesicExact and
|
||||
* GeodesicLineExact (via the -E option).
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT GeodesicLineExact {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
friend class GeodesicExact;
|
||||
friend class GeodesicLine;
|
||||
int _nC4;
|
||||
|
||||
real tiny_;
|
||||
real _lat1, _lon1, _azi1;
|
||||
real _a, _f, _b, _c2, _f1, _e2, _salp0, _calp0, _k2,
|
||||
_salp1, _calp1, _ssig1, _csig1, _dn1, _stau1, _ctau1,
|
||||
_somg1, _comg1, _cchi1,
|
||||
_aA4, _eE0, _dD0, _hH0, _eE1, _dD1, _hH1;
|
||||
real _a13, _s13;
|
||||
real _bB41;
|
||||
std::vector<real> _cC4a;
|
||||
EllipticFunction _eE;
|
||||
unsigned _caps;
|
||||
|
||||
void LineInit(const GeodesicExact& g,
|
||||
real lat1, real lon1,
|
||||
real azi1, real salp1, real calp1,
|
||||
unsigned caps);
|
||||
GeodesicLineExact(const GeodesicExact& g,
|
||||
real lat1, real lon1,
|
||||
real azi1, real salp1, real calp1,
|
||||
unsigned caps, bool arcmode, real s13_a13);
|
||||
|
||||
static constexpr unsigned CAP_NONE = GeodesicExact::CAP_NONE;
|
||||
static constexpr unsigned CAP_E = GeodesicExact::CAP_E;
|
||||
static constexpr unsigned CAP_D = GeodesicExact::CAP_D;
|
||||
static constexpr unsigned CAP_H = GeodesicExact::CAP_H;
|
||||
static constexpr unsigned CAP_C4 = GeodesicExact::CAP_C4;
|
||||
static constexpr unsigned CAP_ALL = GeodesicExact::CAP_ALL;
|
||||
static constexpr unsigned CAP_MASK = GeodesicExact::CAP_MASK;
|
||||
static constexpr unsigned OUT_ALL = GeodesicExact::OUT_ALL;
|
||||
static constexpr unsigned OUT_MASK = GeodesicExact::OUT_MASK;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Bit masks for what calculations to do. They signify to the
|
||||
* GeodesicLineExact::GeodesicLineExact constructor and to
|
||||
* GeodesicExact::Line what capabilities should be included in the
|
||||
* GeodesicLineExact object. This is merely a duplication of
|
||||
* GeodesicExact::mask.
|
||||
**********************************************************************/
|
||||
enum mask {
|
||||
/**
|
||||
* No capabilities, no output.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
NONE = GeodesicExact::NONE,
|
||||
/**
|
||||
* Calculate latitude \e lat2. (It's not necessary to include this as a
|
||||
* capability to GeodesicLineExact because this is included by default.)
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LATITUDE = GeodesicExact::LATITUDE,
|
||||
/**
|
||||
* Calculate longitude \e lon2.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LONGITUDE = GeodesicExact::LONGITUDE,
|
||||
/**
|
||||
* Calculate azimuths \e azi1 and \e azi2. (It's not necessary to
|
||||
* include this as a capability to GeodesicLineExact because this is
|
||||
* included by default.)
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
AZIMUTH = GeodesicExact::AZIMUTH,
|
||||
/**
|
||||
* Calculate distance \e s12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
DISTANCE = GeodesicExact::DISTANCE,
|
||||
/**
|
||||
* A combination of the common capabilities: GeodesicLineExact::LATITUDE,
|
||||
* GeodesicLineExact::LONGITUDE, GeodesicLineExact::AZIMUTH,
|
||||
* GeodesicLineExact::DISTANCE.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
STANDARD = GeodesicExact::STANDARD,
|
||||
/**
|
||||
* Allow distance \e s12 to be used as input in the direct geodesic
|
||||
* problem.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
DISTANCE_IN = GeodesicExact::DISTANCE_IN,
|
||||
/**
|
||||
* Calculate reduced length \e m12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
REDUCEDLENGTH = GeodesicExact::REDUCEDLENGTH,
|
||||
/**
|
||||
* Calculate geodesic scales \e M12 and \e M21.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
GEODESICSCALE = GeodesicExact::GEODESICSCALE,
|
||||
/**
|
||||
* Calculate area \e S12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
AREA = GeodesicExact::AREA,
|
||||
/**
|
||||
* Unroll \e lon2 in the direct calculation.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LONG_UNROLL = GeodesicExact::LONG_UNROLL,
|
||||
/**
|
||||
* All capabilities, calculate everything. (LONG_UNROLL is not
|
||||
* included in this mask.)
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
ALL = GeodesicExact::ALL,
|
||||
};
|
||||
|
||||
/**
|
||||
* Typedef for the base class implementing geodesics.
|
||||
**********************************************************************/
|
||||
typedef GeodesicExact BaseClass;
|
||||
|
||||
/** \name Constructors
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Constructor for a geodesic line staring at latitude \e lat1, longitude
|
||||
* \e lon1, and azimuth \e azi1 (all in degrees).
|
||||
*
|
||||
* @param[in] g A GeodesicExact object used to compute the necessary
|
||||
* information about the GeodesicLineExact.
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi1 azimuth at point 1 (degrees).
|
||||
* @param[in] caps bitor'ed combination of GeodesicLineExact::mask values
|
||||
* specifying the capabilities the GeodesicLineExact object should
|
||||
* possess, i.e., which quantities can be returned in calls to
|
||||
* GeodesicLine::Position.
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°].
|
||||
*
|
||||
* The GeodesicLineExact::mask values are
|
||||
* - \e caps |= GeodesicLineExact::LATITUDE for the latitude \e lat2; this
|
||||
* is added automatically;
|
||||
* - \e caps |= GeodesicLineExact::LONGITUDE for the latitude \e lon2;
|
||||
* - \e caps |= GeodesicLineExact::AZIMUTH for the latitude \e azi2; this
|
||||
* is added automatically;
|
||||
* - \e caps |= GeodesicLineExact::DISTANCE for the distance \e s12;
|
||||
* - \e caps |= GeodesicLineExact::REDUCEDLENGTH for the reduced length \e
|
||||
* m12;
|
||||
* - \e caps |= GeodesicLineExact::GEODESICSCALE for the geodesic scales \e
|
||||
* M12 and \e M21;
|
||||
* - \e caps |= GeodesicLineExact::AREA for the area \e S12;
|
||||
* - \e caps |= GeodesicLineExact::DISTANCE_IN permits the length of the
|
||||
* geodesic to be given in terms of \e s12; without this capability the
|
||||
* length can only be specified in terms of arc length;
|
||||
* - \e caps |= GeodesicLineExact::ALL for all of the above.
|
||||
* .
|
||||
* The default value of \e caps is GeodesicLineExact::ALL.
|
||||
*
|
||||
* If the point is at a pole, the azimuth is defined by keeping \e lon1
|
||||
* fixed, writing \e lat1 = ±(90° − ε), and taking
|
||||
* the limit ε → 0+.
|
||||
**********************************************************************/
|
||||
GeodesicLineExact(const GeodesicExact& g, real lat1, real lon1, real azi1,
|
||||
unsigned caps = ALL);
|
||||
|
||||
/**
|
||||
* A default constructor. If GeodesicLineExact::Position is called on the
|
||||
* resulting object, it returns immediately (without doing any
|
||||
* calculations). The object can be set with a call to
|
||||
* GeodesicExact::Line. Use Init() to test whether object is still in this
|
||||
* uninitialized state.
|
||||
**********************************************************************/
|
||||
GeodesicLineExact() : _caps(0U) {}
|
||||
///@}
|
||||
|
||||
/** \name Position in terms of distance
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Compute the position of point 2 which is a distance \e s12 (meters)
|
||||
* from point 1.
|
||||
*
|
||||
* @param[in] s12 distance from point 1 to point 2 (meters); it can be
|
||||
* signed.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees); requires that the
|
||||
* GeodesicLineExact object was constructed with \e caps |=
|
||||
* GeodesicLineExact::LONGITUDE.
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] m12 reduced length of geodesic (meters); requires that the
|
||||
* GeodesicLineExact object was constructed with \e caps |=
|
||||
* GeodesicLineExact::REDUCEDLENGTH.
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless); requires that the GeodesicLineExact object was
|
||||
* constructed with \e caps |= GeodesicLineExact::GEODESICSCALE.
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless); requires that the GeodesicLineExact object was
|
||||
* constructed with \e caps |= GeodesicLineExact::GEODESICSCALE.
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>); requires
|
||||
* that the GeodesicLineExact object was constructed with \e caps |=
|
||||
* GeodesicLineExact::AREA.
|
||||
* @return \e a12 arc length from point 1 to point 2 (degrees).
|
||||
*
|
||||
* The values of \e lon2 and \e azi2 returned are in the range
|
||||
* [−180°, 180°].
|
||||
*
|
||||
* The GeodesicLineExact object \e must have been constructed with \e caps
|
||||
* |= GeodesicLineExact::DISTANCE_IN; otherwise Math::NaN() is returned and
|
||||
* no parameters are set. Requesting a value which the GeodesicLineExact
|
||||
* object is not capable of computing is not an error; the corresponding
|
||||
* argument will not be altered.
|
||||
*
|
||||
* The following functions are overloaded versions of
|
||||
* GeodesicLineExact::Position which omit some of the output parameters.
|
||||
* Note, however, that the arc length is always computed and returned as
|
||||
* the function value.
|
||||
**********************************************************************/
|
||||
Math::real Position(real s12, real& lat2, real& lon2, real& azi2,
|
||||
real& m12, real& M12, real& M21, real& S12) const {
|
||||
real t;
|
||||
return GenPosition(false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH |
|
||||
REDUCEDLENGTH | GEODESICSCALE | AREA,
|
||||
lat2, lon2, azi2, t, m12, M12, M21, S12);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLineExact::Position.
|
||||
**********************************************************************/
|
||||
Math::real Position(real s12, real& lat2, real& lon2)
|
||||
const {
|
||||
real t;
|
||||
return GenPosition(false, s12,
|
||||
LATITUDE | LONGITUDE,
|
||||
lat2, lon2, t, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLineExact::Position.
|
||||
**********************************************************************/
|
||||
Math::real Position(real s12, real& lat2, real& lon2, real& azi2) const {
|
||||
real t;
|
||||
return GenPosition(false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH,
|
||||
lat2, lon2, azi2, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLineExact::Position.
|
||||
**********************************************************************/
|
||||
Math::real Position(real s12, real& lat2, real& lon2, real& azi2,
|
||||
real& m12) const {
|
||||
real t;
|
||||
return GenPosition(false, s12,
|
||||
LATITUDE | LONGITUDE |
|
||||
AZIMUTH | REDUCEDLENGTH,
|
||||
lat2, lon2, azi2, t, m12, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLineExact::Position.
|
||||
**********************************************************************/
|
||||
Math::real Position(real s12, real& lat2, real& lon2, real& azi2,
|
||||
real& M12, real& M21) const {
|
||||
real t;
|
||||
return GenPosition(false, s12,
|
||||
LATITUDE | LONGITUDE |
|
||||
AZIMUTH | GEODESICSCALE,
|
||||
lat2, lon2, azi2, t, t, M12, M21, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLineExact::Position.
|
||||
**********************************************************************/
|
||||
Math::real Position(real s12, real& lat2, real& lon2, real& azi2,
|
||||
real& m12, real& M12, real& M21) const {
|
||||
real t;
|
||||
return GenPosition(false, s12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH |
|
||||
REDUCEDLENGTH | GEODESICSCALE,
|
||||
lat2, lon2, azi2, t, m12, M12, M21, t);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name Position in terms of arc length
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Compute the position of point 2 which is an arc length \e a12 (degrees)
|
||||
* from point 1.
|
||||
*
|
||||
* @param[in] a12 arc length from point 1 to point 2 (degrees); it can
|
||||
* be signed.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees); requires that the
|
||||
* GeodesicLineExact object was constructed with \e caps |=
|
||||
* GeodesicLineExact::LONGITUDE.
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] s12 distance from point 1 to point 2 (meters); requires
|
||||
* that the GeodesicLineExact object was constructed with \e caps |=
|
||||
* GeodesicLineExact::DISTANCE.
|
||||
* @param[out] m12 reduced length of geodesic (meters); requires that the
|
||||
* GeodesicLineExact object was constructed with \e caps |=
|
||||
* GeodesicLineExact::REDUCEDLENGTH.
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless); requires that the GeodesicLineExact object was
|
||||
* constructed with \e caps |= GeodesicLineExact::GEODESICSCALE.
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless); requires that the GeodesicLineExact object was
|
||||
* constructed with \e caps |= GeodesicLineExact::GEODESICSCALE.
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>); requires
|
||||
* that the GeodesicLineExact object was constructed with \e caps |=
|
||||
* GeodesicLineExact::AREA.
|
||||
*
|
||||
* The values of \e lon2 and \e azi2 returned are in the range
|
||||
* [−180°, 180°].
|
||||
*
|
||||
* Requesting a value which the GeodesicLineExact object is not capable of
|
||||
* computing is not an error; the corresponding argument will not be
|
||||
* altered.
|
||||
*
|
||||
* The following functions are overloaded versions of
|
||||
* GeodesicLineExact::ArcPosition which omit some of the output parameters.
|
||||
**********************************************************************/
|
||||
void ArcPosition(real a12, real& lat2, real& lon2, real& azi2,
|
||||
real& s12, real& m12, real& M12, real& M21, real& S12)
|
||||
const {
|
||||
GenPosition(true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | DISTANCE |
|
||||
REDUCEDLENGTH | GEODESICSCALE | AREA,
|
||||
lat2, lon2, azi2, s12, m12, M12, M21, S12);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLineExact::ArcPosition.
|
||||
**********************************************************************/
|
||||
void ArcPosition(real a12, real& lat2, real& lon2) const {
|
||||
real t;
|
||||
GenPosition(true, a12,
|
||||
LATITUDE | LONGITUDE,
|
||||
lat2, lon2, t, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLineExact::ArcPosition.
|
||||
**********************************************************************/
|
||||
void ArcPosition(real a12, real& lat2, real& lon2, real& azi2) const {
|
||||
real t;
|
||||
GenPosition(true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH,
|
||||
lat2, lon2, azi2, t, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLineExact::ArcPosition.
|
||||
**********************************************************************/
|
||||
void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, real& s12)
|
||||
const {
|
||||
real t;
|
||||
GenPosition(true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH | DISTANCE,
|
||||
lat2, lon2, azi2, s12, t, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLineExact::ArcPosition.
|
||||
**********************************************************************/
|
||||
void ArcPosition(real a12, real& lat2, real& lon2, real& azi2,
|
||||
real& s12, real& m12) const {
|
||||
real t;
|
||||
GenPosition(true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH |
|
||||
DISTANCE | REDUCEDLENGTH,
|
||||
lat2, lon2, azi2, s12, m12, t, t, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLineExact::ArcPosition.
|
||||
**********************************************************************/
|
||||
void ArcPosition(real a12, real& lat2, real& lon2, real& azi2,
|
||||
real& s12, real& M12, real& M21) const {
|
||||
real t;
|
||||
GenPosition(true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH |
|
||||
DISTANCE | GEODESICSCALE,
|
||||
lat2, lon2, azi2, s12, t, M12, M21, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation for GeodesicLineExact::ArcPosition.
|
||||
**********************************************************************/
|
||||
void ArcPosition(real a12, real& lat2, real& lon2, real& azi2,
|
||||
real& s12, real& m12, real& M12, real& M21) const {
|
||||
real t;
|
||||
GenPosition(true, a12,
|
||||
LATITUDE | LONGITUDE | AZIMUTH |
|
||||
DISTANCE | REDUCEDLENGTH | GEODESICSCALE,
|
||||
lat2, lon2, azi2, s12, m12, M12, M21, t);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name The general position function.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* The general position function. GeodesicLineExact::Position and
|
||||
* GeodesicLineExact::ArcPosition are defined in terms of this function.
|
||||
*
|
||||
* @param[in] arcmode boolean flag determining the meaning of the second
|
||||
* parameter; if \e arcmode is false, then the GeodesicLineExact object
|
||||
* must have been constructed with \e caps |=
|
||||
* GeodesicLineExact::DISTANCE_IN.
|
||||
* @param[in] s12_a12 if \e arcmode is false, this is the distance between
|
||||
* point 1 and point 2 (meters); otherwise it is the arc length between
|
||||
* point 1 and point 2 (degrees); it can be signed.
|
||||
* @param[in] outmask a bitor'ed combination of GeodesicLineExact::mask
|
||||
* values specifying which of the following parameters should be set.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees); requires that the
|
||||
* GeodesicLineExact object was constructed with \e caps |=
|
||||
* GeodesicLineExact::LONGITUDE.
|
||||
* @param[out] azi2 (forward) azimuth at point 2 (degrees).
|
||||
* @param[out] s12 distance from point 1 to point 2 (meters); requires
|
||||
* that the GeodesicLineExact object was constructed with \e caps |=
|
||||
* GeodesicLineExact::DISTANCE.
|
||||
* @param[out] m12 reduced length of geodesic (meters); requires that the
|
||||
* GeodesicLineExact object was constructed with \e caps |=
|
||||
* GeodesicLineExact::REDUCEDLENGTH.
|
||||
* @param[out] M12 geodesic scale of point 2 relative to point 1
|
||||
* (dimensionless); requires that the GeodesicLineExact object was
|
||||
* constructed with \e caps |= GeodesicLineExact::GEODESICSCALE.
|
||||
* @param[out] M21 geodesic scale of point 1 relative to point 2
|
||||
* (dimensionless); requires that the GeodesicLineExact object was
|
||||
* constructed with \e caps |= GeodesicLineExact::GEODESICSCALE.
|
||||
* @param[out] S12 area under the geodesic (meters<sup>2</sup>); requires
|
||||
* that the GeodesicLineExact object was constructed with \e caps |=
|
||||
* GeodesicLineExact::AREA.
|
||||
* @return \e a12 arc length from point 1 to point 2 (degrees).
|
||||
*
|
||||
* The GeodesicLineExact::mask values possible for \e outmask are
|
||||
* - \e outmask |= GeodesicLineExact::LATITUDE for the latitude \e lat2;
|
||||
* - \e outmask |= GeodesicLineExact::LONGITUDE for the latitude \e lon2;
|
||||
* - \e outmask |= GeodesicLineExact::AZIMUTH for the latitude \e azi2;
|
||||
* - \e outmask |= GeodesicLineExact::DISTANCE for the distance \e s12;
|
||||
* - \e outmask |= GeodesicLineExact::REDUCEDLENGTH for the reduced length
|
||||
* \e m12;
|
||||
* - \e outmask |= GeodesicLineExact::GEODESICSCALE for the geodesic scales
|
||||
* \e M12 and \e M21;
|
||||
* - \e outmask |= GeodesicLineExact::AREA for the area \e S12;
|
||||
* - \e outmask |= GeodesicLineExact::ALL for all of the above;
|
||||
* - \e outmask |= GeodesicLineExact::LONG_UNROLL to unroll \e lon2 instead
|
||||
* of wrapping it into the range [−180°, 180°].
|
||||
* .
|
||||
* Requesting a value which the GeodesicLineExact object is not capable of
|
||||
* computing is not an error; the corresponding argument will not be
|
||||
* altered. Note, however, that the arc length is always computed and
|
||||
* returned as the function value.
|
||||
*
|
||||
* With the GeodesicLineExact::LONG_UNROLL bit set, the quantity \e lon2
|
||||
* − \e lon1 indicates how many times and in what sense the geodesic
|
||||
* encircles the ellipsoid.
|
||||
**********************************************************************/
|
||||
Math::real GenPosition(bool arcmode, real s12_a12, unsigned outmask,
|
||||
real& lat2, real& lon2, real& azi2,
|
||||
real& s12, real& m12, real& M12, real& M21,
|
||||
real& S12) const;
|
||||
///@}
|
||||
|
||||
/** \name Setting point 3
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Specify position of point 3 in terms of distance.
|
||||
*
|
||||
* @param[in] s13 the distance from point 1 to point 3 (meters); it
|
||||
* can be negative.
|
||||
*
|
||||
* This is only useful if the GeodesicLineExact object has been constructed
|
||||
* with \e caps |= GeodesicLineExact::DISTANCE_IN.
|
||||
**********************************************************************/
|
||||
void SetDistance(real s13);
|
||||
|
||||
/**
|
||||
* Specify position of point 3 in terms of arc length.
|
||||
*
|
||||
* @param[in] a13 the arc length from point 1 to point 3 (degrees); it
|
||||
* can be negative.
|
||||
*
|
||||
* The distance \e s13 is only set if the GeodesicLineExact object has been
|
||||
* constructed with \e caps |= GeodesicLineExact::DISTANCE.
|
||||
**********************************************************************/
|
||||
void SetArc(real a13);
|
||||
|
||||
/**
|
||||
* Specify position of point 3 in terms of either distance or arc length.
|
||||
*
|
||||
* @param[in] arcmode boolean flag determining the meaning of the second
|
||||
* parameter; if \e arcmode is false, then the GeodesicLineExact object
|
||||
* must have been constructed with \e caps |=
|
||||
* GeodesicLineExact::DISTANCE_IN.
|
||||
* @param[in] s13_a13 if \e arcmode is false, this is the distance from
|
||||
* point 1 to point 3 (meters); otherwise it is the arc length from
|
||||
* point 1 to point 3 (degrees); it can be negative.
|
||||
**********************************************************************/
|
||||
void GenSetDistance(bool arcmode, real s13_a13);
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @return true if the object has been initialized.
|
||||
**********************************************************************/
|
||||
bool Init() const { return _caps != 0U; }
|
||||
|
||||
/**
|
||||
* @return \e lat1 the latitude of point 1 (degrees).
|
||||
**********************************************************************/
|
||||
Math::real Latitude() const
|
||||
{ return Init() ? _lat1 : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return \e lon1 the longitude of point 1 (degrees).
|
||||
**********************************************************************/
|
||||
Math::real Longitude() const
|
||||
{ return Init() ? _lon1 : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return \e azi1 the azimuth (degrees) of the geodesic line at point 1.
|
||||
**********************************************************************/
|
||||
Math::real Azimuth() const
|
||||
{ return Init() ? _azi1 : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* The sine and cosine of \e azi1.
|
||||
*
|
||||
* @param[out] sazi1 the sine of \e azi1.
|
||||
* @param[out] cazi1 the cosine of \e azi1.
|
||||
**********************************************************************/
|
||||
void Azimuth(real& sazi1, real& cazi1) const
|
||||
{ if (Init()) { sazi1 = _salp1; cazi1 = _calp1; } }
|
||||
|
||||
/**
|
||||
* @return \e azi0 the azimuth (degrees) of the geodesic line as it crosses
|
||||
* the equator in a northward direction.
|
||||
*
|
||||
* The result lies in [−90°, 90°].
|
||||
**********************************************************************/
|
||||
Math::real EquatorialAzimuth() const
|
||||
{ return Init() ? Math::atan2d(_salp0, _calp0) : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* The sine and cosine of \e azi0.
|
||||
*
|
||||
* @param[out] sazi0 the sine of \e azi0.
|
||||
* @param[out] cazi0 the cosine of \e azi0.
|
||||
**********************************************************************/
|
||||
void EquatorialAzimuth(real& sazi0, real& cazi0) const
|
||||
{ if (Init()) { sazi0 = _salp0; cazi0 = _calp0; } }
|
||||
|
||||
/**
|
||||
* @return \e a1 the arc length (degrees) between the northward equatorial
|
||||
* crossing and point 1.
|
||||
*
|
||||
* The result lies in [−180°, 180°].
|
||||
**********************************************************************/
|
||||
Math::real EquatorialArc() const {
|
||||
using std::atan2;
|
||||
return Init() ? atan2(_ssig1, _csig1) / Math::degree() : Math::NaN();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value inherited from the GeodesicExact object used in the
|
||||
* constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const
|
||||
{ return Init() ? _a : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the value
|
||||
* inherited from the GeodesicExact object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const
|
||||
{ return Init() ? _f : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return \e caps the computational capabilities that this object was
|
||||
* constructed with. LATITUDE and AZIMUTH are always included.
|
||||
**********************************************************************/
|
||||
unsigned Capabilities() const { return _caps; }
|
||||
|
||||
/**
|
||||
* Test what capabilities are available.
|
||||
*
|
||||
* @param[in] testcaps a set of bitor'ed GeodesicLineExact::mask values.
|
||||
* @return true if the GeodesicLineExact object has all these capabilities.
|
||||
**********************************************************************/
|
||||
bool Capabilities(unsigned testcaps) const {
|
||||
testcaps &= OUT_ALL;
|
||||
return (_caps & testcaps) == testcaps;
|
||||
}
|
||||
|
||||
/**
|
||||
* The distance or arc length to point 3.
|
||||
*
|
||||
* @param[in] arcmode boolean flag determining the meaning of returned
|
||||
* value.
|
||||
* @return \e s13 if \e arcmode is false; \e a13 if \e arcmode is true.
|
||||
**********************************************************************/
|
||||
Math::real GenDistance(bool arcmode) const
|
||||
{ return Init() ? (arcmode ? _a13 : _s13) : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return \e s13, the distance to point 3 (meters).
|
||||
**********************************************************************/
|
||||
Math::real Distance() const
|
||||
{ return GenDistance(false); }
|
||||
|
||||
/**
|
||||
* @return \e a13, the arc length to point 3 (degrees).
|
||||
**********************************************************************/
|
||||
Math::real Arc() const { return GenDistance(true); }
|
||||
///@}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#endif // GEOGRAPHICLIB_GEODESICLINEEXACT_HPP
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* \file Geohash.hpp
|
||||
* \brief Header for GeographicLib::Geohash class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2012-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GEOHASH_HPP)
|
||||
#define GEOGRAPHICLIB_GEOHASH_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Conversions for geohashes
|
||||
*
|
||||
* Geohashes are described in
|
||||
* - https://en.wikipedia.org/wiki/Geohash
|
||||
* - http://geohash.org/
|
||||
* .
|
||||
* They provide a compact string representation of a particular geographic
|
||||
* location (expressed as latitude and longitude), with the property that if
|
||||
* trailing characters are dropped from the string the geographic location
|
||||
* remains nearby. The classes Georef and GARS implement similar compact
|
||||
* representations.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Geohash.cpp
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT Geohash {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
static const int maxlen_ = 18;
|
||||
static const unsigned long long mask_ = 1ULL << 45;
|
||||
static const char* const lcdigits_;
|
||||
static const char* const ucdigits_;
|
||||
Geohash() = delete; // Disable constructor
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Convert from geographic coordinates to a geohash.
|
||||
*
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[in] len the length of the resulting geohash.
|
||||
* @param[out] geohash the geohash.
|
||||
* @exception GeographicErr if \e lat is not in [−90°,
|
||||
* 90°].
|
||||
* @exception std::bad_alloc if memory for \e geohash can't be allocated.
|
||||
*
|
||||
* Internally, \e len is first put in the range [0, 18]. (\e len = 18
|
||||
* provides approximately 1μm precision.)
|
||||
*
|
||||
* If \e lat or \e lon is NaN, the returned geohash is "invalid".
|
||||
**********************************************************************/
|
||||
static void Forward(real lat, real lon, int len, std::string& geohash);
|
||||
|
||||
/**
|
||||
* Convert from a geohash to geographic coordinates.
|
||||
*
|
||||
* @param[in] geohash the geohash.
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] len the length of the geohash.
|
||||
* @param[in] centerp if true (the default) return the center of the
|
||||
* geohash location, otherwise return the south-west corner.
|
||||
* @exception GeographicErr if \e geohash contains illegal characters.
|
||||
*
|
||||
* Only the first 18 characters for \e geohash are considered. (18
|
||||
* characters provides approximately 1μm precision.) The case of the
|
||||
* letters in \e geohash is ignored.
|
||||
*
|
||||
* If the first 3 characters of \e geohash are "inv", then \e lat and \e
|
||||
* lon are set to NaN and \e len is unchanged. ("nan" is treated
|
||||
* similarly.)
|
||||
**********************************************************************/
|
||||
static void Reverse(const std::string& geohash, real& lat, real& lon,
|
||||
int& len, bool centerp = true);
|
||||
|
||||
/**
|
||||
* The latitude resolution of a geohash.
|
||||
*
|
||||
* @param[in] len the length of the geohash.
|
||||
* @return the latitude resolution (degrees).
|
||||
*
|
||||
* Internally, \e len is first put in the range [0, 18].
|
||||
**********************************************************************/
|
||||
static Math::real LatitudeResolution(int len) {
|
||||
using std::ldexp;
|
||||
len = (std::max)(0, (std::min)(int(maxlen_), len));
|
||||
return ldexp(real(Math::hd), -(5 * len / 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* The longitude resolution of a geohash.
|
||||
*
|
||||
* @param[in] len the length of the geohash.
|
||||
* @return the longitude resolution (degrees).
|
||||
*
|
||||
* Internally, \e len is first put in the range [0, 18].
|
||||
**********************************************************************/
|
||||
static Math::real LongitudeResolution(int len) {
|
||||
using std::ldexp;
|
||||
len = (std::max)(0, (std::min)(int(maxlen_), len));
|
||||
return ldexp(real(Math::td), -(5 * len - 5 * len / 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* The geohash length required to meet a given geographic resolution.
|
||||
*
|
||||
* @param[in] res the minimum of resolution in latitude and longitude
|
||||
* (degrees).
|
||||
* @return geohash length.
|
||||
*
|
||||
* The returned length is in the range [0, 18].
|
||||
**********************************************************************/
|
||||
static int GeohashLength(real res) {
|
||||
using std::fabs; res = fabs(res);
|
||||
for (int len = 0; len < maxlen_; ++len)
|
||||
if (LongitudeResolution(len) <= res)
|
||||
return len;
|
||||
return maxlen_;
|
||||
}
|
||||
|
||||
/**
|
||||
* The geohash length required to meet a given geographic resolution.
|
||||
*
|
||||
* @param[in] latres the resolution in latitude (degrees).
|
||||
* @param[in] lonres the resolution in longitude (degrees).
|
||||
* @return geohash length.
|
||||
*
|
||||
* The returned length is in the range [0, 18].
|
||||
**********************************************************************/
|
||||
static int GeohashLength(real latres, real lonres) {
|
||||
using std::fabs;
|
||||
latres = fabs(latres);
|
||||
lonres = fabs(lonres);
|
||||
for (int len = 0; len < maxlen_; ++len)
|
||||
if (LatitudeResolution(len) <= latres &&
|
||||
LongitudeResolution(len) <= lonres)
|
||||
return len;
|
||||
return maxlen_;
|
||||
}
|
||||
|
||||
/**
|
||||
* The decimal geographic precision required to match a given geohash
|
||||
* length. This is the number of digits needed after decimal point in a
|
||||
* decimal degrees representation.
|
||||
*
|
||||
* @param[in] len the length of the geohash.
|
||||
* @return the decimal precision (may be negative).
|
||||
*
|
||||
* Internally, \e len is first put in the range [0, 18]. The returned
|
||||
* decimal precision is in the range [−2, 12].
|
||||
**********************************************************************/
|
||||
static int DecimalPrecision(int len) {
|
||||
using std::floor, std::log;
|
||||
return -int(floor(log(LatitudeResolution(len))/log(Math::real(10))));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_GEOHASH_HPP
|
||||
@@ -0,0 +1,474 @@
|
||||
/**
|
||||
* \file Geoid.hpp
|
||||
* \brief Header for GeographicLib::Geoid class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2009-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GEOID_HPP)
|
||||
#define GEOGRAPHICLIB_GEOID_HPP 1
|
||||
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Squelch warnings about dll vs vector and constant conditional expressions
|
||||
# pragma warning (push)
|
||||
# pragma warning (disable: 4251 4127)
|
||||
#endif
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GEOID_PGM_PIXEL_WIDTH)
|
||||
/**
|
||||
* The size of the pixel data in the pgm data files for the geoids. 2 is the
|
||||
* standard size corresponding to a maxval 2<sup>16</sup>−1. Setting it
|
||||
* to 4 uses a maxval of 2<sup>32</sup>−1 and changes the extension for
|
||||
* the data files from .pgm to .pgm4. Note that the format of these pgm4 files
|
||||
* is a non-standard extension of the pgm format.
|
||||
**********************************************************************/
|
||||
# define GEOGRAPHICLIB_GEOID_PGM_PIXEL_WIDTH 2
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Looking up the height of the geoid above the ellipsoid
|
||||
*
|
||||
* This class evaluates the height of one of the standard geoids, EGM84,
|
||||
* EGM96, or EGM2008 by bilinear or cubic interpolation into a rectangular
|
||||
* grid of data. These geoid models are documented in
|
||||
* - EGM84:
|
||||
* https://earth-info.nga.mil/index.php?dir=wgs84&action=wgs84#tab_egm84
|
||||
* - EGM96:
|
||||
* https://earth-info.nga.mil/index.php?dir=wgs84&action=wgs84#tab_egm96
|
||||
* - EGM2008:
|
||||
* https://earth-info.nga.mil/index.php?dir=wgs84&action=wgs84#tab_egm2008
|
||||
*
|
||||
* The geoids are defined in terms of spherical harmonics. However in order
|
||||
* to provide a quick and flexible method of evaluating the geoid heights,
|
||||
* this class evaluates the height by interpolation into a grid of
|
||||
* precomputed values.
|
||||
*
|
||||
* The height of the geoid above the ellipsoid, \e N, is sometimes called the
|
||||
* geoid undulation. It can be used to convert a height above the ellipsoid,
|
||||
* \e h, to the corresponding height above the geoid (the orthometric height,
|
||||
* roughly the height above mean sea level), \e H, using the relations
|
||||
*
|
||||
* \e h = \e N + \e H;
|
||||
* \e H = −\e N + \e h.
|
||||
*
|
||||
* See \ref geoid for details of how to install the data sets, the data
|
||||
* format, estimates of the interpolation errors, and how to use caching.
|
||||
*
|
||||
* This class is typically \e not thread safe in that a single instantiation
|
||||
* cannot be safely used by multiple threads because of the way the object
|
||||
* reads the data set and because it maintains a single-cell cache. If
|
||||
* multiple threads need to calculate geoid heights they should all construct
|
||||
* thread-local instantiations. Alternatively, set the optional \e
|
||||
* threadsafe parameter to true in the constructor. This causes the
|
||||
* constructor to read all the data into memory and to turn off the
|
||||
* single-cell caching which results in a Geoid object which \e is thread
|
||||
* safe.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Geoid.cpp
|
||||
*
|
||||
* <a href="GeoidEval.1.html">GeoidEval</a> is a command-line utility
|
||||
* providing access to the functionality of Geoid.
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT Geoid {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
#if GEOGRAPHICLIB_GEOID_PGM_PIXEL_WIDTH != 4
|
||||
typedef unsigned short pixel_t;
|
||||
static const unsigned pixel_size_ = 2;
|
||||
static const unsigned pixel_max_ = 0xffffu;
|
||||
#else
|
||||
typedef unsigned pixel_t;
|
||||
static const unsigned pixel_size_ = 4;
|
||||
static const unsigned pixel_max_ = 0xffffffffu;
|
||||
#endif
|
||||
static const unsigned stencilsize_ = 12;
|
||||
static const unsigned nterms_ = ((3 + 1) * (3 + 2))/2; // for a cubic fit
|
||||
static const int c0_;
|
||||
static const int c0n_;
|
||||
static const int c0s_;
|
||||
static const int c3_[stencilsize_ * nterms_];
|
||||
static const int c3n_[stencilsize_ * nterms_];
|
||||
static const int c3s_[stencilsize_ * nterms_];
|
||||
|
||||
std::string _name, _dir, _filename;
|
||||
const bool _cubic;
|
||||
const real _a, _e2, _degree, _eps;
|
||||
mutable std::ifstream _file;
|
||||
real _rlonres, _rlatres;
|
||||
std::string _description, _datetime;
|
||||
real _offset, _scale, _maxerror, _rmserror;
|
||||
int _width, _height;
|
||||
unsigned long long _datastart, _swidth;
|
||||
bool _threadsafe;
|
||||
// Area cache
|
||||
mutable std::vector< std::vector<pixel_t> > _data;
|
||||
mutable bool _cache;
|
||||
// NE corner and extent of cache
|
||||
mutable int _xoffset, _yoffset, _xsize, _ysize;
|
||||
// Cell cache
|
||||
mutable int _ix, _iy;
|
||||
mutable real _v00, _v01, _v10, _v11;
|
||||
mutable real _t[nterms_];
|
||||
void filepos(int ix, int iy) const {
|
||||
_file.seekg(std::streamoff
|
||||
(_datastart +
|
||||
pixel_size_ * (unsigned(iy)*_swidth + unsigned(ix))));
|
||||
}
|
||||
real rawval(int ix, int iy) const {
|
||||
if (ix < 0)
|
||||
ix += _width;
|
||||
else if (ix >= _width)
|
||||
ix -= _width;
|
||||
if (_cache && iy >= _yoffset && iy < _yoffset + _ysize &&
|
||||
((ix >= _xoffset && ix < _xoffset + _xsize) ||
|
||||
(ix + _width >= _xoffset && ix + _width < _xoffset + _xsize))) {
|
||||
return real(_data[iy - _yoffset]
|
||||
[ix >= _xoffset ? ix - _xoffset : ix + _width - _xoffset]);
|
||||
} else {
|
||||
if (iy < 0 || iy >= _height) {
|
||||
iy = iy < 0 ? -iy : 2 * (_height - 1) - iy;
|
||||
ix += (ix < _width/2 ? 1 : -1) * _width/2;
|
||||
}
|
||||
try {
|
||||
filepos(ix, iy);
|
||||
// initial values to suppress warnings in case get fails
|
||||
char a = 0, b = 0;
|
||||
_file.get(a);
|
||||
_file.get(b);
|
||||
unsigned r = ((unsigned char)(a) << 8) | (unsigned char)(b);
|
||||
if constexpr (pixel_size_ == 4) {
|
||||
_file.get(a);
|
||||
_file.get(b);
|
||||
r = (r << 16) | ((unsigned char)(a) << 8) | (unsigned char)(b);
|
||||
}
|
||||
return real(r);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
// throw GeographicErr("Error reading " + _filename + ": "
|
||||
// + e.what());
|
||||
// triggers complaints about the "binary '+'" under Visual Studio.
|
||||
// So use '+=' instead.
|
||||
std::string err("Error reading ");
|
||||
err += _filename;
|
||||
err += ": ";
|
||||
err += e.what();
|
||||
throw GeographicErr(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
real height(real lat, real lon) const;
|
||||
Geoid(const Geoid&) = delete; // copy constructor not allowed
|
||||
Geoid& operator=(const Geoid&) = delete; // copy assignment not allowed
|
||||
public:
|
||||
|
||||
/**
|
||||
* Flags indicating conversions between heights above the geoid and heights
|
||||
* above the ellipsoid.
|
||||
**********************************************************************/
|
||||
enum convertflag {
|
||||
/**
|
||||
* The multiplier for converting from heights above the ellipsoid to
|
||||
* heights above the geoid.
|
||||
**********************************************************************/
|
||||
ELLIPSOIDTOGEOID = -1,
|
||||
/**
|
||||
* No conversion.
|
||||
**********************************************************************/
|
||||
NONE = 0,
|
||||
/**
|
||||
* The multiplier for converting from heights above the geoid to heights
|
||||
* above the ellipsoid.
|
||||
**********************************************************************/
|
||||
GEOIDTOELLIPSOID = 1,
|
||||
};
|
||||
|
||||
/** \name Setting up the geoid
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Construct a geoid.
|
||||
*
|
||||
* @param[in] name the name of the geoid.
|
||||
* @param[in] path (optional) directory for data file.
|
||||
* @param[in] cubic (optional) interpolation method; false means bilinear,
|
||||
* true (the default) means cubic.
|
||||
* @param[in] threadsafe (optional), if true, construct a thread safe
|
||||
* object. The default is false
|
||||
* @exception GeographicErr if the data file cannot be found, is
|
||||
* unreadable, or is corrupt.
|
||||
* @exception GeographicErr if \e threadsafe is true but the memory
|
||||
* necessary for caching the data can't be allocated.
|
||||
*
|
||||
* The data file is formed by appending ".pgm" to the name. If \e path is
|
||||
* specified (and is non-empty), then the file is loaded from directory, \e
|
||||
* path. Otherwise the path is given by DefaultGeoidPath(). If the \e
|
||||
* threadsafe parameter is true, the data set is read into memory, the data
|
||||
* file is closed, and single-cell caching is turned off; this results in a
|
||||
* Geoid object which \e is thread safe.
|
||||
**********************************************************************/
|
||||
explicit Geoid(const std::string& name, const std::string& path = "",
|
||||
bool cubic = true, bool threadsafe = false);
|
||||
|
||||
/**
|
||||
* Set up a cache.
|
||||
*
|
||||
* @param[in] south latitude (degrees) of the south edge of the cached
|
||||
* area.
|
||||
* @param[in] west longitude (degrees) of the west edge of the cached area.
|
||||
* @param[in] north latitude (degrees) of the north edge of the cached
|
||||
* area.
|
||||
* @param[in] east longitude (degrees) of the east edge of the cached area.
|
||||
* @exception GeographicErr if the memory necessary for caching the data
|
||||
* can't be allocated (in this case, you will have no cache and can try
|
||||
* again with a smaller area).
|
||||
* @exception GeographicErr if there's a problem reading the data.
|
||||
* @exception GeographicErr if this is called on a threadsafe Geoid.
|
||||
*
|
||||
* Cache the data for the specified "rectangular" area bounded by the
|
||||
* parallels \e south and \e north and the meridians \e west and \e east.
|
||||
* \e east is always interpreted as being east of \e west, if necessary by
|
||||
* adding 360° to its value. \e south and \e north should be in
|
||||
* the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
void CacheArea(real south, real west, real north, real east) const;
|
||||
|
||||
/**
|
||||
* Cache all the data.
|
||||
*
|
||||
* @exception GeographicErr if the memory necessary for caching the data
|
||||
* can't be allocated (in this case, you will have no cache and can try
|
||||
* again with a smaller area).
|
||||
* @exception GeographicErr if there's a problem reading the data.
|
||||
* @exception GeographicErr if this is called on a threadsafe Geoid.
|
||||
*
|
||||
* On most computers, this is fast for data sets with grid resolution of 5'
|
||||
* or coarser. For a 1' grid, the required RAM is 450MB; a 2.5' grid needs
|
||||
* 72MB; and a 5' grid needs 18MB.
|
||||
**********************************************************************/
|
||||
void CacheAll() const { CacheArea(real(-Math::qd), real(0),
|
||||
real( Math::qd), real(Math::td)); }
|
||||
|
||||
/**
|
||||
* Clear the cache. This never throws an error. (This does nothing with a
|
||||
* thread safe Geoid.)
|
||||
**********************************************************************/
|
||||
void CacheClear() const;
|
||||
|
||||
///@}
|
||||
|
||||
/** \name Compute geoid heights
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Compute the geoid height at a point
|
||||
*
|
||||
* @param[in] lat latitude of the point (degrees).
|
||||
* @param[in] lon longitude of the point (degrees).
|
||||
* @exception GeographicErr if there's a problem reading the data; this
|
||||
* never happens if (\e lat, \e lon) is within a successfully cached
|
||||
* area.
|
||||
* @return the height of the geoid above the ellipsoid (meters).
|
||||
*
|
||||
* The latitude should be in [−90°, 90°].
|
||||
**********************************************************************/
|
||||
Math::real operator()(real lat, real lon) const {
|
||||
return height(lat, lon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a height above the geoid to a height above the ellipsoid and
|
||||
* vice versa.
|
||||
*
|
||||
* @param[in] lat latitude of the point (degrees).
|
||||
* @param[in] lon longitude of the point (degrees).
|
||||
* @param[in] h height of the point (degrees).
|
||||
* @param[in] d a Geoid::convertflag specifying the direction of the
|
||||
* conversion; Geoid::GEOIDTOELLIPSOID means convert a height above the
|
||||
* geoid to a height above the ellipsoid; Geoid::ELLIPSOIDTOGEOID means
|
||||
* convert a height above the ellipsoid to a height above the geoid.
|
||||
* @exception GeographicErr if there's a problem reading the data; this
|
||||
* never happens if (\e lat, \e lon) is within a successfully cached
|
||||
* area.
|
||||
* @return converted height (meters).
|
||||
**********************************************************************/
|
||||
Math::real ConvertHeight(real lat, real lon, real h,
|
||||
convertflag d) const {
|
||||
return h + real(d) * height(lat, lon);
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return geoid description, if available, in the data file; if
|
||||
* absent, return "NONE".
|
||||
**********************************************************************/
|
||||
const std::string& Description() const { return _description; }
|
||||
|
||||
/**
|
||||
* @return date of the data file; if absent, return "UNKNOWN".
|
||||
**********************************************************************/
|
||||
const std::string& DateTime() const { return _datetime; }
|
||||
|
||||
/**
|
||||
* @return full file name used to load the geoid data.
|
||||
**********************************************************************/
|
||||
const std::string& GeoidFile() const { return _filename; }
|
||||
|
||||
/**
|
||||
* @return "name" used to load the geoid data (from the first argument of
|
||||
* the constructor).
|
||||
**********************************************************************/
|
||||
const std::string& GeoidName() const { return _name; }
|
||||
|
||||
/**
|
||||
* @return directory used to load the geoid data.
|
||||
**********************************************************************/
|
||||
const std::string& GeoidDirectory() const { return _dir; }
|
||||
|
||||
/**
|
||||
* @return interpolation method ("cubic" or "bilinear").
|
||||
**********************************************************************/
|
||||
const std::string Interpolation() const
|
||||
{ return std::string(_cubic ? "cubic" : "bilinear"); }
|
||||
|
||||
/**
|
||||
* @return estimate of the maximum interpolation and quantization error
|
||||
* (meters).
|
||||
*
|
||||
* This relies on the value being stored in the data file. If the value is
|
||||
* absent, return −1.
|
||||
**********************************************************************/
|
||||
Math::real MaxError() const { return _maxerror; }
|
||||
|
||||
/**
|
||||
* @return estimate of the RMS interpolation and quantization error
|
||||
* (meters).
|
||||
*
|
||||
* This relies on the value being stored in the data file. If the value is
|
||||
* absent, return −1.
|
||||
**********************************************************************/
|
||||
Math::real RMSError() const { return _rmserror; }
|
||||
|
||||
/**
|
||||
* @return offset (meters).
|
||||
*
|
||||
* This in used in converting from the pixel values in the data file to
|
||||
* geoid heights.
|
||||
**********************************************************************/
|
||||
Math::real Offset() const { return _offset; }
|
||||
|
||||
/**
|
||||
* @return scale (meters).
|
||||
*
|
||||
* This in used in converting from the pixel values in the data file to
|
||||
* geoid heights.
|
||||
**********************************************************************/
|
||||
Math::real Scale() const { return _scale; }
|
||||
|
||||
/**
|
||||
* @return true if the object is constructed to be thread safe.
|
||||
**********************************************************************/
|
||||
bool ThreadSafe() const { return _threadsafe; }
|
||||
|
||||
/**
|
||||
* @return true if a data cache is active.
|
||||
**********************************************************************/
|
||||
bool Cache() const { return _cache; }
|
||||
|
||||
/**
|
||||
* @return west edge of the cached area; the cache includes this edge.
|
||||
**********************************************************************/
|
||||
Math::real CacheWest() const {
|
||||
return _cache ? ((_xoffset + (_xsize == _width ? 0 : _cubic)
|
||||
+ _width/2) % _width - _width/2) / _rlonres :
|
||||
0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return east edge of the cached area; the cache excludes this edge.
|
||||
**********************************************************************/
|
||||
Math::real CacheEast() const {
|
||||
return _cache ?
|
||||
CacheWest() +
|
||||
(_xsize - (_xsize == _width ? 0 : 1 + 2 * _cubic)) / _rlonres :
|
||||
0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return north edge of the cached area; the cache includes this edge.
|
||||
**********************************************************************/
|
||||
Math::real CacheNorth() const {
|
||||
return _cache ? real(Math::qd) - (_yoffset + _cubic) / _rlatres : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return south edge of the cached area; the cache excludes this edge
|
||||
* unless it's the south pole.
|
||||
**********************************************************************/
|
||||
Math::real CacheSouth() const {
|
||||
return _cache ?
|
||||
real(Math::qd) - ( _yoffset + _ysize - 1 - _cubic) / _rlatres :
|
||||
0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \e a the equatorial radius of the WGS84 ellipsoid (meters).
|
||||
*
|
||||
* (The WGS84 value is returned because the supported geoid models are all
|
||||
* based on this ellipsoid.)
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const
|
||||
{ return Constants::WGS84_a(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the WGS84 ellipsoid.
|
||||
*
|
||||
* (The WGS84 value is returned because the supported geoid models are all
|
||||
* based on this ellipsoid.)
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return Constants::WGS84_f(); }
|
||||
///@}
|
||||
|
||||
/**
|
||||
* @return the default path for geoid data files.
|
||||
*
|
||||
* This is the value of the environment variable GEOGRAPHICLIB_GEOID_PATH,
|
||||
* if set; otherwise, it is $GEOGRAPHICLIB_DATA/geoids if the environment
|
||||
* variable GEOGRAPHICLIB_DATA is set; otherwise, it is a compile-time
|
||||
* default (/usr/local/share/GeographicLib/geoids on non-Windows systems
|
||||
* and C:/ProgramData/GeographicLib/geoids on Windows systems).
|
||||
**********************************************************************/
|
||||
static std::string DefaultGeoidPath();
|
||||
|
||||
/**
|
||||
* @return the default name for the geoid.
|
||||
*
|
||||
* This is the value of the environment variable GEOGRAPHICLIB_GEOID_NAME,
|
||||
* if set; otherwise, it is "egm96-5". The Geoid class does not use this
|
||||
* function; it is just provided as a convenience for a calling program
|
||||
* when constructing a Geoid object.
|
||||
**********************************************************************/
|
||||
static std::string DefaultGeoidName();
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#endif // GEOGRAPHICLIB_GEOID_HPP
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* \file Georef.hpp
|
||||
* \brief Header for GeographicLib::Georef class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2015-2024) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GEOREF_HPP)
|
||||
#define GEOGRAPHICLIB_GEOREF_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Conversions for the World Geographic Reference System (georef)
|
||||
*
|
||||
* The World Geographic Reference System is described in
|
||||
* - https://en.wikipedia.org/wiki/Georef
|
||||
* - https://web.archive.org/web/20161214054445/http://earth-info.nga.mil/GandG/coordsys/grids/georef.pdf
|
||||
* .
|
||||
* It provides a compact string representation of a geographic area
|
||||
* (expressed as latitude and longitude). The classes GARS and Geohash
|
||||
* implement similar compact representations.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Georef.cpp
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT Georef {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
static const char* const digits_;
|
||||
static const char* const lontile_;
|
||||
static const char* const lattile_;
|
||||
static const char* const degrees_;
|
||||
static constexpr int tile_ = 15; // The size of tile in degrees
|
||||
static constexpr int lonorig_ = -Math::hd; // Origin for longitude
|
||||
static constexpr int latorig_ = -Math::qd; // Origin for latitude
|
||||
static constexpr int base_ = 10; // Base for minutes
|
||||
static constexpr int baselen_ = 4;
|
||||
static constexpr int maxprec_ = 11; // approximately equivalent to MGRS class
|
||||
static constexpr int maxlen_ = baselen_ + 2 * maxprec_;
|
||||
Georef() = delete; // Disable constructor
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Convert from geographic coordinates to georef.
|
||||
*
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[in] prec the precision of the resulting georef.
|
||||
* @param[out] georef the georef string.
|
||||
* @exception GeographicErr if \e lat is not in [−90°,
|
||||
* 90°].
|
||||
* @exception std::bad_alloc if memory for \e georef can't be allocated.
|
||||
*
|
||||
* \e prec specifies the precision of \e georef as follows:
|
||||
* - \e prec = −1 (min), 15°
|
||||
* - \e prec = 0, 1°
|
||||
* - \e prec = 1, converted to \e prec = 2
|
||||
* - \e prec = 2, 1'
|
||||
* - \e prec = 3, 0.1'
|
||||
* - \e prec = 4, 0.01'
|
||||
* - \e prec = 5, 0.001'
|
||||
* - …
|
||||
* - \e prec = 11 (max), 10<sup>−9</sup>'
|
||||
*
|
||||
* If \e lat or \e lon is NaN, then \e georef is set to "INVALID".
|
||||
**********************************************************************/
|
||||
static void Forward(real lat, real lon, int prec, std::string& georef);
|
||||
|
||||
/**
|
||||
* Convert from Georef to geographic coordinates.
|
||||
*
|
||||
* @param[in] georef the Georef.
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] prec the precision of \e georef.
|
||||
* @param[in] centerp if true (the default) return the center
|
||||
* \e georef, otherwise return the south-west corner.
|
||||
* @exception GeographicErr if \e georef is illegal.
|
||||
*
|
||||
* The case of the letters in \e georef is ignored. \e prec is in the
|
||||
* range [−1, 11] and gives the precision of \e georef as follows:
|
||||
* - \e prec = −1 (min), 15°
|
||||
* - \e prec = 0, 1°
|
||||
* - \e prec = 1, not returned
|
||||
* - \e prec = 2, 1'
|
||||
* - \e prec = 3, 0.1'
|
||||
* - \e prec = 4, 0.01'
|
||||
* - \e prec = 5, 0.001'
|
||||
* - …
|
||||
* - \e prec = 11 (max), 10<sup>−9</sup>'
|
||||
*
|
||||
* If the first 3 characters of \e georef are "INV", then \e lat and \e lon
|
||||
* are set to NaN and \e prec is unchanged.
|
||||
**********************************************************************/
|
||||
static void Reverse(const std::string& georef, real& lat, real& lon,
|
||||
int& prec, bool centerp = true);
|
||||
|
||||
/**
|
||||
* The angular resolution of a Georef.
|
||||
*
|
||||
* @param[in] prec the precision of the Georef.
|
||||
* @return the latitude-longitude resolution (degrees).
|
||||
*
|
||||
* Internally, \e prec is first put in the range [−1, 11].
|
||||
**********************************************************************/
|
||||
static Math::real Resolution(int prec) {
|
||||
if (prec < 1)
|
||||
return real(prec < 0 ? 15 : 1);
|
||||
else {
|
||||
using std::pow;
|
||||
// Treat prec = 1 as 2.
|
||||
prec = (std::max)(2, (std::min)(int(maxprec_), prec));
|
||||
// Need extra real because, since C++11, pow(float, int) returns double
|
||||
return 1/(60 * real(pow(real(base_), prec - 2)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Georef precision required to meet a given geographic resolution.
|
||||
*
|
||||
* @param[in] res the minimum of resolution in latitude and longitude
|
||||
* (degrees).
|
||||
* @return Georef precision.
|
||||
*
|
||||
* The returned length is in the range [0, 11].
|
||||
**********************************************************************/
|
||||
static int Precision(real res) {
|
||||
using std::fabs; res = fabs(res);
|
||||
for (int prec = 0; prec < maxprec_; ++prec) {
|
||||
if (prec == 1)
|
||||
continue;
|
||||
if (Resolution(prec) <= res)
|
||||
return prec;
|
||||
}
|
||||
return maxprec_;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_GEOREF_HPP
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* \file Gnomonic.hpp
|
||||
* \brief Header for GeographicLib::Gnomonic class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2010-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GNOMONIC_HPP)
|
||||
#define GEOGRAPHICLIB_GNOMONIC_HPP 1
|
||||
|
||||
#include <GeographicLib/Geodesic.hpp>
|
||||
#include <GeographicLib/GeodesicLine.hpp>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief %Gnomonic projection
|
||||
*
|
||||
* %Gnomonic projection centered at an arbitrary position \e C on the
|
||||
* ellipsoid. This projection is derived in Section 8 of
|
||||
* - C. F. F. Karney,
|
||||
* <a href="https://doi.org/10.1007/s00190-012-0578-z">
|
||||
* Algorithms for geodesics</a>,
|
||||
* J. Geodesy <b>87</b>, 43--55 (2013);
|
||||
* DOI: <a href="https://doi.org/10.1007/s00190-012-0578-z">
|
||||
* 10.1007/s00190-012-0578-z</a>;
|
||||
* addenda:
|
||||
* <a href="https://geographiclib.sourceforge.io/geod-addenda.html">
|
||||
* geod-addenda.html</a>.
|
||||
* .
|
||||
* The projection of \e P is defined as follows: compute the geodesic line
|
||||
* from \e C to \e P; compute the reduced length \e m12, geodesic scale \e
|
||||
* M12, and ρ = <i>m12</i>/\e M12; finally \e x = ρ sin \e azi1; \e
|
||||
* y = ρ cos \e azi1, where \e azi1 is the azimuth of the geodesic at \e
|
||||
* C. The Gnomonic::Forward and Gnomonic::Reverse methods also return the
|
||||
* azimuth \e azi of the geodesic at \e P and reciprocal scale \e rk in the
|
||||
* azimuthal direction. The scale in the radial direction if
|
||||
* 1/<i>rk</i><sup>2</sup>.
|
||||
*
|
||||
* For a sphere, ρ is reduces to \e a tan(<i>s12</i>/<i>a</i>), where \e
|
||||
* s12 is the length of the geodesic from \e C to \e P, and the gnomonic
|
||||
* projection has the property that all geodesics appear as straight lines.
|
||||
* For an ellipsoid, this property holds only for geodesics interesting the
|
||||
* centers. However geodesic segments close to the center are approximately
|
||||
* straight.
|
||||
*
|
||||
* Consider a geodesic segment of length \e l. Let \e T be the point on the
|
||||
* geodesic (extended if necessary) closest to \e C the center of the
|
||||
* projection and \e t be the distance \e CT. To lowest order, the maximum
|
||||
* deviation (as a true distance) of the corresponding gnomonic line segment
|
||||
* (i.e., with the same end points) from the geodesic is<br>
|
||||
* <br>
|
||||
* (<i>K</i>(<i>T</i>) - <i>K</i>(<i>C</i>))
|
||||
* <i>l</i><sup>2</sup> \e t / 32.<br>
|
||||
* <br>
|
||||
* where \e K is the Gaussian curvature.
|
||||
*
|
||||
* This result applies for any surface. For an ellipsoid of revolution,
|
||||
* consider all geodesics whose end points are within a distance \e r of \e
|
||||
* C. For a given \e r, the deviation is maximum when the latitude of \e C
|
||||
* is 45°, when endpoints are a distance \e r away, and when their
|
||||
* azimuths from the center are ± 45° or ± 135°.
|
||||
* To lowest order in \e r and the flattening \e f, the deviation is \e f
|
||||
* (<i>r</i>/2<i>a</i>)<sup>3</sup> \e r.
|
||||
*
|
||||
* The conversions all take place using a Geodesic object (by default
|
||||
* Geodesic::WGS84()). For more information on geodesics see \ref geodesic.
|
||||
*
|
||||
* \warning The definition of this projection for a sphere is
|
||||
* standard. However, there is no standard for how it should be extended to
|
||||
* an ellipsoid. The choices are:
|
||||
* - Declare that the projection is undefined for an ellipsoid.
|
||||
* - Project to a tangent plane from the center of the ellipsoid. This
|
||||
* causes great ellipses to appear as straight lines in the projection;
|
||||
* i.e., it generalizes the spherical great circle to a great ellipse.
|
||||
* This was proposed by independently by Bowring and Williams in 1997.
|
||||
* - Project to the conformal sphere with the constant of integration chosen
|
||||
* so that the values of the latitude match for the center point and
|
||||
* perform a central projection onto the plane tangent to the conformal
|
||||
* sphere at the center point. This causes normal sections through the
|
||||
* center point to appear as straight lines in the projection; i.e., it
|
||||
* generalizes the spherical great circle to a normal section. This was
|
||||
* proposed by I. G. Letoval'tsev, Generalization of the gnomonic
|
||||
* projection for a spheroid and the principal geodetic problems involved
|
||||
* in the alignment of surface routes, Geodesy and Aerophotography (5),
|
||||
* 271--274 (1963).
|
||||
* - The projection given here. This causes geodesics close to the center
|
||||
* point to appear as straight lines in the projection; i.e., it
|
||||
* generalizes the spherical great circle to a geodesic.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Gnomonic.cpp
|
||||
*
|
||||
* <a href="GeodesicProj.1.html">GeodesicProj</a> is a command-line utility
|
||||
* providing access to the functionality of AzimuthalEquidistant, Gnomonic,
|
||||
* and CassiniSoldner.
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT Gnomonic {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
real eps0_, eps_;
|
||||
Geodesic _earth;
|
||||
real _a, _f;
|
||||
// numit_ increased from 10 to 20 to fix convergence failure with high
|
||||
// precision (e.g., GEOGRAPHICLIB_DIGITS=2000) calculations. Reverse uses
|
||||
// Newton's method which converges quadratically and so numit_ = 10 would
|
||||
// normally be big enough. However, since the Geodesic class is based on a
|
||||
// series it is of limited accuracy; in particular, the derivative rules
|
||||
// used by Reverse only hold approximately. Consequently, after a few
|
||||
// iterations, the convergence in the Reverse falls back to improvements in
|
||||
// each step by a constant (albeit small) factor.
|
||||
static const int numit_ = 20;
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for Gnomonic.
|
||||
*
|
||||
* @param[in] earth the Geodesic object to use for geodesic calculations.
|
||||
* By default this uses the WGS84 ellipsoid.
|
||||
**********************************************************************/
|
||||
explicit Gnomonic(const Geodesic& earth = Geodesic::WGS84());
|
||||
|
||||
/**
|
||||
* Forward projection, from geographic to gnomonic.
|
||||
*
|
||||
* @param[in] lat0 latitude of center point of projection (degrees).
|
||||
* @param[in] lon0 longitude of center point of projection (degrees).
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[out] x easting of point (meters).
|
||||
* @param[out] y northing of point (meters).
|
||||
* @param[out] azi azimuth of geodesic at point (degrees).
|
||||
* @param[out] rk reciprocal of azimuthal scale at point.
|
||||
*
|
||||
* \e lat0 and \e lat should be in the range [−90°, 90°].
|
||||
* The scale of the projection is 1/<i>rk</i><sup>2</sup> in the "radial"
|
||||
* direction, \e azi clockwise from true north, and is 1/\e rk in the
|
||||
* direction perpendicular to this. If the point lies "over the horizon",
|
||||
* i.e., if \e rk ≤ 0, then NaNs are returned for \e x and \e y (the
|
||||
* correct values are returned for \e azi and \e rk). A call to Forward
|
||||
* followed by a call to Reverse will return the original (\e lat, \e lon)
|
||||
* (to within roundoff) provided the point in not over the horizon.
|
||||
**********************************************************************/
|
||||
void Forward(real lat0, real lon0, real lat, real lon,
|
||||
real& x, real& y, real& azi, real& rk) const;
|
||||
|
||||
/**
|
||||
* Reverse projection, from gnomonic to geographic.
|
||||
*
|
||||
* @param[in] lat0 latitude of center point of projection (degrees).
|
||||
* @param[in] lon0 longitude of center point of projection (degrees).
|
||||
* @param[in] x easting of point (meters).
|
||||
* @param[in] y northing of point (meters).
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] azi azimuth of geodesic at point (degrees).
|
||||
* @param[out] rk reciprocal of azimuthal scale at point.
|
||||
*
|
||||
* \e lat0 should be in the range [−90°, 90°]. \e lat will
|
||||
* be in the range [−90°, 90°] and \e lon will be in the
|
||||
* range [−180°, 180°]. The scale of the projection is
|
||||
* 1/<i>rk</i><sup>2</sup> in the "radial" direction, \e azi clockwise from
|
||||
* true north, and is 1/\e rk in the direction perpendicular to this. Even
|
||||
* though all inputs should return a valid \e lat and \e lon, it's possible
|
||||
* that the procedure fails to converge for very large \e x or \e y; in
|
||||
* this case NaNs are returned for all the output arguments. A call to
|
||||
* Reverse followed by a call to Forward will return the original (\e x, \e
|
||||
* y) (to roundoff).
|
||||
**********************************************************************/
|
||||
void Reverse(real lat0, real lon0, real x, real y,
|
||||
real& lat, real& lon, real& azi, real& rk) const;
|
||||
|
||||
/**
|
||||
* Gnomonic::Forward without returning the azimuth and scale.
|
||||
**********************************************************************/
|
||||
void Forward(real lat0, real lon0, real lat, real lon,
|
||||
real& x, real& y) const {
|
||||
real azi, rk;
|
||||
Forward(lat0, lon0, lat, lon, x, y, azi, rk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gnomonic::Reverse without returning the azimuth and scale.
|
||||
**********************************************************************/
|
||||
void Reverse(real lat0, real lon0, real x, real y,
|
||||
real& lat, real& lon) const {
|
||||
real azi, rk;
|
||||
Reverse(lat0, lon0, x, y, lat, lon, azi, rk);
|
||||
}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value inherited from the Geodesic object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the value
|
||||
* inherited from the Geodesic object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _earth.Flattening(); }
|
||||
///@}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_GNOMONIC_HPP
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
/**
|
||||
* \file GravityCircle.hpp
|
||||
* \brief Header for GeographicLib::GravityCircle class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2011-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GRAVITYCIRCLE_HPP)
|
||||
#define GEOGRAPHICLIB_GRAVITYCIRCLE_HPP 1
|
||||
|
||||
#include <vector>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/CircularEngine.hpp>
|
||||
#include <GeographicLib/GravityModel.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Gravity on a circle of latitude
|
||||
*
|
||||
* Evaluate the earth's gravity field on a circle of constant height and
|
||||
* latitude. This uses a CircularEngine to pre-evaluate the inner sum of the
|
||||
* spherical harmonic sum, allowing the values of the field at several
|
||||
* different longitudes to be evaluated rapidly.
|
||||
*
|
||||
* Use GravityModel::Circle to create a GravityCircle object. (The
|
||||
* constructor for this class is private.)
|
||||
*
|
||||
* See \ref gravityparallel for an example of using GravityCircle (together
|
||||
* with OpenMP) to speed up the computation of geoid heights.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-GravityCircle.cpp
|
||||
*
|
||||
* <a href="Gravity.1.html">Gravity</a> is a command-line utility providing
|
||||
* access to the functionality of GravityModel and GravityCircle.
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT GravityCircle {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
enum mask {
|
||||
NONE = GravityModel::NONE,
|
||||
GRAVITY = GravityModel::GRAVITY,
|
||||
DISTURBANCE = GravityModel::DISTURBANCE,
|
||||
DISTURBING_POTENTIAL = GravityModel::DISTURBING_POTENTIAL,
|
||||
GEOID_HEIGHT = GravityModel::GEOID_HEIGHT,
|
||||
SPHERICAL_ANOMALY = GravityModel::SPHERICAL_ANOMALY,
|
||||
ALL = GravityModel::ALL,
|
||||
};
|
||||
|
||||
unsigned _caps;
|
||||
real _a, _f, _lat, _h, _zZ, _pPx, _invR, _cpsi, _spsi,
|
||||
_cphi, _sphi, _amodel, _gGMmodel, _dzonal0,
|
||||
_corrmult, _gamma0, _gamma, _frot;
|
||||
CircularEngine _gravitational, _disturbing, _correction;
|
||||
|
||||
GravityCircle(mask caps, real a, real f, real lat, real h,
|
||||
real Z, real P, real cphi, real sphi,
|
||||
real amodel, real GMmodel,
|
||||
real dzonal0, real corrmult,
|
||||
real gamma0, real gamma, real frot,
|
||||
const CircularEngine& gravitational,
|
||||
const CircularEngine& disturbing,
|
||||
const CircularEngine& correction);
|
||||
|
||||
friend class GravityModel; // GravityModel calls the private constructor
|
||||
Math::real W(real slam, real clam,
|
||||
real& gX, real& gY, real& gZ) const;
|
||||
Math::real V(real slam, real clam,
|
||||
real& gX, real& gY, real& gZ) const;
|
||||
Math::real InternalT(real slam, real clam,
|
||||
real& deltaX, real& deltaY, real& deltaZ,
|
||||
bool gradp, bool correct) const;
|
||||
public:
|
||||
/**
|
||||
* A default constructor for the normal gravity. This sets up an
|
||||
* uninitialized object which can be later replaced by the
|
||||
* GravityModel::Circle.
|
||||
**********************************************************************/
|
||||
GravityCircle() : _a(-1) {}
|
||||
|
||||
/** \name Compute the gravitational field
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Evaluate the gravity.
|
||||
*
|
||||
* @param[in] lon the geographic longitude (degrees).
|
||||
* @param[out] gx the easterly component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] gy the northerly component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] gz the upward component of the acceleration
|
||||
* (m s<sup>−2</sup>); this is usually negative.
|
||||
* @return \e W the sum of the gravitational and centrifugal potentials
|
||||
* (m<sup>2</sup> s<sup>−2</sup>).
|
||||
*
|
||||
* The function includes the effects of the earth's rotation.
|
||||
**********************************************************************/
|
||||
Math::real Gravity(real lon, real& gx, real& gy, real& gz) const;
|
||||
|
||||
/**
|
||||
* Evaluate the gravity disturbance vector.
|
||||
*
|
||||
* @param[in] lon the geographic longitude (degrees).
|
||||
* @param[out] deltax the easterly component of the disturbance vector
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] deltay the northerly component of the disturbance vector
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] deltaz the upward component of the disturbance vector
|
||||
* (m s<sup>−2</sup>).
|
||||
* @return \e T the corresponding disturbing potential
|
||||
* (m<sup>2</sup> s<sup>−2</sup>).
|
||||
**********************************************************************/
|
||||
Math::real Disturbance(real lon, real& deltax, real& deltay, real& deltaz)
|
||||
const;
|
||||
|
||||
/**
|
||||
* Evaluate the geoid height.
|
||||
*
|
||||
* @param[in] lon the geographic longitude (degrees).
|
||||
* @return \e N the height of the geoid above the reference ellipsoid
|
||||
* (meters).
|
||||
*
|
||||
* Some approximations are made in computing the geoid height so that the
|
||||
* results of the NGA codes are reproduced accurately. Details are given
|
||||
* in \ref gravitygeoid.
|
||||
**********************************************************************/
|
||||
Math::real GeoidHeight(real lon) const;
|
||||
|
||||
/**
|
||||
* Evaluate the components of the gravity anomaly vector using the
|
||||
* spherical approximation.
|
||||
*
|
||||
* @param[in] lon the geographic longitude (degrees).
|
||||
* @param[out] Dg01 the gravity anomaly (m s<sup>−2</sup>).
|
||||
* @param[out] xi the northerly component of the deflection of the vertical
|
||||
* (degrees).
|
||||
* @param[out] eta the easterly component of the deflection of the vertical
|
||||
* (degrees).
|
||||
*
|
||||
* The spherical approximation (see Heiskanen and Moritz, Sec 2-14) is used
|
||||
* so that the results of the NGA codes are reproduced accurately.
|
||||
* approximations used here. Details are given in \ref gravitygeoid.
|
||||
**********************************************************************/
|
||||
void SphericalAnomaly(real lon, real& Dg01, real& xi, real& eta)
|
||||
const;
|
||||
|
||||
/**
|
||||
* Evaluate the components of the acceleration due to gravity and the
|
||||
* centrifugal acceleration in geocentric coordinates.
|
||||
*
|
||||
* @param[in] lon the geographic longitude (degrees).
|
||||
* @param[out] gX the \e X component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] gY the \e Y component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] gZ the \e Z component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @return \e W = \e V + Φ the sum of the gravitational and
|
||||
* centrifugal potentials (m<sup>2</sup> s<sup>−2</sup>).
|
||||
**********************************************************************/
|
||||
Math::real W(real lon, real& gX, real& gY, real& gZ) const {
|
||||
real slam, clam;
|
||||
Math::sincosd(lon, slam, clam);
|
||||
return W(slam, clam, gX, gY, gZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the components of the acceleration due to gravity in geocentric
|
||||
* coordinates.
|
||||
*
|
||||
* @param[in] lon the geographic longitude (degrees).
|
||||
* @param[out] GX the \e X component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] GY the \e Y component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] GZ the \e Z component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @return \e V = \e W - Φ the gravitational potential
|
||||
* (m<sup>2</sup> s<sup>−2</sup>).
|
||||
**********************************************************************/
|
||||
Math::real V(real lon, real& GX, real& GY, real& GZ) const {
|
||||
real slam, clam;
|
||||
Math::sincosd(lon, slam, clam);
|
||||
return V(slam, clam, GX, GY, GZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the components of the gravity disturbance in geocentric
|
||||
* coordinates.
|
||||
*
|
||||
* @param[in] lon the geographic longitude (degrees).
|
||||
* @param[out] deltaX the \e X component of the gravity disturbance
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] deltaY the \e Y component of the gravity disturbance
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] deltaZ the \e Z component of the gravity disturbance
|
||||
* (m s<sup>−2</sup>).
|
||||
* @return \e T = \e W - \e U the disturbing potential (also called the
|
||||
* anomalous potential) (m<sup>2</sup> s<sup>−2</sup>).
|
||||
**********************************************************************/
|
||||
Math::real T(real lon, real& deltaX, real& deltaY, real& deltaZ)
|
||||
const {
|
||||
real slam, clam;
|
||||
Math::sincosd(lon, slam, clam);
|
||||
return InternalT(slam, clam, deltaX, deltaY, deltaZ, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate disturbing potential in geocentric coordinates.
|
||||
*
|
||||
* @param[in] lon the geographic longitude (degrees).
|
||||
* @return \e T = \e W - \e U the disturbing potential (also called the
|
||||
* anomalous potential) (m<sup>2</sup> s<sup>−2</sup>).
|
||||
**********************************************************************/
|
||||
Math::real T(real lon) const {
|
||||
real slam, clam, dummy;
|
||||
Math::sincosd(lon, slam, clam);
|
||||
return InternalT(slam, clam, dummy, dummy, dummy, false, true);
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return true if the object has been initialized.
|
||||
**********************************************************************/
|
||||
bool Init() const { return _a > 0; }
|
||||
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value inherited from the GravityModel object used in the
|
||||
* constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const
|
||||
{ return Init() ? _a : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the value
|
||||
* inherited from the GravityModel object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const
|
||||
{ return Init() ? _f : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return the latitude of the circle (degrees).
|
||||
**********************************************************************/
|
||||
Math::real Latitude() const
|
||||
{ return Init() ? _lat : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return the height of the circle (meters).
|
||||
**********************************************************************/
|
||||
Math::real Height() const
|
||||
{ return Init() ? _h : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return \e caps the computational capabilities that this object was
|
||||
* constructed with.
|
||||
**********************************************************************/
|
||||
unsigned Capabilities() const { return _caps; }
|
||||
|
||||
/**
|
||||
* @param[in] testcaps a set of bitor'ed GravityModel::mask values.
|
||||
* @return true if the GravityCircle object has all these capabilities.
|
||||
**********************************************************************/
|
||||
bool Capabilities(unsigned testcaps) const {
|
||||
return (_caps & testcaps) == testcaps;
|
||||
}
|
||||
///@}
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_GRAVITYCIRCLE_HPP
|
||||
+554
@@ -0,0 +1,554 @@
|
||||
/**
|
||||
* \file GravityModel.hpp
|
||||
* \brief Header for GeographicLib::GravityModel class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2011-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GRAVITYMODEL_HPP)
|
||||
#define GEOGRAPHICLIB_GRAVITYMODEL_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/NormalGravity.hpp>
|
||||
#include <GeographicLib/SphericalHarmonic.hpp>
|
||||
#include <GeographicLib/SphericalHarmonic1.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Squelch warnings about dll vs vector
|
||||
# pragma warning (push)
|
||||
# pragma warning (disable: 4251)
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
class GravityCircle;
|
||||
|
||||
/**
|
||||
* \brief Model of the earth's gravity field
|
||||
*
|
||||
* Evaluate the earth's gravity field according to a model. The supported
|
||||
* models treat only the gravitational field exterior to the mass of the
|
||||
* earth. When computing the field at points near (but above) the surface of
|
||||
* the earth a small correction can be applied to account for the mass of the
|
||||
* atmosphere above the point in question; see \ref gravityatmos.
|
||||
* Determining the height of the geoid above the ellipsoid entails correcting
|
||||
* for the mass of the earth above the geoid. The egm96 and egm2008 include
|
||||
* separate correction terms to account for this mass.
|
||||
*
|
||||
* Definitions and terminology (from Heiskanen and Moritz, Sec 2-13):
|
||||
* - \e V = gravitational potential;
|
||||
* - Φ = rotational potential;
|
||||
* - \e W = \e V + Φ = \e T + \e U = total potential;
|
||||
* - <i>V</i><sub>0</sub> = normal gravitation potential;
|
||||
* - \e U = <i>V</i><sub>0</sub> + Φ = total normal potential;
|
||||
* - \e T = \e W − \e U = \e V − <i>V</i><sub>0</sub> = anomalous
|
||||
* or disturbing potential;
|
||||
* - <b>g</b> = ∇\e W = <b>γ</b> + <b>δ</b>;
|
||||
* - <b>f</b> = ∇Φ;
|
||||
* - <b>Γ</b> = ∇<i>V</i><sub>0</sub>;
|
||||
* - <b>γ</b> = ∇\e U;
|
||||
* - <b>δ</b> = ∇\e T = gravity disturbance vector
|
||||
* = <b>g</b><sub><i>P</i></sub> − <b>γ</b><sub><i>P</i></sub>;
|
||||
* - δ\e g = gravity disturbance = <i>g</i><sub><i>P</i></sub> −
|
||||
* γ<sub><i>P</i></sub>;
|
||||
* - Δ<b>g</b> = gravity anomaly vector = <b>g</b><sub><i>P</i></sub>
|
||||
* − <b>γ</b><sub><i>Q</i></sub>; here the line \e PQ is
|
||||
* perpendicular to ellipsoid and the potential at \e P equals the normal
|
||||
* potential at \e Q;
|
||||
* - Δ\e g = gravity anomaly = <i>g</i><sub><i>P</i></sub> −
|
||||
* γ<sub><i>Q</i></sub>;
|
||||
* - (ξ, η) deflection of the vertical, the difference in
|
||||
* directions of <b>g</b><sub><i>P</i></sub> and
|
||||
* <b>γ</b><sub><i>Q</i></sub>, ξ = NS, η = EW.
|
||||
* - \e X, \e Y, \e Z, geocentric coordinates;
|
||||
* - \e x, \e y, \e z, local cartesian coordinates used to denote the east,
|
||||
* north and up directions.
|
||||
*
|
||||
* See \ref gravity for details of how to install the gravity models and the
|
||||
* data format.
|
||||
*
|
||||
* References:
|
||||
* - W. A. Heiskanen and H. Moritz, Physical Geodesy (Freeman, San
|
||||
* Francisco, 1967).
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-GravityModel.cpp
|
||||
*
|
||||
* <a href="Gravity.1.html">Gravity</a> is a command-line utility providing
|
||||
* access to the functionality of GravityModel and GravityCircle.
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT GravityModel {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
friend class GravityCircle;
|
||||
static const int idlength_ = 8;
|
||||
std::string _name, _dir, _description, _date, _filename, _id;
|
||||
real _amodel, _gGMmodel, _zeta0, _corrmult;
|
||||
int _nmx, _mmx;
|
||||
SphericalHarmonic::normalization _norm;
|
||||
NormalGravity _earth;
|
||||
std::vector<real> _cCx, _sSx, _cCC, _cCS, _zonal;
|
||||
real _dzonal0; // A left over contribution to _zonal.
|
||||
SphericalHarmonic _gravitational;
|
||||
SphericalHarmonic1 _disturbing;
|
||||
SphericalHarmonic _correction;
|
||||
void ReadMetadata(const std::string& name);
|
||||
Math::real InternalT(real X, real Y, real Z,
|
||||
real& deltaX, real& deltaY, real& deltaZ,
|
||||
bool gradp, bool correct) const;
|
||||
GravityModel(const GravityModel&) = delete; // copy constructor not allowed
|
||||
// nor copy assignment
|
||||
GravityModel& operator=(const GravityModel&) = delete;
|
||||
|
||||
enum captype {
|
||||
CAP_NONE = 0U,
|
||||
CAP_G = 1U<<0, // implies potentials W and V
|
||||
CAP_T = 1U<<1,
|
||||
CAP_DELTA = 1U<<2 | CAP_T, // delta implies T?
|
||||
CAP_C = 1U<<3,
|
||||
CAP_GAMMA0 = 1U<<4,
|
||||
CAP_GAMMA = 1U<<5,
|
||||
CAP_ALL = 0x3FU,
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Bit masks for the capabilities to be given to the GravityCircle object
|
||||
* produced by Circle.
|
||||
**********************************************************************/
|
||||
enum mask {
|
||||
/**
|
||||
* No capabilities.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
NONE = 0U,
|
||||
/**
|
||||
* Allow calls to GravityCircle::Gravity, GravityCircle::W, and
|
||||
* GravityCircle::V.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
GRAVITY = CAP_G,
|
||||
/**
|
||||
* Allow calls to GravityCircle::Disturbance and GravityCircle::T.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
DISTURBANCE = CAP_DELTA,
|
||||
/**
|
||||
* Allow calls to GravityCircle::T(real lon) (i.e., computing the
|
||||
* disturbing potential and not the gravity disturbance vector).
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
DISTURBING_POTENTIAL = CAP_T,
|
||||
/**
|
||||
* Allow calls to GravityCircle::SphericalAnomaly.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
SPHERICAL_ANOMALY = CAP_DELTA | CAP_GAMMA,
|
||||
/**
|
||||
* Allow calls to GravityCircle::GeoidHeight.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
GEOID_HEIGHT = CAP_T | CAP_C | CAP_GAMMA0,
|
||||
/**
|
||||
* All capabilities.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
ALL = CAP_ALL,
|
||||
};
|
||||
|
||||
/**
|
||||
* Move constructs a gravity model.
|
||||
**********************************************************************/
|
||||
GravityModel(GravityModel&&) = default;
|
||||
|
||||
/**
|
||||
* Move assigns a gravity model.
|
||||
**********************************************************************/
|
||||
GravityModel& operator=(GravityModel&&) = default;
|
||||
|
||||
/** \name Setting up the gravity model
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Construct a gravity model.
|
||||
*
|
||||
* @param[in] name the name of the model.
|
||||
* @param[in] path (optional) directory for data file.
|
||||
* @param[in] Nmax (optional) if nonnegative, truncate the degree of the
|
||||
* model this value.
|
||||
* @param[in] Mmax (optional) if nonnegative, truncate the order of the
|
||||
* model this value.
|
||||
* @exception GeographicErr if the data file cannot be found, is
|
||||
* unreadable, or is corrupt, or if \e Mmax > \e Nmax.
|
||||
* @exception std::bad_alloc if the memory necessary for storing the model
|
||||
* can't be allocated.
|
||||
*
|
||||
* A filename is formed by appending ".egm" (World Gravity Model) to the
|
||||
* name. If \e path is specified (and is non-empty), then the file is
|
||||
* loaded from directory, \e path. Otherwise the path is given by
|
||||
* DefaultGravityPath().
|
||||
*
|
||||
* This file contains the metadata which specifies the properties of the
|
||||
* model. The coefficients for the spherical harmonic sums are obtained
|
||||
* from a file obtained by appending ".cof" to metadata file (so the
|
||||
* filename ends in ".egm.cof").
|
||||
*
|
||||
* If \e Nmax ≥ 0 and \e Mmax < 0, then \e Mmax is set to \e Nmax.
|
||||
* After the model is loaded, the maximum degree and order of the model can
|
||||
* be found by the Degree() and Order() methods.
|
||||
**********************************************************************/
|
||||
explicit GravityModel(const std::string& name,
|
||||
const std::string& path = "",
|
||||
int Nmax = -1, int Mmax = -1);
|
||||
///@}
|
||||
|
||||
/** \name Compute gravity in geodetic coordinates
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Evaluate the gravity at an arbitrary point above (or below) the
|
||||
* ellipsoid.
|
||||
*
|
||||
* @param[in] lat the geographic latitude (degrees).
|
||||
* @param[in] lon the geographic longitude (degrees).
|
||||
* @param[in] h the height above the ellipsoid (meters).
|
||||
* @param[out] gx the easterly component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] gy the northerly component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] gz the upward component of the acceleration
|
||||
* (m s<sup>−2</sup>); this is usually negative.
|
||||
* @return \e W the sum of the gravitational and centrifugal potentials
|
||||
* (m<sup>2</sup> s<sup>−2</sup>).
|
||||
*
|
||||
* The function includes the effects of the earth's rotation.
|
||||
**********************************************************************/
|
||||
Math::real Gravity(real lat, real lon, real h,
|
||||
real& gx, real& gy, real& gz) const;
|
||||
|
||||
/**
|
||||
* Evaluate the gravity disturbance vector at an arbitrary point above (or
|
||||
* below) the ellipsoid.
|
||||
*
|
||||
* @param[in] lat the geographic latitude (degrees).
|
||||
* @param[in] lon the geographic longitude (degrees).
|
||||
* @param[in] h the height above the ellipsoid (meters).
|
||||
* @param[out] deltax the easterly component of the disturbance vector
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] deltay the northerly component of the disturbance vector
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] deltaz the upward component of the disturbance vector
|
||||
* (m s<sup>−2</sup>).
|
||||
* @return \e T the corresponding disturbing potential
|
||||
* (m<sup>2</sup> s<sup>−2</sup>).
|
||||
**********************************************************************/
|
||||
Math::real Disturbance(real lat, real lon, real h,
|
||||
real& deltax, real& deltay, real& deltaz)
|
||||
const;
|
||||
|
||||
/**
|
||||
* Evaluate the geoid height.
|
||||
*
|
||||
* @param[in] lat the geographic latitude (degrees).
|
||||
* @param[in] lon the geographic longitude (degrees).
|
||||
* @return \e N the height of the geoid above the ReferenceEllipsoid()
|
||||
* (meters).
|
||||
*
|
||||
* This calls NormalGravity::U for ReferenceEllipsoid(). Some
|
||||
* approximations are made in computing the geoid height so that the
|
||||
* results of the NGA codes are reproduced accurately. Details are given
|
||||
* in \ref gravitygeoid.
|
||||
**********************************************************************/
|
||||
Math::real GeoidHeight(real lat, real lon) const;
|
||||
|
||||
/**
|
||||
* Evaluate the components of the gravity anomaly vector using the
|
||||
* spherical approximation.
|
||||
*
|
||||
* @param[in] lat the geographic latitude (degrees).
|
||||
* @param[in] lon the geographic longitude (degrees).
|
||||
* @param[in] h the height above the ellipsoid (meters).
|
||||
* @param[out] Dg01 the gravity anomaly (m s<sup>−2</sup>).
|
||||
* @param[out] xi the northerly component of the deflection of the vertical
|
||||
* (degrees).
|
||||
* @param[out] eta the easterly component of the deflection of the vertical
|
||||
* (degrees).
|
||||
*
|
||||
* The spherical approximation (see Heiskanen and Moritz, Sec 2-14) is used
|
||||
* so that the results of the NGA codes are reproduced accurately.
|
||||
* approximations used here. Details are given in \ref gravitygeoid.
|
||||
**********************************************************************/
|
||||
void SphericalAnomaly(real lat, real lon, real h,
|
||||
real& Dg01, real& xi, real& eta) const;
|
||||
///@}
|
||||
|
||||
/** \name Compute gravity in geocentric coordinates
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Evaluate the components of the acceleration due to gravity and the
|
||||
* centrifugal acceleration in geocentric coordinates.
|
||||
*
|
||||
* @param[in] X geocentric coordinate of point (meters).
|
||||
* @param[in] Y geocentric coordinate of point (meters).
|
||||
* @param[in] Z geocentric coordinate of point (meters).
|
||||
* @param[out] gX the \e X component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] gY the \e Y component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] gZ the \e Z component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @return \e W = \e V + Φ the sum of the gravitational and
|
||||
* centrifugal potentials (m<sup>2</sup> s<sup>−2</sup>).
|
||||
*
|
||||
* This calls NormalGravity::U for ReferenceEllipsoid().
|
||||
**********************************************************************/
|
||||
Math::real W(real X, real Y, real Z,
|
||||
real& gX, real& gY, real& gZ) const;
|
||||
|
||||
/**
|
||||
* Evaluate the components of the acceleration due to gravity in geocentric
|
||||
* coordinates.
|
||||
*
|
||||
* @param[in] X geocentric coordinate of point (meters).
|
||||
* @param[in] Y geocentric coordinate of point (meters).
|
||||
* @param[in] Z geocentric coordinate of point (meters).
|
||||
* @param[out] GX the \e X component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] GY the \e Y component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] GZ the \e Z component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @return \e V = \e W - Φ the gravitational potential
|
||||
* (m<sup>2</sup> s<sup>−2</sup>).
|
||||
**********************************************************************/
|
||||
Math::real V(real X, real Y, real Z,
|
||||
real& GX, real& GY, real& GZ) const;
|
||||
|
||||
/**
|
||||
* Evaluate the components of the gravity disturbance in geocentric
|
||||
* coordinates.
|
||||
*
|
||||
* @param[in] X geocentric coordinate of point (meters).
|
||||
* @param[in] Y geocentric coordinate of point (meters).
|
||||
* @param[in] Z geocentric coordinate of point (meters).
|
||||
* @param[out] deltaX the \e X component of the gravity disturbance
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] deltaY the \e Y component of the gravity disturbance
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] deltaZ the \e Z component of the gravity disturbance
|
||||
* (m s<sup>−2</sup>).
|
||||
* @return \e T = \e W - \e U the disturbing potential (also called the
|
||||
* anomalous potential) (m<sup>2</sup> s<sup>−2</sup>).
|
||||
**********************************************************************/
|
||||
Math::real T(real X, real Y, real Z,
|
||||
real& deltaX, real& deltaY, real& deltaZ) const
|
||||
{ return InternalT(X, Y, Z, deltaX, deltaY, deltaZ, true, true); }
|
||||
|
||||
/**
|
||||
* Evaluate disturbing potential in geocentric coordinates.
|
||||
*
|
||||
* @param[in] X geocentric coordinate of point (meters).
|
||||
* @param[in] Y geocentric coordinate of point (meters).
|
||||
* @param[in] Z geocentric coordinate of point (meters).
|
||||
* @return \e T = \e W - \e U the disturbing potential (also called the
|
||||
* anomalous potential) (m<sup>2</sup> s<sup>−2</sup>).
|
||||
**********************************************************************/
|
||||
Math::real T(real X, real Y, real Z) const {
|
||||
real dummy;
|
||||
return InternalT(X, Y, Z, dummy, dummy, dummy, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the components of the acceleration due to normal gravity and
|
||||
* the centrifugal acceleration in geocentric coordinates.
|
||||
*
|
||||
* @param[in] X geocentric coordinate of point (meters).
|
||||
* @param[in] Y geocentric coordinate of point (meters).
|
||||
* @param[in] Z geocentric coordinate of point (meters).
|
||||
* @param[out] gammaX the \e X component of the normal acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] gammaY the \e Y component of the normal acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] gammaZ the \e Z component of the normal acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @return \e U = <i>V</i><sub>0</sub> + Φ the sum of the
|
||||
* normal gravitational and centrifugal potentials
|
||||
* (m<sup>2</sup> s<sup>−2</sup>).
|
||||
*
|
||||
* This calls NormalGravity::U for ReferenceEllipsoid().
|
||||
**********************************************************************/
|
||||
Math::real U(real X, real Y, real Z,
|
||||
real& gammaX, real& gammaY, real& gammaZ) const
|
||||
{ return _earth.U(X, Y, Z, gammaX, gammaY, gammaZ); }
|
||||
|
||||
/**
|
||||
* Evaluate the centrifugal acceleration in geocentric coordinates.
|
||||
*
|
||||
* @param[in] X geocentric coordinate of point (meters).
|
||||
* @param[in] Y geocentric coordinate of point (meters).
|
||||
* @param[out] fX the \e X component of the centrifugal acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] fY the \e Y component of the centrifugal acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @return Φ the centrifugal potential (m<sup>2</sup>
|
||||
* s<sup>−2</sup>).
|
||||
*
|
||||
* This calls NormalGravity::Phi for ReferenceEllipsoid().
|
||||
**********************************************************************/
|
||||
Math::real Phi(real X, real Y, real& fX, real& fY) const
|
||||
{ return _earth.Phi(X, Y, fX, fY); }
|
||||
///@}
|
||||
|
||||
/** \name Compute gravity on a circle of constant latitude
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Create a GravityCircle object to allow the gravity field at many points
|
||||
* with constant \e lat and \e h and varying \e lon to be computed
|
||||
* efficiently.
|
||||
*
|
||||
* @param[in] lat latitude of the point (degrees).
|
||||
* @param[in] h the height of the point above the ellipsoid (meters).
|
||||
* @param[in] caps bitor'ed combination of GravityModel::mask values
|
||||
* specifying the capabilities of the resulting GravityCircle object.
|
||||
* @exception std::bad_alloc if the memory necessary for creating a
|
||||
* GravityCircle can't be allocated.
|
||||
* @return a GravityCircle object whose member functions computes the
|
||||
* gravitational field at a particular values of \e lon.
|
||||
*
|
||||
* The GravityModel::mask values are
|
||||
* - \e caps |= GravityModel::GRAVITY
|
||||
* - \e caps |= GravityModel::DISTURBANCE
|
||||
* - \e caps |= GravityModel::DISTURBING_POTENTIAL
|
||||
* - \e caps |= GravityModel::SPHERICAL_ANOMALY
|
||||
* - \e caps |= GravityModel::GEOID_HEIGHT
|
||||
* .
|
||||
* The default value of \e caps is GravityModel::ALL which turns on all the
|
||||
* capabilities. If an unsupported function is invoked, it will return
|
||||
* NaNs. Note that GravityModel::GEOID_HEIGHT will only be honored if \e h
|
||||
* = 0.
|
||||
*
|
||||
* If the field at several points on a circle of latitude need to be
|
||||
* calculated then creating a GravityCircle object and using its member
|
||||
* functions will be substantially faster, especially for high-degree
|
||||
* models. See \ref gravityparallel for an example of using GravityCircle
|
||||
* (together with OpenMP) to speed up the computation of geoid heights.
|
||||
**********************************************************************/
|
||||
GravityCircle Circle(real lat, real h, unsigned caps = ALL) const;
|
||||
///@}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @return the NormalGravity object for the reference ellipsoid.
|
||||
**********************************************************************/
|
||||
const NormalGravity& ReferenceEllipsoid() const { return _earth; }
|
||||
|
||||
/**
|
||||
* @return the description of the gravity model, if available, in the data
|
||||
* file; if absent, return "NONE".
|
||||
**********************************************************************/
|
||||
const std::string& Description() const { return _description; }
|
||||
|
||||
/**
|
||||
* @return date of the model; if absent, return "UNKNOWN".
|
||||
**********************************************************************/
|
||||
const std::string& DateTime() const { return _date; }
|
||||
|
||||
/**
|
||||
* @return full file name used to load the gravity model.
|
||||
**********************************************************************/
|
||||
const std::string& GravityFile() const { return _filename; }
|
||||
|
||||
/**
|
||||
* @return "name" used to load the gravity model (from the first argument
|
||||
* of the constructor, but this may be overridden by the model file).
|
||||
**********************************************************************/
|
||||
const std::string& GravityModelName() const { return _name; }
|
||||
|
||||
/**
|
||||
* @return directory used to load the gravity model.
|
||||
**********************************************************************/
|
||||
const std::string& GravityModelDirectory() const { return _dir; }
|
||||
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters).
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); }
|
||||
|
||||
/**
|
||||
* @return \e GM the mass constant of the model (m<sup>3</sup>
|
||||
* s<sup>−2</sup>); this is the product of \e G the gravitational
|
||||
* constant and \e M the mass of the earth (usually including the mass of
|
||||
* the earth's atmosphere).
|
||||
**********************************************************************/
|
||||
Math::real MassConstant() const { return _gGMmodel; }
|
||||
|
||||
/**
|
||||
* @return \e GM the mass constant of the ReferenceEllipsoid()
|
||||
* (m<sup>3</sup> s<sup>−2</sup>).
|
||||
**********************************************************************/
|
||||
Math::real ReferenceMassConstant() const
|
||||
{ return _earth.MassConstant(); }
|
||||
|
||||
/**
|
||||
* @return ω the angular velocity of the model and the
|
||||
* ReferenceEllipsoid() (rad s<sup>−1</sup>).
|
||||
**********************************************************************/
|
||||
Math::real AngularVelocity() const
|
||||
{ return _earth.AngularVelocity(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _earth.Flattening(); }
|
||||
|
||||
/**
|
||||
* @return \e Nmax the maximum degree of the components of the model.
|
||||
**********************************************************************/
|
||||
int Degree() const { return _nmx; }
|
||||
|
||||
/**
|
||||
* @return \e Mmax the maximum order of the components of the model.
|
||||
**********************************************************************/
|
||||
int Order() const { return _mmx; }
|
||||
///@}
|
||||
|
||||
/**
|
||||
* @return the default path for gravity model data files.
|
||||
*
|
||||
* This is the value of the environment variable
|
||||
* GEOGRAPHICLIB_GRAVITY_PATH, if set; otherwise, it is
|
||||
* $GEOGRAPHICLIB_DATA/gravity if the environment variable
|
||||
* GEOGRAPHICLIB_DATA is set; otherwise, it is a compile-time default
|
||||
* (/usr/local/share/GeographicLib/gravity on non-Windows systems and
|
||||
* C:/ProgramData/GeographicLib/gravity on Windows systems).
|
||||
**********************************************************************/
|
||||
static std::string DefaultGravityPath();
|
||||
|
||||
/**
|
||||
* @return the default name for the gravity model.
|
||||
*
|
||||
* This is the value of the environment variable
|
||||
* GEOGRAPHICLIB_GRAVITY_NAME, if set; otherwise, it is "egm96". The
|
||||
* GravityModel class does not use this function; it is just provided as a
|
||||
* convenience for a calling program when constructing a GravityModel
|
||||
* object.
|
||||
**********************************************************************/
|
||||
static std::string DefaultGravityName();
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#endif // GEOGRAPHICLIB_GRAVITYMODEL_HPP
|
||||
+584
@@ -0,0 +1,584 @@
|
||||
/**
|
||||
* \file Intersect.hpp
|
||||
* \brief Header for GeographicLib::Intersect class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2023-2024) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_INTERSECT_HPP)
|
||||
#define GEOGRAPHICLIB_INTERSECT_HPP 1
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <GeographicLib/Math.hpp>
|
||||
#include <GeographicLib/Geodesic.hpp>
|
||||
#include <GeographicLib/GeodesicLine.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief %Geodesic intersections
|
||||
*
|
||||
* Find the intersections of two geodesics \e X and \e Y. Four calling
|
||||
* sequences are supported.
|
||||
* - The geodesics are defined by a position (latitude and longitude) and an
|
||||
* azimuth. In this case the \e closest intersection is found.
|
||||
* - The geodesics are defined by two endpoints. The intersection of the two
|
||||
* segments is found. If they don't intersect, then the closest
|
||||
* intersection is returned.
|
||||
* - The geodesics are defined as an intersection point, a single position
|
||||
* and two azimuths. In this case, the next closest intersection is found.
|
||||
* - The geodesics are defined as in the first case and all intersection
|
||||
* within a specified distance are returned.
|
||||
* .
|
||||
* In all cases the position of the intersection is given by the signed
|
||||
* displacements \e x and \e y along the geodesics from the starting point
|
||||
* (the first point in the case of a geodesic segment). The closest
|
||||
* itersection is defined as the one that minimizes the L1 distance,
|
||||
* Intersect::Dist([<i>x</i>, <i>y</i>) = |<i>x</i>| + |<i>y</i>|.
|
||||
*
|
||||
* The routines also optionally return a coincidence indicator \e c. This is
|
||||
* typically 0. However if the geodesics lie on top of one another at the
|
||||
* point of intersection, then \e c is set to +1, if they are parallel, and
|
||||
* −1, if they are antiparallel.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Intersect.cpp
|
||||
*
|
||||
* <a href="IntersectTool.1.html">IntersectTool</a> is a command-line utility
|
||||
* providing access to the functionality of this class.
|
||||
*
|
||||
* This solution for intersections is described in
|
||||
* - C. F. F. Karney,<br>
|
||||
* <a href="https://doi.org/10.1061/JSUED2.SUENG-1483">
|
||||
* Geodesic intersections</a>,
|
||||
* J. Surveying Eng. <b>150</b>(3), 04024005:1--9 (2024);
|
||||
* preprint
|
||||
* <a href="https://arxiv.org/abs/2308.00495">arxiv:2308.00495</a>.
|
||||
* .
|
||||
* It is based on the work of
|
||||
* - S. Baseldga and J. C. Martinez-Llario,
|
||||
* <a href="https://doi.org/10.1007/s11200-017-1020-z">
|
||||
* Intersection and point-to-line solutions for geodesics
|
||||
* on the ellipsoid</a>,
|
||||
* Stud. Geophys. Geod. <b>62</b>, 353--363 (2018);
|
||||
* DOI: <a href="https://doi.org/10.1007/s11200-017-1020-z">
|
||||
* 10.1007/s11200-017-1020-z</a>.
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT Intersect {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
public:
|
||||
/**
|
||||
* The type used to hold the two displacement along the geodesics. This is
|
||||
* just a std::pair with \e x = \e first and \e y = \e second.
|
||||
**********************************************************************/
|
||||
typedef std::pair<Math::real, Math::real> Point;
|
||||
/**
|
||||
* The minimum capabilities for GeodesicLine objects which are passed to
|
||||
* this class.
|
||||
**********************************************************************/
|
||||
static const unsigned LineCaps = Geodesic::LATITUDE | Geodesic::LONGITUDE |
|
||||
Geodesic::AZIMUTH | Geodesic::REDUCEDLENGTH | Geodesic::GEODESICSCALE |
|
||||
Geodesic::DISTANCE_IN;
|
||||
private:
|
||||
static const int numit_ = 100;
|
||||
const Geodesic _geod;
|
||||
real _a, _f, // equatorial radius, flattening
|
||||
_rR, // authalic radius
|
||||
_d, // pi*_rR
|
||||
_eps, // criterion for intersection + coincidence
|
||||
_tol, // convergence for Newton in Solve1
|
||||
_delta, // for equality tests, safety margin for tiling
|
||||
_t1, // min distance between intersections
|
||||
_t2, // furthest dist to closest intersection
|
||||
_t3, // 1/2 furthest min dist to next intersection
|
||||
_t4, // capture radius for spherical sol in Solve0
|
||||
_t5, // longest shortest geodesic
|
||||
_d1, // tile spacing for Closest
|
||||
_d2, // tile spacing for Next
|
||||
_d3; // tile spacing for All
|
||||
// The L1 distance
|
||||
static Math::real d1(Math::real x, Math::real y)
|
||||
{ using std::fabs; return fabs(x) + fabs(y); }
|
||||
// An internal version of Point with a little more functionality
|
||||
class XPoint {
|
||||
public:
|
||||
real x, y;
|
||||
int c;
|
||||
XPoint(Math::real x, Math::real y, int c = 0)
|
||||
: x(x), y(y), c(c)
|
||||
{}
|
||||
XPoint()
|
||||
: x(Math::NaN()), y(Math::NaN()), c(0)
|
||||
{}
|
||||
XPoint(const Point& p)
|
||||
: x(p.first), y(p.second), c(0)
|
||||
{}
|
||||
XPoint& operator+=(const XPoint& p) {
|
||||
x += p.x; y += p.y;
|
||||
if (p.c) c = p.c; // pass along a nonzero c from either operand
|
||||
return *this;
|
||||
}
|
||||
XPoint operator+(const XPoint& p) const {
|
||||
XPoint t = *this; t += p; return t;
|
||||
}
|
||||
Math::real Dist() const { return d1(x, y); }
|
||||
Math::real Dist(const XPoint& p) const { return d1(x - p.x, y - p.y); }
|
||||
Point data() const { return Point(x, y); }
|
||||
};
|
||||
// Comparing XPoints for insertions into sets, but ensure that close
|
||||
// XPoints test equal.
|
||||
class GEOGRAPHICLIB_EXPORT SetComp {
|
||||
private:
|
||||
const real _delta;
|
||||
public:
|
||||
SetComp(Math::real delta) : _delta(delta) {}
|
||||
bool eq(const XPoint& p, const XPoint& q) const {
|
||||
return d1(p.x - q.x, p.y - q.y) <= _delta;
|
||||
}
|
||||
bool operator()(const XPoint& p, const XPoint& q) const {
|
||||
return !eq(p, q) && ( p.x != q.x ? p.x < q.x : p.y < q.y );
|
||||
}
|
||||
};
|
||||
SetComp _comp;
|
||||
// For ranking XPoints by closeness
|
||||
class RankPoint {
|
||||
private:
|
||||
const real _x, _y;
|
||||
public:
|
||||
RankPoint(const Point& p0) : _x(p0.first), _y(p0.second) {}
|
||||
RankPoint(const XPoint& p0) : _x(p0.x), _y(p0.y) {}
|
||||
bool operator()(const XPoint& p, const XPoint& q) const {
|
||||
real dp = d1(p.x - _x, p.y - _y),
|
||||
dq = d1(q.x - _x, q.y - _y);
|
||||
return dp != dq ? (dp < dq) :
|
||||
(p.x != q.x ? (p.x < q.x) : (p.y < q.y));
|
||||
}
|
||||
};
|
||||
// The spherical solution
|
||||
XPoint Spherical(const GeodesicLine& lineX, const GeodesicLine& lineY,
|
||||
const XPoint& p) const;
|
||||
// The basic algorithm
|
||||
XPoint Basic(const GeodesicLine& lineX, const GeodesicLine& lineY,
|
||||
const XPoint& p0) const;
|
||||
// The closest intersecton
|
||||
XPoint ClosestInt(const GeodesicLine& lineX, const GeodesicLine& lineY,
|
||||
const XPoint& p0) const;
|
||||
// The next intersecton
|
||||
XPoint NextInt(const GeodesicLine& lineX, const GeodesicLine& lineY) const;
|
||||
// Segment intersecton
|
||||
XPoint SegmentInt(const GeodesicLine& lineX, const GeodesicLine& lineY,
|
||||
int& segmode) const;
|
||||
// All intersectons
|
||||
std::vector<XPoint>
|
||||
AllInt0(const GeodesicLine& lineX, const GeodesicLine& lineY,
|
||||
Math::real maxdist, const XPoint& p0) const;
|
||||
std::vector<Point>
|
||||
AllInternal(const GeodesicLine& lineX, const GeodesicLine& lineY,
|
||||
Math::real maxdist, const Point& p0,
|
||||
std::vector<int>& c, bool cp) const;
|
||||
// Find {semi-,}conjugate point which is close to s3. Optional m12, M12,
|
||||
// M21 use {semi-,}conjugacy relative to point 2
|
||||
Math::real ConjugateDist(const GeodesicLine& line, Math::real s3, bool semi,
|
||||
Math::real m12 = 0, Math::real M12 = 1,
|
||||
Math::real M21 = 1) const;
|
||||
Math::real distpolar(Math::real lat1, Math::real* lat2 = nullptr) const;
|
||||
Math::real polarb(Math::real* lata = nullptr, Math::real* latb = nullptr)
|
||||
const;
|
||||
Math::real conjdist(Math::real azi, Math::real* ds = nullptr,
|
||||
Math::real* sp = nullptr, Math::real* sm = nullptr)
|
||||
const;
|
||||
Math::real distoblique(Math::real* azi = nullptr, Math::real* sp = nullptr,
|
||||
Math::real* sm = nullptr) const;
|
||||
// p is intersection point on coincident lines orientation = c; p0 is
|
||||
// origin point. Change p to center point wrt p0, i.e, abs((p-p0)_x) =
|
||||
// abs((p-p0)_y)
|
||||
static XPoint fixcoincident(const XPoint& p0, const XPoint& p);
|
||||
static XPoint fixcoincident(const XPoint& p0, const XPoint& p, int c);
|
||||
static XPoint fixsegment(Math::real sx, Math::real sy, const XPoint& p);
|
||||
static int segmentmode(Math::real sx, Math::real sy, const XPoint& p) {
|
||||
return (p.x < 0 ? -1 : p.x <= sx ? 0 : 1) * 3
|
||||
+ (p.y < 0 ? -1 : p.y <= sy ? 0 : 1);
|
||||
}
|
||||
mutable long long _cnt0, _cnt1, _cnt2, _cnt3, _cnt4;
|
||||
public:
|
||||
/** \name Constructor
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Constructor for an ellipsoid with
|
||||
*
|
||||
* @param[in] geod a Geodesic object. This sets the parameters \e a and \e
|
||||
* f for the ellipsoid.
|
||||
* @exception GeographicErr if the eccentricity of the elliposdoid is too
|
||||
* large.
|
||||
*
|
||||
* \note This class has been validated for -1/4 ≤ \e f ≤ 1/5. It may
|
||||
* give satisfactory results slightly outside this range; however
|
||||
* sufficient far outside the range, some internal checks will fail and an
|
||||
* exception thrown.
|
||||
*
|
||||
* \note If |<i>f</i>| > 1/50, then the Geodesic object should be
|
||||
* constructed with \e exact = true.
|
||||
**********************************************************************/
|
||||
Intersect(const Geodesic& geod);
|
||||
///@}
|
||||
|
||||
/** \name Finding intersections
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Find the closest intersection point, with each geodesic specified by
|
||||
* position and azimuth.
|
||||
*
|
||||
* @param[in] latX latitude of starting point for geodesic \e X (degrees).
|
||||
* @param[in] lonX longitude of starting point for geodesic \e X (degrees).
|
||||
* @param[in] aziX azimuth at starting point for geodesic \e X (degrees).
|
||||
* @param[in] latY latitude of starting point for geodesic \e Y (degrees).
|
||||
* @param[in] lonY longitude of starting point for geodesic \e Y (degrees).
|
||||
* @param[in] aziY azimuth at starting point for geodesic \e Y (degrees).
|
||||
* @param[in] p0 an optional offset for the starting points (meters),
|
||||
* default = [0,0].
|
||||
* @param[out] c optional pointer to an integer coincidence indicator.
|
||||
* @return \e p the intersection point closest to \e p0.
|
||||
*
|
||||
* The returned intersection minimizes Intersect::Dist(\e p, \e p0).
|
||||
**********************************************************************/
|
||||
Point Closest(Math::real latX, Math::real lonX, Math::real aziX,
|
||||
Math::real latY, Math::real lonY, Math::real aziY,
|
||||
const Point& p0 = Point(0, 0), int* c = nullptr) const;
|
||||
/**
|
||||
* Find the closest intersection point, with each geodesic given as a
|
||||
* GeodesicLine.
|
||||
*
|
||||
* @param[in] lineX geodesic \e X.
|
||||
* @param[in] lineY geodesic \e Y.
|
||||
* @param[in] p0 an optional offset for the starting points (meters),
|
||||
* default = [0,0].
|
||||
* @param[out] c optional pointer to an integer coincidence indicator.
|
||||
* @return \e p the intersection point closest to \e p0.
|
||||
*
|
||||
* The returned intersection minimizes Intersect::Dist(\e p, \e p0).
|
||||
*
|
||||
* \note \e lineX and \e lineY should be created with minimum capabilities
|
||||
* Intersect::LineCaps. The methods for creating a GeodesicLine include
|
||||
* all these capabilities by default.
|
||||
**********************************************************************/
|
||||
Point Closest(const GeodesicLine& lineX, const GeodesicLine& lineY,
|
||||
const Point& p0 = Point(0, 0), int* c = nullptr) const;
|
||||
/**
|
||||
* Find the intersection of two geodesic segments defined by their
|
||||
* endpoints.
|
||||
*
|
||||
* @param[in] latX1 latitude of first point for segment \e X (degrees).
|
||||
* @param[in] lonX1 longitude of first point for segment \e X (degrees).
|
||||
* @param[in] latX2 latitude of second point for segment \e X (degrees).
|
||||
* @param[in] lonX2 longitude of second point for segment \e X (degrees).
|
||||
* @param[in] latY1 latitude of first point for segment \e Y (degrees).
|
||||
* @param[in] lonY1 longitude of first point for segment \e Y (degrees).
|
||||
* @param[in] latY2 latitude of second point for segment \e Y (degrees).
|
||||
* @param[in] lonY2 longitude of second point for segment \e Y (degrees).
|
||||
* @param[out] segmode an indicator equal to zero if the segments
|
||||
* intersect (see below).
|
||||
* @param[out] c optional pointer to an integer coincidence indicator.
|
||||
* @return \e p the intersection point if the segments intersect, otherwise
|
||||
* the intersection point closest to the midpoints of the two
|
||||
* segments.
|
||||
*
|
||||
* \warning The results are only well defined if there's a \e unique
|
||||
* shortest geodesic between the endpoints of the two segments.
|
||||
*
|
||||
* \e segmode codes up information about the closest intersection in the
|
||||
* case where the segments intersect. Let <i>x</i><sub>12</sub> be the
|
||||
* length of the segment \e X and \e x = <i>p</i>.first, the position of
|
||||
* the intersection on segment \e X. Define
|
||||
* - \e k<sub><i>x</i></sub> = −1, if \e x < 0,
|
||||
* - \e k<sub><i>x</i></sub> = 0,
|
||||
* if 0 ≤ \e x ≤ <i>x</i><sub>12</sub>,
|
||||
* - \e k<sub><i>x</i></sub> = 1, if <i>x</i><sub>12</sub> < \e x.
|
||||
* .
|
||||
* and similarly for segment \e Y. Then
|
||||
* \e segmode = 3 \e k<sub><i>x</i></sub> + \e k<sub><i>y</i></sub>.
|
||||
**********************************************************************/
|
||||
Point Segment(Math::real latX1, Math::real lonX1,
|
||||
Math::real latX2, Math::real lonX2,
|
||||
Math::real latY1, Math::real lonY1,
|
||||
Math::real latY2, Math::real lonY2,
|
||||
int& segmode, int* c = nullptr) const;
|
||||
/**
|
||||
* Find the intersection of two geodesic segments each defined by a
|
||||
* GeodesicLine.
|
||||
*
|
||||
* @param[in] lineX segment \e X.
|
||||
* @param[in] lineY segment \e Y.
|
||||
* @param[out] segmode an indicator equal to zero if the segments
|
||||
* intersect (see below).
|
||||
* @param[out] c optional pointer to an integer coincidence indicator.
|
||||
* @return \e p the intersection point if the segments intersect, otherwise
|
||||
* the intersection point closest to the midpoints of the two
|
||||
* segments.
|
||||
*
|
||||
* \warning \e lineX and \e lineY must represent shortest geodesics, e.g.,
|
||||
* they can be created by Geodesic::InverseLine. The results are only well
|
||||
* defined if there's a \e unique shortest geodesic between the endpoints
|
||||
* of the two segments.
|
||||
*
|
||||
* \note \e lineX and \e lineY should be created with minimum capabilities
|
||||
* Intersect::LineCaps. The methods for creating a GeodesicLine include
|
||||
* all these capabilities by default.
|
||||
*
|
||||
* See previous definition of Intersect::Segment for more information on \e
|
||||
* segmode.
|
||||
**********************************************************************/
|
||||
Point Segment(const GeodesicLine& lineX, const GeodesicLine& lineY,
|
||||
int& segmode, int* c = nullptr) const;
|
||||
/**
|
||||
* Find the next closest intersection point to a given intersection,
|
||||
* specified by position and two azimuths.
|
||||
*
|
||||
* @param[in] latX latitude of starting points for geodesics \e X and \e Y
|
||||
* (degrees).
|
||||
* @param[in] lonX longitude of starting points for geodesics \e X and \e Y
|
||||
* (degrees).
|
||||
* @param[in] aziX azimuth at starting point for geodesic \e X (degrees).
|
||||
* @param[in] aziY azimuth at starting point for geodesic \e Y (degrees).
|
||||
* @param[out] c optional pointer to an integer coincidence indicator.
|
||||
* @return \e p the next closest intersection point.
|
||||
*
|
||||
* The returned intersection minimizes Intersect::Dist(\e p) (excluding \e
|
||||
* p = [0,0]).
|
||||
*
|
||||
* \note Equidistant closest intersections are surprisingly common. If
|
||||
* this may be a problem, use Intersect::All with a sufficiently large \e
|
||||
* maxdist to capture close intersections.
|
||||
**********************************************************************/
|
||||
Point Next(Math::real latX, Math::real lonX,
|
||||
Math::real aziX, Math::real aziY, int* c = nullptr) const;
|
||||
/**
|
||||
* Find the next closest intersection point to a given intersection,
|
||||
* with each geodesic specified a GeodesicLine.
|
||||
*
|
||||
* @param[in] lineX geodesic \e X.
|
||||
* @param[in] lineY geodesic \e Y.
|
||||
* @param[out] c optional pointer to an integer coincidence indicator.
|
||||
* @return \e p the next closest intersection point.
|
||||
*
|
||||
* \warning \e lineX and \e lineY must both have the same starting point,
|
||||
* i.e., the distance between [<i>lineX</i>.Latitude(),
|
||||
* <i>lineX</i>.Longitude()] and [<i>lineY</i>.Latitude(),
|
||||
* <i>lineY</i>.Longitude()] must be zero.
|
||||
*
|
||||
* \note \e lineX and \e lineY should be created with minimum capabilities
|
||||
* Intersect::LineCaps. The methods for creating a GeodesicLine include
|
||||
* all these capabilities by default.
|
||||
*
|
||||
* \note Equidistant closest intersections are surprisingly common. If
|
||||
* this may be a problem, use Intersect::All with a sufficiently large \e
|
||||
* maxdist to capture close intersections.
|
||||
**********************************************************************/
|
||||
Point Next(const GeodesicLine& lineX, const GeodesicLine& lineY,
|
||||
int* c = nullptr) const;
|
||||
///@}
|
||||
|
||||
/** \name Finding all intersections
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Find all intersections within a certain distance, with each geodesic
|
||||
* specified by position and azimuth.
|
||||
*
|
||||
* @param[in] latX latitude of starting point for geodesic \e X (degrees).
|
||||
* @param[in] lonX longitude of starting point for geodesic \e X (degrees).
|
||||
* @param[in] aziX azimuth at starting point for geodesic \e X (degrees).
|
||||
* @param[in] latY latitude of starting point for geodesic \e Y (degrees).
|
||||
* @param[in] lonY longitude of starting point for geodesic \e Y (degrees).
|
||||
* @param[in] aziY azimuth at starting point for geodesic \e Y (degrees).
|
||||
* @param[in] maxdist the maximum distance for the returned intersections
|
||||
* (meters).
|
||||
* @param[out] c vector of coincidences.
|
||||
* @param[in] p0 an optional offset for the starting points (meters),
|
||||
* default = [0,0].
|
||||
* @return \e plist a vector for the intersections closest to \e p0.
|
||||
*
|
||||
* Each intersection point satisfies Intersect::Dist(\e p, \e p0) ≤ \e
|
||||
* maxdist. The vector of returned intersections is sorted on the distance
|
||||
* from \e p0.
|
||||
**********************************************************************/
|
||||
std::vector<Point> All(Math::real latX, Math::real lonX, Math::real aziX,
|
||||
Math::real latY, Math::real lonY, Math::real aziY,
|
||||
Math::real maxdist, std::vector<int>& c,
|
||||
const Point& p0 = Point(0, 0))
|
||||
const;
|
||||
/**
|
||||
* Find all intersections within a certain distance, with each geodesic
|
||||
* specified by position and azimuth. Don't return vector of
|
||||
* coincidences.
|
||||
*
|
||||
* @param[in] latX latitude of starting point for geodesic \e X (degrees).
|
||||
* @param[in] lonX longitude of starting point for geodesic \e X (degrees).
|
||||
* @param[in] aziX azimuth at starting point for geodesic \e X (degrees).
|
||||
* @param[in] latY latitude of starting point for geodesic \e Y (degrees).
|
||||
* @param[in] lonY longitude of starting point for geodesic \e Y (degrees).
|
||||
* @param[in] aziY azimuth at starting point for geodesic \e Y (degrees).
|
||||
* @param[in] maxdist the maximum distance for the returned intersections
|
||||
* (meters).
|
||||
* @param[in] p0 an optional offset for the starting points (meters),
|
||||
* default = [0,0].
|
||||
* @return \e plist a vector for the intersections closest to \e p0.
|
||||
*
|
||||
* Each intersection point satisfies Intersect::Dist(\e p, \e p0) ≤ \e
|
||||
* maxdist. The vector of returned intersections is sorted on the distance
|
||||
* from \e p0.
|
||||
**********************************************************************/
|
||||
std::vector<Point> All(Math::real latX, Math::real lonX, Math::real aziX,
|
||||
Math::real latY, Math::real lonY, Math::real aziY,
|
||||
Math::real maxdist, const Point& p0 = Point(0, 0))
|
||||
const;
|
||||
/**
|
||||
* Find all intersections within a certain distance, with each geodesic
|
||||
* specified by a GeodesicLine.
|
||||
*
|
||||
* @param[in] lineX geodesic \e X.
|
||||
* @param[in] lineY geodesic \e Y.
|
||||
* @param[in] maxdist the maximum distance for the returned intersections
|
||||
* (meters).
|
||||
* @param[out] c vector of coincidences.
|
||||
* @param[in] p0 an optional offset for the starting points (meters),
|
||||
* default = [0,0].
|
||||
* @return \e plist a vector for the intersections closest to \e p0.
|
||||
*
|
||||
* Each intersection point satisfies Intersect::Dist(\e p, \e p0) ≤ \e
|
||||
* maxdist. The vector of returned intersections is sorted on the distance
|
||||
* from \e p0.
|
||||
*
|
||||
* \note \e lineX and \e lineY should be created with minimum capabilities
|
||||
* Intersect::LineCaps. The methods for creating a GeodesicLine include
|
||||
* all these capabilities by default.
|
||||
**********************************************************************/
|
||||
std::vector<Point> All(const GeodesicLine& lineX, const GeodesicLine& lineY,
|
||||
Math::real maxdist, std::vector<int>& c,
|
||||
const Point& p0 = Point(0, 0))
|
||||
const;
|
||||
/**
|
||||
* Find all intersections within a certain distance, with each geodesic
|
||||
* specified by a GeodesicLine. Don't return vector or coincidences.
|
||||
*
|
||||
* @param[in] lineX geodesic \e X.
|
||||
* @param[in] lineY geodesic \e Y.
|
||||
* @param[in] maxdist the maximum distance for the returned intersections
|
||||
* (meters).
|
||||
* @param[in] p0 an optional offset for the starting points (meters),
|
||||
* default = [0,0].
|
||||
* @return \e plist a vector for the intersections closest to \e p0.
|
||||
*
|
||||
* Each intersection point satisfies Intersect::Dist(\e p, \e p0) ≤ \e
|
||||
* maxdist. The vector of returned intersections is sorted on the distance
|
||||
* from \e p0.
|
||||
*
|
||||
* \note \e lineX and \e lineY should be created with minimum capabilities
|
||||
* Intersect::LineCaps. The methods for creating a GeodesicLine include
|
||||
* all these capabilities by default.
|
||||
**********************************************************************/
|
||||
std::vector<Point> All(const GeodesicLine& lineX, const GeodesicLine& lineY,
|
||||
Math::real maxdist, const Point& p0 = Point(0, 0))
|
||||
const;
|
||||
///@}
|
||||
|
||||
/** \name Diagnostic counters
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return the cumulative number of invocations of **h**.
|
||||
*
|
||||
* This is a count of the number of times the spherical triangle needs to
|
||||
* be solved. Each involves a call to Geodesic::Inverse and this is a good
|
||||
* metric for the overall cost. This counter is set to zero by the
|
||||
* constructor.
|
||||
*
|
||||
* \warning The counter is a mutable variable and so is not thread safe.
|
||||
**********************************************************************/
|
||||
long long NumInverse() const { return _cnt0; }
|
||||
/**
|
||||
* @return the cumulative number of invocations of **b**.
|
||||
*
|
||||
* This is a count of the number of invocations of the basic algorithm,
|
||||
* which is used by all the intersection methods. This counter is set to
|
||||
* zero by the constructor.
|
||||
*
|
||||
* \warning The counter is a mutable variable and so is not thread safe.
|
||||
**********************************************************************/
|
||||
long long NumBasic() const { return _cnt1; }
|
||||
/**
|
||||
* @return the number of times intersection point was changed in
|
||||
* Intersect::Closest and Intersect::Next.
|
||||
*
|
||||
* If this counter is incremented by just 1 in Intersect::Closest, then the
|
||||
* initial result of the basic algorithm was eventually accepted. This
|
||||
* counter is set to zero by the constructor.
|
||||
*
|
||||
* \note This counter is also incremented by Intersect::Segment, which
|
||||
* calls Intersect::Closest.
|
||||
*
|
||||
* \warning The counter is a mutable variable and so is not thread safe.
|
||||
**********************************************************************/
|
||||
long long NumChange() const { return _cnt2; }
|
||||
/**
|
||||
* @return the number of times a corner point is checked in
|
||||
* Intersect::Segment.
|
||||
*
|
||||
* This counter is set to zero by the constructor.
|
||||
*
|
||||
* \warning The counter is a mutable variable and so is not thread safe.
|
||||
**********************************************************************/
|
||||
long long NumCorner() const { return _cnt3; }
|
||||
/**
|
||||
* @return the number of times a corner point is returned by
|
||||
* Intersect::Segment.
|
||||
*
|
||||
* This counter is set to zero by the constructor.
|
||||
*
|
||||
* \note A conjecture is that a corner point never results in an
|
||||
* intersection that overrides the intersection closest to the midpoints of
|
||||
* the segments; i.e., NumCorner() always returns 0.
|
||||
*
|
||||
* \warning The counter is a mutable variable and so is not thread safe.
|
||||
**********************************************************************/
|
||||
long long NumOverride() const { return _cnt4; }
|
||||
///@}
|
||||
|
||||
/** \name Insepctor function
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return \e geod the Geodesic object used in the constructor.
|
||||
*
|
||||
* This can be used to query Geodesic::EquatorialRadius(),
|
||||
* Geodesic::Flattening(), Geodesic::Exact(), and
|
||||
* Geodesic::EllipsoidArea().
|
||||
**********************************************************************/
|
||||
const Geodesic& GeodesicObject() const { return _geod; }
|
||||
///@}
|
||||
|
||||
/**
|
||||
* The L1 distance.
|
||||
*
|
||||
* @param[in] p the position along geodesics \e X and \e Y.
|
||||
* @param[in] p0 [optional] the reference position, default = [0, 0].
|
||||
|
||||
* @return the L1 distance of \e p from \e p0, i.e.,
|
||||
* |<i>p</i><sub><i>x</i></sub> − <i>p0</i><sub><i>x</i></sub>| +
|
||||
* |<i>p</i><sub><i>y</i></sub> − <i>p0</i><sub><i>y</i></sub>|.
|
||||
**********************************************************************/
|
||||
static Math::real Dist(const Point& p, const Point& p0 = Point(0, 0)) {
|
||||
using std::fabs;
|
||||
return fabs(p.first - p0.first) + fabs(p.second - p0.second);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_INTERSECT_HPP
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* \file LambertConformalConic.hpp
|
||||
* \brief Header for GeographicLib::LambertConformalConic class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2010-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_LAMBERTCONFORMALCONIC_HPP)
|
||||
#define GEOGRAPHICLIB_LAMBERTCONFORMALCONIC_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Lambert conformal conic projection
|
||||
*
|
||||
* Implementation taken from the report,
|
||||
* - J. P. Snyder,
|
||||
* <a href="https://pubs.usgs.gov/publication/pp1395"> Map Projections: A
|
||||
* Working Manual</a>, USGS Professional Paper 1395 (1987),
|
||||
* pp. 107--109.
|
||||
*
|
||||
* This is a implementation of the equations in Snyder except that divided
|
||||
* differences have been used to transform the expressions into ones which
|
||||
* may be evaluated accurately and that Newton's method is used to invert the
|
||||
* projection. In this implementation, the projection correctly becomes the
|
||||
* Mercator projection or the polar stereographic projection when the
|
||||
* standard latitude is the equator or a pole. The accuracy of the
|
||||
* projections is about 10 nm (10 nanometers).
|
||||
*
|
||||
* The ellipsoid parameters, the standard parallels, and the scale on the
|
||||
* standard parallels are set in the constructor. Internally, the case with
|
||||
* two standard parallels is converted into a single standard parallel, the
|
||||
* latitude of tangency (also the latitude of minimum scale), with a scale
|
||||
* specified on this parallel. This latitude is also used as the latitude of
|
||||
* origin which is returned by LambertConformalConic::OriginLatitude. The
|
||||
* scale on the latitude of origin is given by
|
||||
* LambertConformalConic::CentralScale. The case with two distinct standard
|
||||
* parallels where one is a pole is singular and is disallowed. The central
|
||||
* meridian (which is a trivial shift of the longitude) is specified as the
|
||||
* \e lon0 argument of the LambertConformalConic::Forward and
|
||||
* LambertConformalConic::Reverse functions.
|
||||
*
|
||||
* This class also returns the meridian convergence \e gamma and scale \e k.
|
||||
* The meridian convergence is the bearing of grid north (the \e y axis)
|
||||
* measured clockwise from true north.
|
||||
*
|
||||
* There is no provision in this
|
||||
* class for specifying a false easting or false northing or a different
|
||||
* latitude of origin. However these are can be simply included by the
|
||||
* calling function. For example the Pennsylvania South state coordinate
|
||||
* system (<a href="https://www.spatialreference.org/ref/epsg/3364/">
|
||||
* EPSG:3364</a>) is obtained by:
|
||||
* \include example-LambertConformalConic.cpp
|
||||
*
|
||||
* <a href="ConicProj.1.html">ConicProj</a> is a command-line utility
|
||||
* providing access to the functionality of LambertConformalConic and
|
||||
* AlbersEqualArea.
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT LambertConformalConic {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
real eps_, epsx_, ahypover_;
|
||||
real _a, _f, _fm, _e2, _es;
|
||||
real _sign, _n, _nc, _t0nm1, _scale, _lat0, _k0;
|
||||
real _scbet0, _tchi0, _scchi0, _psi0, _nrho0, _drhomax;
|
||||
static const int numit_ = 5;
|
||||
static real hyp(real x) {
|
||||
using std::hypot;
|
||||
return hypot(real(1), x);
|
||||
}
|
||||
// Divided differences
|
||||
// Definition: Df(x,y) = (f(x)-f(y))/(x-y)
|
||||
// See:
|
||||
// W. M. Kahan and R. J. Fateman,
|
||||
// Symbolic computation of divided differences,
|
||||
// SIGSAM Bull. 33(2), 7-28 (1999)
|
||||
// https://doi.org/10.1145/334714.334716
|
||||
// http://www.cs.berkeley.edu/~fateman/papers/divdiff.pdf
|
||||
//
|
||||
// General rules
|
||||
// h(x) = f(g(x)): Dh(x,y) = Df(g(x),g(y))*Dg(x,y)
|
||||
// h(x) = f(x)*g(x):
|
||||
// Dh(x,y) = Df(x,y)*g(x) + Dg(x,y)*f(y)
|
||||
// = Df(x,y)*g(y) + Dg(x,y)*f(x)
|
||||
// = Df(x,y)*(g(x)+g(y))/2 + Dg(x,y)*(f(x)+f(y))/2
|
||||
//
|
||||
// hyp(x) = sqrt(1+x^2): Dhyp(x,y) = (x+y)/(hyp(x)+hyp(y))
|
||||
static real Dhyp(real x, real y, real hx, real hy)
|
||||
// hx = hyp(x)
|
||||
{ return (x + y) / (hx + hy); }
|
||||
// sn(x) = x/sqrt(1+x^2): Dsn(x,y) = (x+y)/((sn(x)+sn(y))*(1+x^2)*(1+y^2))
|
||||
static real Dsn(real x, real y, real sx, real sy) {
|
||||
// sx = x/hyp(x)
|
||||
real t = x * y;
|
||||
return t > 0 ? (x + y) * Math::sq( (sx * sy)/t ) / (sx + sy) :
|
||||
(x - y != 0 ? (sx - sy) / (x - y) : 1);
|
||||
}
|
||||
// Dlog1p(x,y) = log1p((x-y)/(1+y))/(x-y)
|
||||
static real Dlog1p(real x, real y) {
|
||||
using std::log1p;
|
||||
real t = x - y; if (t < 0) { t = -t; y = x; }
|
||||
return t != 0 ? log1p(t / (1 + y)) / t : 1 / (1 + x);
|
||||
}
|
||||
// Dexp(x,y) = exp((x+y)/2) * 2*sinh((x-y)/2)/(x-y)
|
||||
static real Dexp(real x, real y) {
|
||||
using std::sinh, std::exp;
|
||||
real t = (x - y)/2;
|
||||
return (t != 0 ? sinh(t)/t : 1) * exp((x + y)/2);
|
||||
}
|
||||
// Dsinh(x,y) = 2*sinh((x-y)/2)/(x-y) * cosh((x+y)/2)
|
||||
// cosh((x+y)/2) = (c+sinh(x)*sinh(y)/c)/2
|
||||
// c=sqrt((1+cosh(x))*(1+cosh(y)))
|
||||
// cosh((x+y)/2) = sqrt( (sinh(x)*sinh(y) + cosh(x)*cosh(y) + 1)/2 )
|
||||
static real Dsinh(real x, real y, real sx, real sy, real cx, real cy)
|
||||
// sx = sinh(x), cx = cosh(x)
|
||||
{
|
||||
// real t = (x - y)/2, c = sqrt((1 + cx) * (1 + cy));
|
||||
// return (t ? sinh(t)/t : real(1)) * (c + sx * sy / c) /2;
|
||||
using std::sinh, std::sqrt;
|
||||
real t = (x - y)/2;
|
||||
return (t != 0 ? sinh(t)/t : 1) * sqrt((sx * sy + cx * cy + 1) /2);
|
||||
}
|
||||
// Dasinh(x,y) = asinh((x-y)*(x+y)/(x*sqrt(1+y^2)+y*sqrt(1+x^2)))/(x-y)
|
||||
// = asinh((x*sqrt(1+y^2)-y*sqrt(1+x^2)))/(x-y)
|
||||
static real Dasinh(real x, real y, real hx, real hy) {
|
||||
// hx = hyp(x)
|
||||
using std::asinh;
|
||||
real t = x - y;
|
||||
return t != 0 ?
|
||||
asinh(x*y > 0 ? t * (x + y) / (x*hy + y*hx) : x*hy - y*hx) / t :
|
||||
1 / hx;
|
||||
}
|
||||
// Deatanhe(x,y) = eatanhe((x-y)/(1-e^2*x*y))/(x-y)
|
||||
real Deatanhe(real x, real y) const {
|
||||
real t = x - y, d = 1 - _e2 * x * y;
|
||||
return t != 0 ? Math::eatanhe(t / d, _es) / t : _e2 / d;
|
||||
}
|
||||
void Init(real sphi1, real cphi1, real sphi2, real cphi2, real k1);
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor with a single standard parallel.
|
||||
*
|
||||
* @param[in] a equatorial radius of ellipsoid (meters).
|
||||
* @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
|
||||
* Negative \e f gives a prolate ellipsoid.
|
||||
* @param[in] stdlat standard parallel (degrees), the circle of tangency.
|
||||
* @param[in] k0 scale on the standard parallel.
|
||||
* @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k0 is
|
||||
* not positive.
|
||||
* @exception GeographicErr if \e stdlat is not in [−90°,
|
||||
* 90°].
|
||||
**********************************************************************/
|
||||
LambertConformalConic(real a, real f, real stdlat, real k0);
|
||||
|
||||
/**
|
||||
* Constructor with two standard parallels.
|
||||
*
|
||||
* @param[in] a equatorial radius of ellipsoid (meters).
|
||||
* @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
|
||||
* Negative \e f gives a prolate ellipsoid.
|
||||
* @param[in] stdlat1 first standard parallel (degrees).
|
||||
* @param[in] stdlat2 second standard parallel (degrees).
|
||||
* @param[in] k1 scale on the standard parallels.
|
||||
* @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k1 is
|
||||
* not positive.
|
||||
* @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in
|
||||
* [−90°, 90°], or if either \e stdlat1 or \e
|
||||
* stdlat2 is a pole and \e stdlat1 is not equal \e stdlat2.
|
||||
**********************************************************************/
|
||||
LambertConformalConic(real a, real f, real stdlat1, real stdlat2, real k1);
|
||||
|
||||
/**
|
||||
* Constructor with two standard parallels specified by sines and cosines.
|
||||
*
|
||||
* @param[in] a equatorial radius of ellipsoid (meters).
|
||||
* @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
|
||||
* Negative \e f gives a prolate ellipsoid.
|
||||
* @param[in] sinlat1 sine of first standard parallel.
|
||||
* @param[in] coslat1 cosine of first standard parallel.
|
||||
* @param[in] sinlat2 sine of second standard parallel.
|
||||
* @param[in] coslat2 cosine of second standard parallel.
|
||||
* @param[in] k1 scale on the standard parallels.
|
||||
* @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k1 is
|
||||
* not positive.
|
||||
* @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in
|
||||
* [−90°, 90°], or if either \e stdlat1 or \e
|
||||
* stdlat2 is a pole and \e stdlat1 is not equal \e stdlat2.
|
||||
*
|
||||
* This allows parallels close to the poles to be specified accurately.
|
||||
* This routine computes the latitude of origin and the scale at this
|
||||
* latitude. In the case where \e lat1 and \e lat2 are different, the
|
||||
* errors in this routines are as follows: if \e dlat = abs(\e lat2 −
|
||||
* \e lat1) ≤ 160° and max(abs(\e lat1), abs(\e lat2)) ≤ 90
|
||||
* − min(0.0002, 2.2 × 10<sup>−6</sup>(180 − \e
|
||||
* dlat), 6 × 10<sup>−8</sup> <i>dlat</i><sup>2</sup>) (in
|
||||
* degrees), then the error in the latitude of origin is less than 4.5
|
||||
* × 10<sup>−14</sup>d and the relative error in the scale is
|
||||
* less than 7 × 10<sup>−15</sup>.
|
||||
**********************************************************************/
|
||||
LambertConformalConic(real a, real f,
|
||||
real sinlat1, real coslat1,
|
||||
real sinlat2, real coslat2,
|
||||
real k1);
|
||||
|
||||
/**
|
||||
* Set the scale for the projection.
|
||||
*
|
||||
* @param[in] lat (degrees).
|
||||
* @param[in] k scale at latitude \e lat (default 1).
|
||||
* @exception GeographicErr \e k is not positive.
|
||||
* @exception GeographicErr if \e lat is not in [−90°,
|
||||
* 90°].
|
||||
**********************************************************************/
|
||||
void SetScale(real lat, real k = real(1));
|
||||
|
||||
/**
|
||||
* Forward projection, from geographic to Lambert conformal conic.
|
||||
*
|
||||
* @param[in] lon0 central meridian longitude (degrees).
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[out] x easting of point (meters).
|
||||
* @param[out] y northing of point (meters).
|
||||
* @param[out] gamma meridian convergence at point (degrees).
|
||||
* @param[out] k scale of projection at point.
|
||||
*
|
||||
* The latitude origin is given by LambertConformalConic::LatitudeOrigin().
|
||||
* No false easting or northing is added and \e lat should be in the range
|
||||
* [−90°, 90°]. The error in the projection is less than
|
||||
* about 10 nm (10 nanometers), true distance, and the errors in the
|
||||
* meridian convergence and scale are consistent with this. The values of
|
||||
* \e x and \e y returned for points which project to infinity (i.e., one
|
||||
* or both of the poles) will be large but finite.
|
||||
**********************************************************************/
|
||||
void Forward(real lon0, real lat, real lon,
|
||||
real& x, real& y, real& gamma, real& k) const;
|
||||
|
||||
/**
|
||||
* Reverse projection, from Lambert conformal conic to geographic.
|
||||
*
|
||||
* @param[in] lon0 central meridian longitude (degrees).
|
||||
* @param[in] x easting of point (meters).
|
||||
* @param[in] y northing of point (meters).
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] gamma meridian convergence at point (degrees).
|
||||
* @param[out] k scale of projection at point.
|
||||
*
|
||||
* The latitude origin is given by LambertConformalConic::LatitudeOrigin().
|
||||
* No false easting or northing is added. The value of \e lon returned is
|
||||
* in the range [−180°, 180°]. The error in the projection
|
||||
* is less than about 10 nm (10 nanometers), true distance, and the errors
|
||||
* in the meridian convergence and scale are consistent with this.
|
||||
**********************************************************************/
|
||||
void Reverse(real lon0, real x, real y,
|
||||
real& lat, real& lon, real& gamma, real& k) const;
|
||||
|
||||
/**
|
||||
* LambertConformalConic::Forward without returning the convergence and
|
||||
* scale.
|
||||
**********************************************************************/
|
||||
void Forward(real lon0, real lat, real lon,
|
||||
real& x, real& y) const {
|
||||
real gamma, k;
|
||||
Forward(lon0, lat, lon, x, y, gamma, k);
|
||||
}
|
||||
|
||||
/**
|
||||
* LambertConformalConic::Reverse without returning the convergence and
|
||||
* scale.
|
||||
**********************************************************************/
|
||||
void Reverse(real lon0, real x, real y,
|
||||
real& lat, real& lon) const {
|
||||
real gamma, k;
|
||||
Reverse(lon0, x, y, lat, lon, gamma, k);
|
||||
}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _a; }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the
|
||||
* value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _f; }
|
||||
|
||||
/**
|
||||
* @return latitude of the origin for the projection (degrees).
|
||||
*
|
||||
* This is the latitude of minimum scale and equals the \e stdlat in the
|
||||
* 1-parallel constructor and lies between \e stdlat1 and \e stdlat2 in the
|
||||
* 2-parallel constructors.
|
||||
**********************************************************************/
|
||||
Math::real OriginLatitude() const { return _lat0; }
|
||||
|
||||
/**
|
||||
* @return central scale for the projection. This is the scale on the
|
||||
* latitude of origin.
|
||||
**********************************************************************/
|
||||
Math::real CentralScale() const { return _k0; }
|
||||
///@}
|
||||
|
||||
/**
|
||||
* A global instantiation of LambertConformalConic with the WGS84
|
||||
* ellipsoid, \e stdlat = 0, and \e k0 = 1. This degenerates to the
|
||||
* Mercator projection.
|
||||
**********************************************************************/
|
||||
static const LambertConformalConic& Mercator();
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_LAMBERTCONFORMALCONIC_HPP
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* \file LocalCartesian.hpp
|
||||
* \brief Header for GeographicLib::LocalCartesian class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2008-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_LOCALCARTESIAN_HPP)
|
||||
#define GEOGRAPHICLIB_LOCALCARTESIAN_HPP 1
|
||||
|
||||
#include <GeographicLib/Geocentric.hpp>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Local cartesian coordinates
|
||||
*
|
||||
* Convert between geodetic coordinates latitude = \e lat, longitude = \e
|
||||
* lon, height = \e h (measured vertically from the surface of the ellipsoid)
|
||||
* to local cartesian coordinates (\e x, \e y, \e z). The origin of local
|
||||
* cartesian coordinate system is at \e lat = \e lat0, \e lon = \e lon0, \e h
|
||||
* = \e h0. The \e z axis is normal to the ellipsoid; the \e y axis points
|
||||
* due north. The plane \e z = - \e h0 is tangent to the ellipsoid.
|
||||
*
|
||||
* The conversions all take place via geocentric coordinates using a
|
||||
* Geocentric object (by default Geocentric::WGS84()).
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-LocalCartesian.cpp
|
||||
*
|
||||
* <a href="CartConvert.1.html">CartConvert</a> is a command-line utility
|
||||
* providing access to the functionality of Geocentric and LocalCartesian.
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT LocalCartesian {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
static const size_t dim_ = 3;
|
||||
static const size_t dim2_ = dim_ * dim_;
|
||||
Geocentric _earth;
|
||||
real _lat0, _lon0, _h0;
|
||||
real _x0, _y0, _z0, _r[dim2_];
|
||||
void IntForward(real lat, real lon, real h, real& x, real& y, real& z,
|
||||
real M[dim2_]) const;
|
||||
void IntReverse(real x, real y, real z, real& lat, real& lon, real& h,
|
||||
real M[dim2_]) const;
|
||||
void MatrixMultiply(real M[dim2_]) const;
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor setting the origin.
|
||||
*
|
||||
* @param[in] lat0 latitude at origin (degrees).
|
||||
* @param[in] lon0 longitude at origin (degrees).
|
||||
* @param[in] h0 height above ellipsoid at origin (meters); default 0.
|
||||
* @param[in] earth Geocentric object for the transformation; default
|
||||
* Geocentric::WGS84().
|
||||
*
|
||||
* \e lat0 should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
LocalCartesian(real lat0, real lon0, real h0 = 0,
|
||||
const Geocentric& earth = Geocentric::WGS84())
|
||||
: _earth(earth)
|
||||
{ Reset(lat0, lon0, h0); }
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*
|
||||
* @param[in] earth Geocentric object for the transformation; default
|
||||
* Geocentric::WGS84().
|
||||
*
|
||||
* Sets \e lat0 = 0, \e lon0 = 0, \e h0 = 0.
|
||||
**********************************************************************/
|
||||
explicit LocalCartesian(const Geocentric& earth = Geocentric::WGS84())
|
||||
: _earth(earth)
|
||||
{ Reset(real(0), real(0), real(0)); }
|
||||
|
||||
/**
|
||||
* Reset the origin.
|
||||
*
|
||||
* @param[in] lat0 latitude at origin (degrees).
|
||||
* @param[in] lon0 longitude at origin (degrees).
|
||||
* @param[in] h0 height above ellipsoid at origin (meters); default 0.
|
||||
*
|
||||
* \e lat0 should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
void Reset(real lat0, real lon0, real h0 = 0);
|
||||
|
||||
/**
|
||||
* Convert from geodetic to local cartesian coordinates.
|
||||
*
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[in] h height of point above the ellipsoid (meters).
|
||||
* @param[out] x local cartesian coordinate (meters).
|
||||
* @param[out] y local cartesian coordinate (meters).
|
||||
* @param[out] z local cartesian coordinate (meters).
|
||||
*
|
||||
* \e lat should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
void Forward(real lat, real lon, real h, real& x, real& y, real& z)
|
||||
const {
|
||||
IntForward(lat, lon, h, x, y, z, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from geodetic to local cartesian coordinates and return rotation
|
||||
* matrix.
|
||||
*
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[in] h height of point above the ellipsoid (meters).
|
||||
* @param[out] x local cartesian coordinate (meters).
|
||||
* @param[out] y local cartesian coordinate (meters).
|
||||
* @param[out] z local cartesian coordinate (meters).
|
||||
* @param[out] M if the length of the vector is 9, fill with the rotation
|
||||
* matrix in row-major order.
|
||||
*
|
||||
* \e lat should be in the range [−90°, 90°].
|
||||
*
|
||||
* Let \e v be a unit vector located at (\e lat, \e lon, \e h). We can
|
||||
* express \e v as \e column vectors in one of two ways
|
||||
* - in east, north, up coordinates (where the components are relative to a
|
||||
* local coordinate system at (\e lat, \e lon, \e h)); call this
|
||||
* representation \e v1.
|
||||
* - in \e x, \e y, \e z coordinates (where the components are relative to
|
||||
* the local coordinate system at (\e lat0, \e lon0, \e h0)); call this
|
||||
* representation \e v0.
|
||||
* .
|
||||
* Then we have \e v0 = \e M ⋅ \e v1.
|
||||
**********************************************************************/
|
||||
void Forward(real lat, real lon, real h, real& x, real& y, real& z,
|
||||
std::vector<real>& M)
|
||||
const {
|
||||
if (M.end() == M.begin() + dim2_) {
|
||||
real t[dim2_];
|
||||
IntForward(lat, lon, h, x, y, z, t);
|
||||
std::copy(t, t + dim2_, M.begin());
|
||||
} else
|
||||
IntForward(lat, lon, h, x, y, z, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from local cartesian to geodetic coordinates.
|
||||
*
|
||||
* @param[in] x local cartesian coordinate (meters).
|
||||
* @param[in] y local cartesian coordinate (meters).
|
||||
* @param[in] z local cartesian coordinate (meters).
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] h height of point above the ellipsoid (meters).
|
||||
*
|
||||
* In general, there are multiple solutions and the result which minimizes
|
||||
* |<i>h</i> |is returned, i.e., (<i>lat</i>, <i>lon</i>) corresponds to
|
||||
* the closest point on the ellipsoid. The value of \e lon returned is in
|
||||
* the range [−180°, 180°].
|
||||
**********************************************************************/
|
||||
void Reverse(real x, real y, real z, real& lat, real& lon, real& h)
|
||||
const {
|
||||
IntReverse(x, y, z, lat, lon, h, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from local cartesian to geodetic coordinates and return rotation
|
||||
* matrix.
|
||||
*
|
||||
* @param[in] x local cartesian coordinate (meters).
|
||||
* @param[in] y local cartesian coordinate (meters).
|
||||
* @param[in] z local cartesian coordinate (meters).
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] h height of point above the ellipsoid (meters).
|
||||
* @param[out] M if the length of the vector is 9, fill with the rotation
|
||||
* matrix in row-major order.
|
||||
*
|
||||
* Let \e v be a unit vector located at (\e lat, \e lon, \e h). We can
|
||||
* express \e v as \e column vectors in one of two ways
|
||||
* - in east, north, up coordinates (where the components are relative to a
|
||||
* local coordinate system at (\e lat, \e lon, \e h)); call this
|
||||
* representation \e v1.
|
||||
* - in \e x, \e y, \e z coordinates (where the components are relative to
|
||||
* the local coordinate system at (\e lat0, \e lon0, \e h0)); call this
|
||||
* representation \e v0.
|
||||
* .
|
||||
* Then we have \e v1 = <i>M</i><sup>T</sup> ⋅ \e v0, where
|
||||
* <i>M</i><sup>T</sup> is the transpose of \e M.
|
||||
**********************************************************************/
|
||||
void Reverse(real x, real y, real z, real& lat, real& lon, real& h,
|
||||
std::vector<real>& M)
|
||||
const {
|
||||
if (M.end() == M.begin() + dim2_) {
|
||||
real t[dim2_];
|
||||
IntReverse(x, y, z, lat, lon, h, t);
|
||||
std::copy(t, t + dim2_, M.begin());
|
||||
} else
|
||||
IntReverse(x, y, z, lat, lon, h, NULL);
|
||||
}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return latitude of the origin (degrees).
|
||||
**********************************************************************/
|
||||
Math::real LatitudeOrigin() const { return _lat0; }
|
||||
|
||||
/**
|
||||
* @return longitude of the origin (degrees).
|
||||
**********************************************************************/
|
||||
Math::real LongitudeOrigin() const { return _lon0; }
|
||||
|
||||
/**
|
||||
* @return height of the origin (meters).
|
||||
**********************************************************************/
|
||||
Math::real HeightOrigin() const { return _h0; }
|
||||
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value of \e a inherited from the Geocentric object used in the
|
||||
* constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the value
|
||||
* inherited from the Geocentric object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _earth.Flattening(); }
|
||||
///@}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_LOCALCARTESIAN_HPP
|
||||
@@ -0,0 +1,368 @@
|
||||
/**
|
||||
* \file MGRS.hpp
|
||||
* \brief Header for GeographicLib::MGRS class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2008-2024) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_MGRS_HPP)
|
||||
#define GEOGRAPHICLIB_MGRS_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/UTMUPS.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Convert between UTM/UPS and %MGRS
|
||||
*
|
||||
* MGRS is defined in Chapter 3 of
|
||||
* - J. W. Hager, L. L. Fry, S. S. Jacks, D. R. Hill,
|
||||
* <a href="https://web.archive.org/web/20161214054445/http://earth-info.nga.mil/GandG/publications/tm8358.1/pdf/TM8358_1.pdf">
|
||||
* Datums, Ellipsoids, Grids, and Grid Reference Systems</a>,
|
||||
* Defense Mapping Agency, Technical Manual TM8358.1 (1990).
|
||||
* .
|
||||
* This document has been updated by the two NGA documents
|
||||
* - <a href="https://earth-info.nga.mil/php/download.php?file=coord-grids">
|
||||
* Universal Grids and Grid Reference Systems</a>,
|
||||
* NGA.STND.0037 (2014).
|
||||
* - <a href="https://earth-info.nga.mil/php/download.php?file=coord-utmups">
|
||||
* The Universal Grids and the Transverse Mercator and Polar Stereographic
|
||||
* Map Projections</a>, NGA.SIG.0012 (2014).
|
||||
*
|
||||
* This implementation has the following properties:
|
||||
* - The conversions are closed, i.e., output from Forward is legal input for
|
||||
* Reverse and vice versa. Conversion in both directions preserve the
|
||||
* UTM/UPS selection and the UTM zone.
|
||||
* - Forward followed by Reverse and vice versa is approximately the
|
||||
* identity. (This is affected in predictable ways by errors in
|
||||
* determining the latitude band and by loss of precision in the MGRS
|
||||
* coordinates.)
|
||||
* - The trailing digits produced by Forward are consistent as the precision
|
||||
* is varied. Specifically, the digits are obtained by operating on the
|
||||
* easting with ⌊10<sup>6</sup> <i>x</i>⌋ and extracting the
|
||||
* required digits from the resulting number (and similarly for the
|
||||
* northing).
|
||||
* - All MGRS coordinates truncate to legal 100 km blocks. All MGRS
|
||||
* coordinates with a legal 100 km block prefix are legal (even though the
|
||||
* latitude band letter may now belong to a neighboring band).
|
||||
* - The range of UTM/UPS coordinates allowed for conversion to MGRS
|
||||
* coordinates is the maximum consistent with staying within the letter
|
||||
* ranges of the MGRS scheme.
|
||||
* - All the transformations are implemented as static methods in the MGRS
|
||||
* class.
|
||||
*
|
||||
* The <a href="http://www.nga.mil">NGA</a> software package
|
||||
* <a href="https://earth-info.nga.mil/index.php?dir=wgs84&action=wgs84#tab_geotrans">geotrans</a>
|
||||
* also provides conversions to and from MGRS. Version 3.0 (and earlier)
|
||||
* suffers from some drawbacks:
|
||||
* - Inconsistent rules are used to determine the whether a particular MGRS
|
||||
* coordinate is legal. A more systematic approach is taken here.
|
||||
* - The underlying projections are not very accurately implemented.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-MGRS.cpp
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT MGRS {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
static const char* const hemispheres_;
|
||||
static const char* const utmcols_[3];
|
||||
static const char* const utmrow_;
|
||||
static const char* const upscols_[4];
|
||||
static const char* const upsrows_[2];
|
||||
static const char* const latband_;
|
||||
static const char* const upsband_;
|
||||
static const char* const digits_;
|
||||
static const char* const alpha_;
|
||||
|
||||
static const int mineasting_[4];
|
||||
static const int maxeasting_[4];
|
||||
static const int minnorthing_[4];
|
||||
static const int maxnorthing_[4];
|
||||
static constexpr int base_ = 10;
|
||||
// Top-level tiles are 10^5 m = 100 km on a side
|
||||
static constexpr int tilelevel_ = 5;
|
||||
// Period of UTM row letters
|
||||
static constexpr int utmrowperiod_ = 20;
|
||||
// Row letters are shifted by 5 for even zones
|
||||
static constexpr int utmevenrowshift_ = 5;
|
||||
// Maximum precision is um
|
||||
static constexpr int maxprec_ = 5 + 6;
|
||||
// For generating digits at maxprec
|
||||
static constexpr int mult_ = 1000000;
|
||||
static void CheckCoords(bool utmp, bool& northp, real& x, real& y);
|
||||
static int UTMRow(int iband, int icol, int irow);
|
||||
|
||||
friend class UTMUPS; // UTMUPS::StandardZone calls LatitudeBand
|
||||
// Return latitude band number [-10, 10) for the given latitude (degrees).
|
||||
// The bands are reckoned in include their southern edges.
|
||||
static int LatitudeBand(real lat) {
|
||||
using std::floor;
|
||||
int ilat = int(floor(lat));
|
||||
return (std::max)(-10, (std::min)(9, (ilat + 80)/8 - 10));
|
||||
}
|
||||
// Return approximate latitude band number [-10, 10) for the given northing
|
||||
// (meters). With this rule, each 100km tile would have a unique band
|
||||
// letter corresponding to the latitude at the center of the tile. This
|
||||
// function isn't currently used.
|
||||
static int ApproxLatitudeBand(real y) {
|
||||
// northing at tile center in units of tile = 100km
|
||||
using std::floor, std::fabs, std::fmin;
|
||||
real ya = floor( fmin(real(88), fabs(y / real(tile_))) ) + real(0.5);
|
||||
// convert to lat (mult by 90/100) and then to band (divide by 8)
|
||||
// the +1 fine tunes the boundary between bands 3 and 4
|
||||
int b = int(floor( ((ya * 9 + 1) / 10) / 8 ));
|
||||
// For the northern hemisphere we have
|
||||
// band rows num
|
||||
// N 0 0:8 9
|
||||
// P 1 9:17 9
|
||||
// Q 2 18:26 9
|
||||
// R 3 27:34 8
|
||||
// S 4 35:43 9
|
||||
// T 5 44:52 9
|
||||
// U 6 53:61 9
|
||||
// V 7 62:70 9
|
||||
// W 8 71:79 9
|
||||
// X 9 80:94 15
|
||||
return y >= 0 ? b : -(b + 1);
|
||||
}
|
||||
// UTMUPS accesses these constants
|
||||
static constexpr int tile_ = 100000; // Size MGRS blocks
|
||||
static constexpr int minutmcol_ = 1;
|
||||
static constexpr int maxutmcol_ = 9;
|
||||
static constexpr int minutmSrow_ = 10;
|
||||
static constexpr int maxutmSrow_ = 100; // Also used for UTM S false northing
|
||||
static constexpr int minutmNrow_ = 0; // Also used for UTM N false northing
|
||||
static constexpr int maxutmNrow_ = 95;
|
||||
static constexpr int minupsSind_ = 8; // These 4 ind's apply to easting and northing
|
||||
static constexpr int maxupsSind_ = 32;
|
||||
static constexpr int minupsNind_ = 13;
|
||||
static constexpr int maxupsNind_ = 27;
|
||||
static constexpr int upseasting_ = 20; // Also used for UPS false northing
|
||||
static constexpr int utmeasting_ = 5; // UTM false easting
|
||||
// Difference between S hemisphere northing and N hemisphere northing
|
||||
static constexpr int utmNshift_ = (maxutmSrow_ - minutmNrow_) * tile_;
|
||||
MGRS() = delete; // Disable constructor
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Convert UTM or UPS coordinate to an MGRS coordinate.
|
||||
*
|
||||
* @param[in] zone UTM zone (zero means UPS).
|
||||
* @param[in] northp hemisphere (true means north, false means south).
|
||||
* @param[in] x easting of point (meters).
|
||||
* @param[in] y northing of point (meters).
|
||||
* @param[in] prec precision relative to 100 km.
|
||||
* @param[out] mgrs MGRS string.
|
||||
* @exception GeographicErr if \e zone, \e x, or \e y is outside its
|
||||
* allowed range.
|
||||
* @exception GeographicErr if the memory for the MGRS string can't be
|
||||
* allocated.
|
||||
*
|
||||
* \e prec specifies the precision of the MGRS string as follows:
|
||||
* - \e prec = −1 (min), only the grid zone is returned
|
||||
* - \e prec = 0, 100 km
|
||||
* - \e prec = 1, 10 km
|
||||
* - \e prec = 2, 1 km
|
||||
* - \e prec = 3, 100 m
|
||||
* - \e prec = 4, 10 m
|
||||
* - \e prec = 5, 1 m
|
||||
* - \e prec = 6, 0.1 m
|
||||
* - …
|
||||
* - \e prec = 11 (max), 1 μm
|
||||
*
|
||||
* UTM eastings are allowed to be in the range [100 km, 900 km], northings
|
||||
* are allowed to be in in [0 km, 9500 km] for the northern hemisphere and
|
||||
* in [1000 km, 10000 km] for the southern hemisphere. (However UTM
|
||||
* northings can be continued across the equator. So the actual limits on
|
||||
* the northings are [−9000 km, 9500 km] for the "northern"
|
||||
* hemisphere and [1000 km, 19500 km] for the "southern" hemisphere.)
|
||||
*
|
||||
* UPS eastings/northings are allowed to be in the range [1300 km, 2700 km]
|
||||
* in the northern hemisphere and in [800 km, 3200 km] in the southern
|
||||
* hemisphere.
|
||||
*
|
||||
* The ranges are 100 km more restrictive than for the conversion between
|
||||
* geographic coordinates and UTM and UPS given by UTMUPS. These
|
||||
* restrictions are dictated by the allowed letters in MGRS coordinates.
|
||||
* The choice of 9500 km for the maximum northing for northern hemisphere
|
||||
* and of 1000 km as the minimum northing for southern hemisphere provide
|
||||
* at least 0.5 degree extension into standard UPS zones. The upper ends
|
||||
* of the ranges for the UPS coordinates is dictated by requiring symmetry
|
||||
* about the meridians 0E and 90E.
|
||||
*
|
||||
* All allowed UTM and UPS coordinates may now be converted to legal MGRS
|
||||
* coordinates with the proviso that eastings and northings on the upper
|
||||
* boundaries are silently reduced by about 4 nm (4 nanometers) to place
|
||||
* them \e within the allowed range. (This includes reducing a southern
|
||||
* hemisphere northing of 10000 km by 4 nm so that it is placed in latitude
|
||||
* band M.) The UTM or UPS coordinates are truncated to requested
|
||||
* precision to determine the MGRS coordinate. Thus in UTM zone 38n, the
|
||||
* square area with easting in [444 km, 445 km) and northing in [3688 km,
|
||||
* 3689 km) maps to MGRS coordinate 38SMB4488 (at \e prec = 2, 1 km),
|
||||
* Khulani Sq., Baghdad.
|
||||
*
|
||||
* The UTM/UPS selection and the UTM zone is preserved in the conversion to
|
||||
* MGRS coordinate. Thus for \e zone > 0, the MGRS coordinate begins with
|
||||
* the zone number followed by one of [C--M] for the southern
|
||||
* hemisphere and [N--X] for the northern hemisphere. For \e zone =
|
||||
* 0, the MGRS coordinates begins with one of [AB] for the southern
|
||||
* hemisphere and [XY] for the northern hemisphere.
|
||||
*
|
||||
* The conversion to the MGRS is exact for prec in [0, 5] except that a
|
||||
* neighboring latitude band letter may be given if the point is within 5nm
|
||||
* of a band boundary. For prec in [6, 11], the conversion is accurate to
|
||||
* roundoff.
|
||||
*
|
||||
* If \e prec = −1, then the "grid zone designation", e.g., 18T, is
|
||||
* returned. This consists of the UTM zone number (absent for UPS) and the
|
||||
* first letter of the MGRS string which labels the latitude band for UTM
|
||||
* and the hemisphere for UPS.
|
||||
*
|
||||
* If \e x or \e y is NaN or if \e zone is UTMUPS::INVALID, the returned
|
||||
* MGRS string is "INVALID".
|
||||
*
|
||||
* Return the result via a reference argument to avoid the overhead of
|
||||
* allocating a potentially large number of small strings. If an error is
|
||||
* thrown, then \e mgrs is unchanged.
|
||||
**********************************************************************/
|
||||
static void Forward(int zone, bool northp, real x, real y,
|
||||
int prec, std::string& mgrs);
|
||||
|
||||
/**
|
||||
* Convert UTM or UPS coordinate to an MGRS coordinate when the latitude is
|
||||
* known.
|
||||
*
|
||||
* @param[in] zone UTM zone (zero means UPS).
|
||||
* @param[in] northp hemisphere (true means north, false means south).
|
||||
* @param[in] x easting of point (meters).
|
||||
* @param[in] y northing of point (meters).
|
||||
* @param[in] lat latitude (degrees).
|
||||
* @param[in] prec precision relative to 100 km.
|
||||
* @param[out] mgrs MGRS string.
|
||||
* @exception GeographicErr if \e zone, \e x, or \e y is outside its
|
||||
* allowed range.
|
||||
* @exception GeographicErr if \e lat is inconsistent with the given UTM
|
||||
* coordinates.
|
||||
* @exception std::bad_alloc if the memory for \e mgrs can't be allocated.
|
||||
*
|
||||
* The latitude is ignored for \e zone = 0 (UPS); otherwise the latitude is
|
||||
* used to determine the latitude band and this is checked for consistency
|
||||
* using the same tests as Reverse.
|
||||
**********************************************************************/
|
||||
static void Forward(int zone, bool northp, real x, real y, real lat,
|
||||
int prec, std::string& mgrs);
|
||||
|
||||
/**
|
||||
* Convert a MGRS coordinate to UTM or UPS coordinates.
|
||||
*
|
||||
* @param[in] mgrs MGRS string.
|
||||
* @param[out] zone UTM zone (zero means UPS).
|
||||
* @param[out] northp hemisphere (true means north, false means south).
|
||||
* @param[out] x easting of point (meters).
|
||||
* @param[out] y northing of point (meters).
|
||||
* @param[out] prec precision relative to 100 km.
|
||||
* @param[in] centerp if true (default), return center of the MGRS square,
|
||||
* else return SW (lower left) corner.
|
||||
* @exception GeographicErr if \e mgrs is illegal.
|
||||
*
|
||||
* All conversions from MGRS to UTM/UPS are permitted provided the MGRS
|
||||
* coordinate is a possible result of a conversion in the other direction.
|
||||
* (The leading 0 may be dropped from an input MGRS coordinate for UTM
|
||||
* zones 1--9.) In addition, MGRS coordinates with a neighboring
|
||||
* latitude band letter are permitted provided that some portion of the
|
||||
* 100 km block is within the given latitude band. Thus
|
||||
* - 38VLS and 38WLS are allowed (latitude 64N intersects the square
|
||||
* 38[VW]LS); but 38VMS is not permitted (all of 38WMS is north of 64N)
|
||||
* - 38MPE and 38NPF are permitted (they straddle the equator); but 38NPE
|
||||
* and 38MPF are not permitted (the equator does not intersect either
|
||||
* block).
|
||||
* - Similarly ZAB and YZB are permitted (they straddle the prime
|
||||
* meridian); but YAB and ZZB are not (the prime meridian does not
|
||||
* intersect either block).
|
||||
*
|
||||
* The UTM/UPS selection and the UTM zone is preserved in the conversion
|
||||
* from MGRS coordinate. The conversion is exact for prec in [0, 5]. With
|
||||
* \e centerp = true, the conversion from MGRS to geographic and back is
|
||||
* stable. This is not assured if \e centerp = false.
|
||||
*
|
||||
* If a "grid zone designation" (for example, 18T or A) is given, then some
|
||||
* suitable (but essentially arbitrary) point within that grid zone is
|
||||
* returned. The main utility of the conversion is to allow \e zone and \e
|
||||
* northp to be determined. In this case, the \e centerp parameter is
|
||||
* ignored and \e prec is set to −1.
|
||||
*
|
||||
* If the first 3 characters of \e mgrs are "INV", then \e x and \e y are
|
||||
* set to NaN, \e zone is set to UTMUPS::INVALID, and \e prec is set to
|
||||
* −2.
|
||||
*
|
||||
* If an exception is thrown, then the arguments are unchanged.
|
||||
**********************************************************************/
|
||||
static void Reverse(const std::string& mgrs,
|
||||
int& zone, bool& northp, real& x, real& y,
|
||||
int& prec, bool centerp = true);
|
||||
|
||||
/**
|
||||
* Split a MGRS grid reference into its components.
|
||||
*
|
||||
* @param[in] mgrs MGRS string, e.g., 38SMB4488.
|
||||
* @param[out] gridzone the grid zone, e.g., 38S.
|
||||
* @param[out] block the 100km block id, e.g., MB.
|
||||
* @param[out] easting the leading digits of the block easting, e.g., 44.
|
||||
* @param[out] northing the leading digits of the block easting, e.g., 88.
|
||||
* @exception GeographicErr if \e mgrs is illegal.
|
||||
*
|
||||
* Only the most rudimentary checking of MGRS grid ref is done: it is
|
||||
* expected to consist of 0-2 digits followed by 1 or 3 letters, followed
|
||||
* (in the case of 3 letters) by an even number (possibly 0) of digits. In
|
||||
* reporting errors, the letters I and O (illegal in MSRS) are regarded as
|
||||
* non-alphabetic. The returned \e gridzone will always be non-empty. The
|
||||
* other output arguments may be empty strings.
|
||||
*
|
||||
* If the first 3 characters of \e mgrs are "INV", then \e gridzone is set
|
||||
* to those 3 characters and the other return arguments are set to empty
|
||||
* strings..
|
||||
*
|
||||
* If an exception is thrown, then the arguments are unchanged.
|
||||
**********************************************************************/
|
||||
static void Decode(const std::string& mgrs,
|
||||
std::string& gridzone, std::string& block,
|
||||
std::string& easting, std::string& northing);
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return \e a the equatorial radius of the WGS84 ellipsoid (meters).
|
||||
*
|
||||
* (The WGS84 value is returned because the UTM and UPS projections are
|
||||
* based on this ellipsoid.)
|
||||
**********************************************************************/
|
||||
static Math::real EquatorialRadius() { return UTMUPS::EquatorialRadius(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the WGS84 ellipsoid.
|
||||
*
|
||||
* (The WGS84 value is returned because the UTM and UPS projections are
|
||||
* based on this ellipsoid.)
|
||||
**********************************************************************/
|
||||
static Math::real Flattening() { return UTMUPS::Flattening(); }
|
||||
///@}
|
||||
|
||||
/**
|
||||
* Perform some checks on the UTMUPS coordinates on this ellipsoid. Throw
|
||||
* an error if any of the assumptions made in the MGRS class is not true.
|
||||
* This check needs to be carried out if the ellipsoid parameters (or the
|
||||
* UTM/UPS scales) are ever changed.
|
||||
**********************************************************************/
|
||||
static void Check();
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_MGRS_HPP
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* \file MagneticCircle.hpp
|
||||
* \brief Header for GeographicLib::MagneticCircle class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2011-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_MAGNETICCIRCLE_HPP)
|
||||
#define GEOGRAPHICLIB_MAGNETICCIRCLE_HPP 1
|
||||
|
||||
#include <vector>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/CircularEngine.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Geomagnetic field on a circle of latitude
|
||||
*
|
||||
* Evaluate the earth's magnetic field on a circle of constant height and
|
||||
* latitude. This uses a CircularEngine to pre-evaluate the inner sum of the
|
||||
* spherical harmonic sum, allowing the values of the field at several
|
||||
* different longitudes to be evaluated rapidly.
|
||||
*
|
||||
* Use MagneticModel::Circle to create a MagneticCircle object. (The
|
||||
* constructor for this class is private.)
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-MagneticCircle.cpp
|
||||
*
|
||||
* <a href="MagneticField.1.html">MagneticField</a> is a command-line utility
|
||||
* providing access to the functionality of MagneticModel and MagneticCircle.
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT MagneticCircle {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
|
||||
real _a, _f, _lat, _h, _t, _cphi, _sphi, _t1, _dt0;
|
||||
bool _interpolate, _constterm;
|
||||
CircularEngine _circ0, _circ1, _circ2;
|
||||
|
||||
MagneticCircle(real a, real f, real lat, real h, real t,
|
||||
real cphi, real sphi, real t1, real dt0,
|
||||
bool interpolate,
|
||||
const CircularEngine& circ0, const CircularEngine& circ1)
|
||||
: _a(a)
|
||||
, _f(f)
|
||||
, _lat(Math::LatFix(lat))
|
||||
, _h(h)
|
||||
, _t(t)
|
||||
, _cphi(cphi)
|
||||
, _sphi(sphi)
|
||||
, _t1(t1)
|
||||
, _dt0(dt0)
|
||||
, _interpolate(interpolate)
|
||||
, _constterm(false)
|
||||
, _circ0(circ0)
|
||||
, _circ1(circ1)
|
||||
{}
|
||||
|
||||
MagneticCircle(real a, real f, real lat, real h, real t,
|
||||
real cphi, real sphi, real t1, real dt0,
|
||||
bool interpolate,
|
||||
const CircularEngine& circ0, const CircularEngine& circ1,
|
||||
const CircularEngine& circ2)
|
||||
: _a(a)
|
||||
, _f(f)
|
||||
, _lat(lat)
|
||||
, _h(h)
|
||||
, _t(t)
|
||||
, _cphi(cphi)
|
||||
, _sphi(sphi)
|
||||
, _t1(t1)
|
||||
, _dt0(dt0)
|
||||
, _interpolate(interpolate)
|
||||
, _constterm(true)
|
||||
, _circ0(circ0)
|
||||
, _circ1(circ1)
|
||||
, _circ2(circ2)
|
||||
{}
|
||||
|
||||
void Field(real lon, bool diffp,
|
||||
real& Bx, real& By, real& Bz,
|
||||
real& Bxt, real& Byt, real& Bzt) const;
|
||||
|
||||
void FieldGeocentric(real slam, real clam,
|
||||
real& BX, real& BY, real& BZ,
|
||||
real& BXt, real& BYt, real& BZt) const;
|
||||
|
||||
friend class MagneticModel; // MagneticModel calls the private constructor
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* A default constructor for the normal gravity. This sets up an
|
||||
* uninitialized object which can be later replaced by the
|
||||
* MagneticModel::Circle.
|
||||
**********************************************************************/
|
||||
MagneticCircle() : _a(-1) {}
|
||||
|
||||
/** \name Compute the magnetic field
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Evaluate the components of the geomagnetic field at a particular
|
||||
* longitude.
|
||||
*
|
||||
* @param[in] lon longitude of the point (degrees).
|
||||
* @param[out] Bx the easterly component of the magnetic field (nanotesla).
|
||||
* @param[out] By the northerly component of the magnetic field
|
||||
* (nanotesla).
|
||||
* @param[out] Bz the vertical (up) component of the magnetic field
|
||||
* (nanotesla).
|
||||
**********************************************************************/
|
||||
void operator()(real lon, real& Bx, real& By, real& Bz) const {
|
||||
real dummy;
|
||||
Field(lon, false, Bx, By, Bz, dummy, dummy, dummy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the components of the geomagnetic field and their time
|
||||
* derivatives at a particular longitude.
|
||||
*
|
||||
* @param[in] lon longitude of the point (degrees).
|
||||
* @param[out] Bx the easterly component of the magnetic field (nanotesla).
|
||||
* @param[out] By the northerly component of the magnetic field
|
||||
* (nanotesla).
|
||||
* @param[out] Bz the vertical (up) component of the magnetic field
|
||||
* (nanotesla).
|
||||
* @param[out] Bxt the rate of change of \e Bx (nT/yr).
|
||||
* @param[out] Byt the rate of change of \e By (nT/yr).
|
||||
* @param[out] Bzt the rate of change of \e Bz (nT/yr).
|
||||
**********************************************************************/
|
||||
void operator()(real lon, real& Bx, real& By, real& Bz,
|
||||
real& Bxt, real& Byt, real& Bzt) const {
|
||||
Field(lon, true, Bx, By, Bz, Bxt, Byt, Bzt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the components of the geomagnetic field and their time
|
||||
* derivatives at a particular longitude.
|
||||
*
|
||||
* @param[in] lon longitude of the point (degrees).
|
||||
* @param[out] BX the \e X component of the magnetic field (nT).
|
||||
* @param[out] BY the \e Y component of the magnetic field (nT).
|
||||
* @param[out] BZ the \e Z component of the magnetic field (nT).
|
||||
* @param[out] BXt the rate of change of \e BX (nT/yr).
|
||||
* @param[out] BYt the rate of change of \e BY (nT/yr).
|
||||
* @param[out] BZt the rate of change of \e BZ (nT/yr).
|
||||
**********************************************************************/
|
||||
void FieldGeocentric(real lon, real& BX, real& BY, real& BZ,
|
||||
real& BXt, real& BYt, real& BZt) const;
|
||||
///@}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return true if the object has been initialized.
|
||||
**********************************************************************/
|
||||
bool Init() const { return _a > 0; }
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value inherited from the MagneticModel object used in the
|
||||
* constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const
|
||||
{ return Init() ? _a : Math::NaN(); }
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the value
|
||||
* inherited from the MagneticModel object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const
|
||||
{ return Init() ? _f : Math::NaN(); }
|
||||
/**
|
||||
* @return the latitude of the circle (degrees).
|
||||
**********************************************************************/
|
||||
Math::real Latitude() const
|
||||
{ return Init() ? _lat : Math::NaN(); }
|
||||
/**
|
||||
* @return the height of the circle (meters).
|
||||
**********************************************************************/
|
||||
Math::real Height() const
|
||||
{ return Init() ? _h : Math::NaN(); }
|
||||
/**
|
||||
* @return the time (fractional years).
|
||||
**********************************************************************/
|
||||
Math::real Time() const
|
||||
{ return Init() ? _t : Math::NaN(); }
|
||||
///@}
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_MAGNETICCIRCLE_HPP
|
||||
+426
@@ -0,0 +1,426 @@
|
||||
/**
|
||||
* \file MagneticModel.hpp
|
||||
* \brief Header for GeographicLib::MagneticModel class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2011-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_MAGNETICMODEL_HPP)
|
||||
#define GEOGRAPHICLIB_MAGNETICMODEL_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/Geocentric.hpp>
|
||||
#include <GeographicLib/SphericalHarmonic.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Squelch warnings about dll vs vector
|
||||
# pragma warning (push)
|
||||
# pragma warning (disable: 4251)
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
class MagneticCircle;
|
||||
|
||||
/**
|
||||
* \brief Model of the earth's magnetic field
|
||||
*
|
||||
* Evaluate the earth's magnetic field according to a model. At present only
|
||||
* internal magnetic fields are handled. These are due to the earth's code
|
||||
* and crust; these vary slowly (over many years). Excluded are the effects
|
||||
* of currents in the ionosphere and magnetosphere which have daily and
|
||||
* annual variations.
|
||||
*
|
||||
* See \ref magnetic for details of how to install the magnetic models and
|
||||
* the data format.
|
||||
*
|
||||
* See
|
||||
* - General information:
|
||||
* - http://geomag.org/models/index.html
|
||||
* - WMM2010:
|
||||
* - https://ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml
|
||||
* - https://ngdc.noaa.gov/geomag/WMM/data/WMM2010/WMM2010COF.zip
|
||||
* - WMM2015 (deprecated):
|
||||
* - https://ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml
|
||||
* - https://ngdc.noaa.gov/geomag/WMM/data/WMM2015/WMM2015COF.zip
|
||||
* - WMM2015V2:
|
||||
* - https://ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml
|
||||
* - https://ngdc.noaa.gov/geomag/WMM/data/WMM2015/WMM2015v2COF.zip
|
||||
* - WMM2020:
|
||||
* - https://ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml
|
||||
* - https://ngdc.noaa.gov/geomag/WMM/data/WMM2020/WMM2020COF.zip
|
||||
* - WMM2025:
|
||||
* - https://www.ncei.noaa.gov/products/world-magnetic-model
|
||||
* - WMMHR2025:
|
||||
* - https://www.ncei.noaa.gov/products/world-magnetic-model-high-resolution
|
||||
* - IGRF11:
|
||||
* - https://ngdc.noaa.gov/IAGA/vmod/igrf.html
|
||||
* - https://ngdc.noaa.gov/IAGA/vmod/igrf11coeffs.txt
|
||||
* - https://ngdc.noaa.gov/IAGA/vmod/geomag70_linux.tar.gz
|
||||
* - EMM2010:
|
||||
* - https://ngdc.noaa.gov/geomag/EMM/index.html
|
||||
* - https://ngdc.noaa.gov/geomag/EMM/data/geomag/EMM2010_Sph_Windows_Linux.zip
|
||||
* - EMM2015:
|
||||
* - https://ngdc.noaa.gov/geomag/EMM/index.html
|
||||
* - https://www.ngdc.noaa.gov/geomag/EMM/data/geomag/EMM2015_Sph_Linux.zip
|
||||
* - EMM2017:
|
||||
* - https://ngdc.noaa.gov/geomag/EMM/index.html
|
||||
* - https://www.ngdc.noaa.gov/geomag/EMM/data/geomag/EMM2017_Sph_Linux.zip
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-MagneticModel.cpp
|
||||
*
|
||||
* <a href="MagneticField.1.html">MagneticField</a> is a command-line utility
|
||||
* providing access to the functionality of MagneticModel and MagneticCircle.
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT MagneticModel {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
static const int idlength_ = 8;
|
||||
std::string _name, _dir, _description, _date, _filename, _id;
|
||||
real _t0, _dt0, _tmin, _tmax, _a, _hmin, _hmax;
|
||||
int _nNmodels, _nNconstants, _nmx, _mmx;
|
||||
SphericalHarmonic::normalization _norm;
|
||||
Geocentric _earth;
|
||||
std::vector< std::vector<real> > _gG;
|
||||
std::vector< std::vector<real> > _hH;
|
||||
std::vector<SphericalHarmonic> _harm;
|
||||
void Field(real t, real lat, real lon, real h, bool diffp,
|
||||
real& Bx, real& By, real& Bz,
|
||||
real& Bxt, real& Byt, real& Bzt) const;
|
||||
void ReadMetadata(const std::string& name);
|
||||
// copy constructor not allowed
|
||||
MagneticModel(const MagneticModel&) = delete;
|
||||
// nor copy assignment
|
||||
MagneticModel& operator=(const MagneticModel&) = delete;
|
||||
public:
|
||||
|
||||
/**
|
||||
* Move constructs a magnetic model.
|
||||
**********************************************************************/
|
||||
MagneticModel(MagneticModel&&) = default;
|
||||
|
||||
/**
|
||||
* Move assigns a magnetic model.
|
||||
**********************************************************************/
|
||||
MagneticModel& operator=(MagneticModel&&) = default;
|
||||
|
||||
/** \name Setting up the magnetic model
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Construct a magnetic model.
|
||||
*
|
||||
* @param[in] name the name of the model.
|
||||
* @param[in] path (optional) directory for data file.
|
||||
* @param[in] earth (optional) Geocentric object for converting
|
||||
* coordinates; default Geocentric::WGS84().
|
||||
* @param[in] Nmax (optional) if nonnegative, truncate the degree of the
|
||||
* model this value.
|
||||
* @param[in] Mmax (optional) if nonnegative, truncate the order of the
|
||||
* model this value.
|
||||
* @exception GeographicErr if the data file cannot be found, is
|
||||
* unreadable, or is corrupt, or if \e Mmax > \e Nmax.
|
||||
* @exception std::bad_alloc if the memory necessary for storing the model
|
||||
* can't be allocated.
|
||||
*
|
||||
* A filename is formed by appending ".wmm" (World Magnetic Model) to the
|
||||
* name. If \e path is specified (and is non-empty), then the file is
|
||||
* loaded from directory, \e path. Otherwise the path is given by the
|
||||
* DefaultMagneticPath().
|
||||
*
|
||||
* This file contains the metadata which specifies the properties of the
|
||||
* model. The coefficients for the spherical harmonic sums are obtained
|
||||
* from a file obtained by appending ".cof" to metadata file (so the
|
||||
* filename ends in ".wwm.cof").
|
||||
*
|
||||
* The model is not tied to a particular ellipsoidal model of the earth.
|
||||
* The final earth argument to the constructor specifies an ellipsoid to
|
||||
* allow geodetic coordinates to the transformed into the spherical
|
||||
* coordinates used in the spherical harmonic sum.
|
||||
*
|
||||
* If \e Nmax ≥ 0 and \e Mmax < 0, then \e Mmax is set to \e Nmax.
|
||||
* After the model is loaded, the maximum degree and order of the model can
|
||||
* be found by the Degree() and Order() methods.
|
||||
**********************************************************************/
|
||||
explicit MagneticModel(const std::string& name,
|
||||
const std::string& path = "",
|
||||
const Geocentric& earth = Geocentric::WGS84(),
|
||||
int Nmax = -1, int Mmax = -1);
|
||||
///@}
|
||||
|
||||
/** \name Compute the magnetic field
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Evaluate the components of the geomagnetic field.
|
||||
*
|
||||
* @param[in] t the time (fractional years).
|
||||
* @param[in] lat latitude of the point (degrees).
|
||||
* @param[in] lon longitude of the point (degrees).
|
||||
* @param[in] h the height of the point above the ellipsoid (meters).
|
||||
* @param[out] Bx the easterly component of the magnetic field (nanotesla).
|
||||
* @param[out] By the northerly component of the magnetic field
|
||||
* (nanotesla).
|
||||
* @param[out] Bz the vertical (up) component of the magnetic field
|
||||
* (nanotesla).
|
||||
*
|
||||
* Use Utility::fractionalyear to convert a date of the form yyyy-mm or
|
||||
* yyyy-mm-dd into a fractional year.
|
||||
**********************************************************************/
|
||||
void operator()(real t, real lat, real lon, real h,
|
||||
real& Bx, real& By, real& Bz) const {
|
||||
real dummy;
|
||||
Field(t, lat, lon, h, false, Bx, By, Bz, dummy, dummy, dummy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the components of the geomagnetic field and their time
|
||||
* derivatives
|
||||
*
|
||||
* @param[in] t the time (fractional years).
|
||||
* @param[in] lat latitude of the point (degrees).
|
||||
* @param[in] lon longitude of the point (degrees).
|
||||
* @param[in] h the height of the point above the ellipsoid (meters).
|
||||
* @param[out] Bx the easterly component of the magnetic field (nanotesla).
|
||||
* @param[out] By the northerly component of the magnetic field
|
||||
* (nanotesla).
|
||||
* @param[out] Bz the vertical (up) component of the magnetic field
|
||||
* (nanotesla).
|
||||
* @param[out] Bxt the rate of change of \e Bx (nT/yr).
|
||||
* @param[out] Byt the rate of change of \e By (nT/yr).
|
||||
* @param[out] Bzt the rate of change of \e Bz (nT/yr).
|
||||
*
|
||||
* Use Utility::fractionalyear to convert a date of the form yyyy-mm or
|
||||
* yyyy-mm-dd into a fractional year.
|
||||
**********************************************************************/
|
||||
void operator()(real t, real lat, real lon, real h,
|
||||
real& Bx, real& By, real& Bz,
|
||||
real& Bxt, real& Byt, real& Bzt) const {
|
||||
Field(t, lat, lon, h, true, Bx, By, Bz, Bxt, Byt, Bzt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MagneticCircle object to allow the geomagnetic field at many
|
||||
* points with constant \e lat, \e h, and \e t and varying \e lon to be
|
||||
* computed efficiently.
|
||||
*
|
||||
* @param[in] t the time (fractional years).
|
||||
* @param[in] lat latitude of the point (degrees).
|
||||
* @param[in] h the height of the point above the ellipsoid (meters).
|
||||
* @exception std::bad_alloc if the memory necessary for creating a
|
||||
* MagneticCircle can't be allocated.
|
||||
* @return a MagneticCircle object whose MagneticCircle::operator()(real
|
||||
* lon) member function computes the field at particular values of \e
|
||||
* lon.
|
||||
*
|
||||
* If the field at several points on a circle of latitude need to be
|
||||
* calculated then creating a MagneticCircle and using its member functions
|
||||
* will be substantially faster, especially for high-degree models.
|
||||
*
|
||||
* Use Utility::fractionalyear to convert a date of the form yyyy-mm or
|
||||
* yyyy-mm-dd into a fractional year.
|
||||
**********************************************************************/
|
||||
MagneticCircle Circle(real t, real lat, real h) const;
|
||||
|
||||
/**
|
||||
* Compute the magnetic field in geocentric coordinate.
|
||||
*
|
||||
* @param[in] t the time (fractional years).
|
||||
* @param[in] X geocentric coordinate (meters).
|
||||
* @param[in] Y geocentric coordinate (meters).
|
||||
* @param[in] Z geocentric coordinate (meters).
|
||||
* @param[out] BX the \e X component of the magnetic field (nT).
|
||||
* @param[out] BY the \e Y component of the magnetic field (nT).
|
||||
* @param[out] BZ the \e Z component of the magnetic field (nT).
|
||||
* @param[out] BXt the rate of change of \e BX (nT/yr).
|
||||
* @param[out] BYt the rate of change of \e BY (nT/yr).
|
||||
* @param[out] BZt the rate of change of \e BZ (nT/yr).
|
||||
*
|
||||
* Use Utility::fractionalyear to convert a date of the form yyyy-mm or
|
||||
* yyyy-mm-dd into a fractional year.
|
||||
**********************************************************************/
|
||||
void FieldGeocentric(real t, real X, real Y, real Z,
|
||||
real& BX, real& BY, real& BZ,
|
||||
real& BXt, real& BYt, real& BZt) const;
|
||||
|
||||
/**
|
||||
* Compute various quantities dependent on the magnetic field.
|
||||
*
|
||||
* @param[in] Bx the \e x (easterly) component of the magnetic field (nT).
|
||||
* @param[in] By the \e y (northerly) component of the magnetic field (nT).
|
||||
* @param[in] Bz the \e z (vertical, up positive) component of the magnetic
|
||||
* field (nT).
|
||||
* @param[out] H the horizontal magnetic field (nT).
|
||||
* @param[out] F the total magnetic field (nT).
|
||||
* @param[out] D the declination of the field (degrees east of north).
|
||||
* @param[out] I the inclination of the field (degrees down from
|
||||
* horizontal).
|
||||
**********************************************************************/
|
||||
static void FieldComponents(real Bx, real By, real Bz,
|
||||
real& H, real& F, real& D, real& I) {
|
||||
real Ht, Ft, Dt, It;
|
||||
FieldComponents(Bx, By, Bz, real(0), real(1), real(0),
|
||||
H, F, D, I, Ht, Ft, Dt, It);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute various quantities dependent on the magnetic field and its rate
|
||||
* of change.
|
||||
*
|
||||
* @param[in] Bx the \e x (easterly) component of the magnetic field (nT).
|
||||
* @param[in] By the \e y (northerly) component of the magnetic field (nT).
|
||||
* @param[in] Bz the \e z (vertical, up positive) component of the magnetic
|
||||
* field (nT).
|
||||
* @param[in] Bxt the rate of change of \e Bx (nT/yr).
|
||||
* @param[in] Byt the rate of change of \e By (nT/yr).
|
||||
* @param[in] Bzt the rate of change of \e Bz (nT/yr).
|
||||
* @param[out] H the horizontal magnetic field (nT).
|
||||
* @param[out] F the total magnetic field (nT).
|
||||
* @param[out] D the declination of the field (degrees east of north).
|
||||
* @param[out] I the inclination of the field (degrees down from
|
||||
* horizontal).
|
||||
* @param[out] Ht the rate of change of \e H (nT/yr).
|
||||
* @param[out] Ft the rate of change of \e F (nT/yr).
|
||||
* @param[out] Dt the rate of change of \e D (degrees/yr).
|
||||
* @param[out] It the rate of change of \e I (degrees/yr).
|
||||
**********************************************************************/
|
||||
static void FieldComponents(real Bx, real By, real Bz,
|
||||
real Bxt, real Byt, real Bzt,
|
||||
real& H, real& F, real& D, real& I,
|
||||
real& Ht, real& Ft, real& Dt, real& It);
|
||||
///@}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return the description of the magnetic model, if available, from the
|
||||
* Description file in the data file; if absent, return "NONE".
|
||||
**********************************************************************/
|
||||
const std::string& Description() const { return _description; }
|
||||
|
||||
/**
|
||||
* @return date of the model, if available, from the ReleaseDate field in
|
||||
* the data file; if absent, return "UNKNOWN".
|
||||
**********************************************************************/
|
||||
const std::string& DateTime() const { return _date; }
|
||||
|
||||
/**
|
||||
* @return full file name used to load the magnetic model.
|
||||
**********************************************************************/
|
||||
const std::string& MagneticFile() const { return _filename; }
|
||||
|
||||
/**
|
||||
* @return "name" used to load the magnetic model (from the first argument
|
||||
* of the constructor, but this may be overridden by the model file).
|
||||
**********************************************************************/
|
||||
const std::string& MagneticModelName() const { return _name; }
|
||||
|
||||
/**
|
||||
* @return directory used to load the magnetic model.
|
||||
**********************************************************************/
|
||||
const std::string& MagneticModelDirectory() const { return _dir; }
|
||||
|
||||
/**
|
||||
* @return the minimum height above the ellipsoid (in meters) for which
|
||||
* this MagneticModel should be used.
|
||||
*
|
||||
* Because the model will typically provide useful results
|
||||
* slightly outside the range of allowed heights, no check of \e t
|
||||
* argument is made by MagneticModel::operator()() or
|
||||
* MagneticModel::Circle.
|
||||
**********************************************************************/
|
||||
Math::real MinHeight() const { return _hmin; }
|
||||
|
||||
/**
|
||||
* @return the maximum height above the ellipsoid (in meters) for which
|
||||
* this MagneticModel should be used.
|
||||
*
|
||||
* Because the model will typically provide useful results
|
||||
* slightly outside the range of allowed heights, no check of \e t
|
||||
* argument is made by MagneticModel::operator()() or
|
||||
* MagneticModel::Circle.
|
||||
**********************************************************************/
|
||||
Math::real MaxHeight() const { return _hmax; }
|
||||
|
||||
/**
|
||||
* @return the minimum time (in years) for which this MagneticModel should
|
||||
* be used.
|
||||
*
|
||||
* Because the model will typically provide useful results
|
||||
* slightly outside the range of allowed times, no check of \e t
|
||||
* argument is made by MagneticModel::operator()() or
|
||||
* MagneticModel::Circle.
|
||||
**********************************************************************/
|
||||
Math::real MinTime() const { return _tmin; }
|
||||
|
||||
/**
|
||||
* @return the maximum time (in years) for which this MagneticModel should
|
||||
* be used.
|
||||
*
|
||||
* Because the model will typically provide useful results
|
||||
* slightly outside the range of allowed times, no check of \e t
|
||||
* argument is made by MagneticModel::operator()() or
|
||||
* MagneticModel::Circle.
|
||||
**********************************************************************/
|
||||
Math::real MaxTime() const { return _tmax; }
|
||||
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value of \e a inherited from the Geocentric object used in the
|
||||
* constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the value
|
||||
* inherited from the Geocentric object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _earth.Flattening(); }
|
||||
|
||||
/**
|
||||
* @return \e Nmax the maximum degree of the components of the model.
|
||||
**********************************************************************/
|
||||
int Degree() const { return _nmx; }
|
||||
|
||||
/**
|
||||
* @return \e Mmax the maximum order of the components of the model.
|
||||
**********************************************************************/
|
||||
int Order() const { return _mmx; }
|
||||
///@}
|
||||
|
||||
/**
|
||||
* @return the default path for magnetic model data files.
|
||||
*
|
||||
* This is the value of the environment variable
|
||||
* GEOGRAPHICLIB_MAGNETIC_PATH, if set; otherwise, it is
|
||||
* $GEOGRAPHICLIB_DATA/magnetic if the environment variable
|
||||
* GEOGRAPHICLIB_DATA is set; otherwise, it is a compile-time default
|
||||
* (/usr/local/share/GeographicLib/magnetic on non-Windows systems and
|
||||
* C:/ProgramData/GeographicLib/magnetic on Windows systems).
|
||||
**********************************************************************/
|
||||
static std::string DefaultMagneticPath();
|
||||
|
||||
/**
|
||||
* @return the default name for the magnetic model.
|
||||
*
|
||||
* This is the value of the environment variable
|
||||
* GEOGRAPHICLIB_MAGNETIC_NAME, if set; otherwise, it is "wmm2025". The
|
||||
* MagneticModel class does not use this function; it is just provided as a
|
||||
* convenience for a calling program when constructing a MagneticModel
|
||||
* object.
|
||||
**********************************************************************/
|
||||
static std::string DefaultMagneticName();
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#endif // GEOGRAPHICLIB_MAGNETICMODEL_HPP
|
||||
@@ -0,0 +1,562 @@
|
||||
/**
|
||||
* \file Math.hpp
|
||||
* \brief Header for GeographicLib::Math class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2008-2024) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
// Constants.hpp includes Math.hpp. Place this include outside Math.hpp's
|
||||
// include guard to enforce this ordering.
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_MATH_HPP)
|
||||
#define GEOGRAPHICLIB_MATH_HPP 1
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_WORDS_BIGENDIAN)
|
||||
# define GEOGRAPHICLIB_WORDS_BIGENDIAN 0
|
||||
#endif
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_HAVE_LONG_DOUBLE)
|
||||
# define GEOGRAPHICLIB_HAVE_LONG_DOUBLE 0
|
||||
#endif
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_PRECISION)
|
||||
/**
|
||||
* The precision of floating point numbers used in %GeographicLib. 1 means
|
||||
* float (single precision); 2 (the default) means double; 3 means long double;
|
||||
* 4 is reserved for quadruple precision. Nearly all the testing has been
|
||||
* carried out with doubles and that's the recommended configuration. In order
|
||||
* for long double to be used, GEOGRAPHICLIB_HAVE_LONG_DOUBLE needs to be
|
||||
* defined. Note that with Microsoft Visual Studio, long double is the same as
|
||||
* double.
|
||||
**********************************************************************/
|
||||
# define GEOGRAPHICLIB_PRECISION 2
|
||||
#endif
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#if GEOGRAPHICLIB_PRECISION == 4
|
||||
# include <memory>
|
||||
# include <boost/version.hpp>
|
||||
# include <boost/multiprecision/float128.hpp>
|
||||
# include <boost/math/special_functions.hpp>
|
||||
#elif GEOGRAPHICLIB_PRECISION >= 5
|
||||
# if GEOGRAPHICLIB_PRECISION > 5
|
||||
# define MPREAL_FIXED_PRECISION GEOGRAPHICLIB_PRECISION
|
||||
# else
|
||||
# define MPREAL_FIXED_PRECISION 0
|
||||
# endif
|
||||
# include <mpreal.h>
|
||||
#endif
|
||||
|
||||
#if GEOGRAPHICLIB_PRECISION > 3
|
||||
// volatile keyword makes no sense for multiprec types
|
||||
# define GEOGRAPHICLIB_VOLATILE
|
||||
// Signal a convergence failure with multiprec types by throwing an exception
|
||||
// at loop exit.
|
||||
# define GEOGRAPHICLIB_PANIC(msg) \
|
||||
(throw GeographicLib::GeographicErr(msg), false)
|
||||
#else
|
||||
# define GEOGRAPHICLIB_VOLATILE volatile
|
||||
// Ignore convergence failures with standard floating points types by allowing
|
||||
// loop to exit cleanly.
|
||||
# define GEOGRAPHICLIB_PANIC(msg) false
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Mathematical functions needed by %GeographicLib
|
||||
*
|
||||
* Define mathematical functions in order to localize system dependencies and
|
||||
* to provide generic versions of the functions. In addition define a real
|
||||
* type to be used by %GeographicLib.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Math.cpp
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT Math {
|
||||
private:
|
||||
void dummy(); // Static check for GEOGRAPHICLIB_PRECISION
|
||||
Math() = delete; // Disable constructor
|
||||
public:
|
||||
|
||||
#if GEOGRAPHICLIB_HAVE_LONG_DOUBLE
|
||||
/**
|
||||
* The extended precision type for real numbers, used for some testing.
|
||||
* This is long double on computers with this type; otherwise it is double.
|
||||
**********************************************************************/
|
||||
typedef long double extended;
|
||||
#else
|
||||
typedef double extended;
|
||||
#endif
|
||||
|
||||
#if GEOGRAPHICLIB_PRECISION == 2
|
||||
/**
|
||||
* The real type for %GeographicLib. Nearly all the testing has been done
|
||||
* with \e real = double. However, the algorithms should also work with
|
||||
* float and long double (where available). (<b>CAUTION</b>: reasonable
|
||||
* accuracy typically cannot be obtained using floats.)
|
||||
**********************************************************************/
|
||||
typedef double real;
|
||||
#elif GEOGRAPHICLIB_PRECISION == 1
|
||||
typedef float real;
|
||||
#elif GEOGRAPHICLIB_PRECISION == 3
|
||||
typedef extended real;
|
||||
#elif GEOGRAPHICLIB_PRECISION == 4
|
||||
typedef boost::multiprecision::float128 real;
|
||||
#elif GEOGRAPHICLIB_PRECISION >= 5
|
||||
typedef mpfr::mpreal real;
|
||||
#else
|
||||
typedef double real;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The constants defining the standard (Babylonian) meanings of degrees,
|
||||
* minutes, and seconds, for angles. Read the constants as follows (for
|
||||
* example): \e ms = 60 is the ratio 1 minute / 1 second. The
|
||||
* abbreviations are
|
||||
* - \e t a whole turn (360°)
|
||||
* - \e h a half turn (180°)
|
||||
* - \e q a quarter turn (a right angle = 90°)
|
||||
* - \e d a degree
|
||||
* - \e m a minute
|
||||
* - \e s a second
|
||||
* .
|
||||
* Note that degree() is ratio 1 degree / 1 radian, thus, for example,
|
||||
* Math::degree() * Math::qd is the ratio 1 quarter turn / 1 radian =
|
||||
* π/2.
|
||||
*
|
||||
* Defining all these in one place would mean that it's simple to convert
|
||||
* to the centesimal system for measuring angles. The DMS class assumes
|
||||
* that Math::dm and Math::ms are less than or equal to 100 (so that two
|
||||
* digits suffice for the integer parts of the minutes and degrees
|
||||
* components of an angle). Switching to the centesimal convention will
|
||||
* break most of the tests. Also the normal definition of degree is baked
|
||||
* into some classes, e.g., UTMUPS, MGRS, Georef, Geohash, etc.
|
||||
**********************************************************************/
|
||||
static inline constexpr int qd = 90; ///< degrees per quarter turn
|
||||
static inline constexpr int dm = 60; ///< minutes per degree
|
||||
static inline constexpr int ms = 60; ///< seconds per minute
|
||||
static inline constexpr int hd = 2 * qd; ///< degrees per half turn
|
||||
static inline constexpr int td = 2 * hd; ///< degrees per turn
|
||||
static inline constexpr int ds = dm * ms; ///< seconds per degree
|
||||
|
||||
/**
|
||||
* @return the number of bits of precision in a real number.
|
||||
**********************************************************************/
|
||||
static int digits();
|
||||
|
||||
/**
|
||||
* Set the binary precision of a real number.
|
||||
*
|
||||
* @param[in] ndigits the number of bits of precision.
|
||||
* @return the resulting number of bits of precision.
|
||||
*
|
||||
* This only has an effect when GEOGRAPHICLIB_PRECISION >= 5. See also
|
||||
* Utility::set_digits for caveats about when this routine should be
|
||||
* called. If GEOGRAPHICLIB_PRECISION > 5, the precision is set to the
|
||||
* compile-time value of GEOGRAPHICLIB_PRECISION and \e ndigits is ignored.
|
||||
**********************************************************************/
|
||||
static int set_digits(int ndigits);
|
||||
|
||||
/**
|
||||
* @return the number of decimal digits of precision in a real number.
|
||||
**********************************************************************/
|
||||
static int digits10();
|
||||
|
||||
/**
|
||||
* Number of additional decimal digits of precision for real relative to
|
||||
* double (0 for float).
|
||||
**********************************************************************/
|
||||
static int extra_digits();
|
||||
|
||||
/**
|
||||
* true if the machine is big-endian.
|
||||
**********************************************************************/
|
||||
static const bool bigendian = GEOGRAPHICLIB_WORDS_BIGENDIAN;
|
||||
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return π.
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T pi() {
|
||||
using std::atan2;
|
||||
static const T pi = atan2(T(0), T(-1));
|
||||
return pi;
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the type of the returned value.
|
||||
* @return the number of radians in a degree.
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T degree() {
|
||||
static const T degree = pi<T>() / T(hd);
|
||||
return degree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Square a number.
|
||||
*
|
||||
* @tparam T the type of the argument and the returned value.
|
||||
* @param[in] x
|
||||
* @return <i>x</i><sup>2</sup>.
|
||||
**********************************************************************/
|
||||
template<typename T> static T sq(T x)
|
||||
{ return x * x; }
|
||||
|
||||
/**
|
||||
* Normalize a two-vector.
|
||||
*
|
||||
* @tparam T the type of the argument and the returned value.
|
||||
* @param[in,out] x on output set to <i>x</i>/hypot(<i>x</i>, <i>y</i>).
|
||||
* @param[in,out] y on output set to <i>y</i>/hypot(<i>x</i>, <i>y</i>).
|
||||
**********************************************************************/
|
||||
template<typename T> static void norm(T& x, T& y) {
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1950 && defined(_M_IX86)
|
||||
// hypot for Visual Studio (A=win32) fails monotonicity, e.g., with
|
||||
// x = 0.6102683302836215
|
||||
// y1 = 0.7906090004346522
|
||||
// y2 = y1 + 1e-16
|
||||
// the test
|
||||
// hypot(x, y2) >= hypot(x, y1)
|
||||
// fails. Reported 2021-03-14:
|
||||
// https://developercommunity.visualstudio.com/t/1369259
|
||||
// See also:
|
||||
// https://bugs.python.org/issue43088
|
||||
// Bug still present in my version of vc17 (2022) updated on 2025-09-01.
|
||||
// Let's hope it's fixed in vc18.
|
||||
using std::sqrt; T h = sqrt(x * x + y * y);
|
||||
#else
|
||||
using std::hypot; T h = hypot(x, y);
|
||||
#endif
|
||||
x /= h; y /= h;
|
||||
}
|
||||
|
||||
/**
|
||||
* The error-free sum of two numbers.
|
||||
*
|
||||
* @tparam T the type of the argument and the returned value.
|
||||
* @param[in] u
|
||||
* @param[in] v
|
||||
* @param[out] t the exact error given by (\e u + \e v) - \e s.
|
||||
* @return \e s = round(\e u + \e v).
|
||||
*
|
||||
* See D. E. Knuth, TAOCP, Vol 2, 4.2.2, Theorem B.
|
||||
*
|
||||
* \note \e t can be the same as one of the first two arguments.
|
||||
**********************************************************************/
|
||||
template<typename T> static T sum(T u, T v, T& t);
|
||||
|
||||
/**
|
||||
* Evaluate a polynomial.
|
||||
*
|
||||
* @tparam T the type of the arguments and returned value.
|
||||
* @param[in] N the order of the polynomial.
|
||||
* @param[in] p the coefficient array (of size \e N + 1) with
|
||||
* <i>p</i><sub>0</sub> being coefficient of <i>x</i><sup><i>N</i></sup>.
|
||||
* @param[in] x the variable.
|
||||
* @return the value of the polynomial.
|
||||
*
|
||||
* Evaluate ∑<sub><i>n</i>=0..<i>N</i></sub>
|
||||
* <i>p</i><sub><i>n</i></sub> <i>x</i><sup><i>N</i>−<i>n</i></sup>.
|
||||
* Return 0 if \e N < 0. Return <i>p</i><sub>0</sub>, if \e N = 0 (even
|
||||
* if \e x is infinite or a nan). The evaluation uses Horner's method.
|
||||
**********************************************************************/
|
||||
template<typename T> static T polyval(int N, const T p[], T x) {
|
||||
// This used to employ Math::fma; but that's too slow and it seemed not
|
||||
// to improve the accuracy noticeably. This might change when there's
|
||||
// direct hardware support for fma.
|
||||
T z = N < 0 ? 0 : *p++;
|
||||
while (--N >= 0) z = z * x + *p++;
|
||||
// To compute z = p(x) and dz = (p(y)-p(x))/(y-x) at the same time
|
||||
// See Kahan + Fateman Sec 2.3. If y = x, dz = p'(x)
|
||||
// T z = N < 0 ? 0 : *p++, dz = 0;
|
||||
// while (--N >= 0) { dz = dz * y + p; z = z * x + *p++; }
|
||||
return z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize an angle.
|
||||
*
|
||||
* @tparam T the type of the argument and returned value.
|
||||
* @param[in] x the angle in degrees.
|
||||
* @return the angle reduced to the range [−180°, 180°].
|
||||
*
|
||||
* The range of \e x is unrestricted. If the result is ±0° or
|
||||
* ±180° then the sign is the sign of \e x.
|
||||
**********************************************************************/
|
||||
template<typename T> static T AngNormalize(T x);
|
||||
|
||||
/**
|
||||
* Normalize a latitude.
|
||||
*
|
||||
* @tparam T the type of the argument and returned value.
|
||||
* @param[in] x the angle in degrees.
|
||||
* @return x if it is in the range [−90°, 90°], otherwise
|
||||
* return NaN.
|
||||
**********************************************************************/
|
||||
template<typename T> static T LatFix(T x)
|
||||
{ using std::fabs; return fabs(x) > T(qd) ? NaN<T>() : x; }
|
||||
|
||||
/**
|
||||
* The exact difference of two angles reduced to
|
||||
* [−180°, 180°].
|
||||
*
|
||||
* @tparam T the type of the arguments and returned value.
|
||||
* @param[in] x the first angle in degrees.
|
||||
* @param[in] y the second angle in degrees.
|
||||
* @param[out] e the error term in degrees.
|
||||
* @return \e d, the truncated value of \e y − \e x.
|
||||
*
|
||||
* This computes \e z = \e y − \e x exactly, reduced to
|
||||
* [−180°, 180°]; and then sets \e z = \e d + \e e where \e d
|
||||
* is the nearest representable number to \e z and \e e is the truncation
|
||||
* error. If \e z = ±0° or ±180°, then the sign of
|
||||
* \e d is given by the sign of \e y − \e x. The maximum absolute
|
||||
* value of \e e is 2<sup>−26</sup> (for doubles).
|
||||
**********************************************************************/
|
||||
template<typename T> static T AngDiff(T x, T y, T& e);
|
||||
|
||||
/**
|
||||
* Difference of two angles reduced to [−180°, 180°]
|
||||
*
|
||||
* @tparam T the type of the arguments and returned value.
|
||||
* @param[in] x the first angle in degrees.
|
||||
* @param[in] y the second angle in degrees.
|
||||
* @return \e y − \e x, reduced to the range [−180°,
|
||||
* 180°].
|
||||
*
|
||||
* The result is equivalent to computing the difference exactly, reducing
|
||||
* it to [−180°, 180°] and rounding the result.
|
||||
**********************************************************************/
|
||||
template<typename T> static T AngDiff(T x, T y)
|
||||
{ T e; return AngDiff(x, y, e); }
|
||||
|
||||
/**
|
||||
* Coarsen a value close to zero.
|
||||
*
|
||||
* @tparam T the type of the argument and returned value.
|
||||
* @param[in] x
|
||||
* @return the coarsened value.
|
||||
*
|
||||
* The makes the smallest gap in \e x = 1/16 − nextafter(1/16, 0) =
|
||||
* 1/2<sup>57</sup> for doubles = 0.8 pm on the earth if \e x is an angle
|
||||
* in degrees. (This is about 2000 times more resolution than we get with
|
||||
* angles around 90°.) We use this to avoid having to deal with near
|
||||
* singular cases when \e x is non-zero but tiny (e.g.,
|
||||
* 10<sup>−200</sup>). This sign of ±0 is preserved.
|
||||
**********************************************************************/
|
||||
template<typename T> static T AngRound(T x);
|
||||
|
||||
/**
|
||||
* Evaluate the sine and cosine function with the argument in degrees
|
||||
*
|
||||
* @tparam T the type of the arguments.
|
||||
* @param[in] x in degrees.
|
||||
* @param[out] sinx sin(<i>x</i>).
|
||||
* @param[out] cosx cos(<i>x</i>).
|
||||
*
|
||||
* The results obey exactly the elementary properties of the trigonometric
|
||||
* functions, e.g., sin 9° = cos 81° = − sin 123456789°.
|
||||
* If x = −0 or a negative multiple of 180°, then \e sinx =
|
||||
* −0; this is the only case where −0 is returned.
|
||||
**********************************************************************/
|
||||
template<typename T> static void sincosd(T x, T& sinx, T& cosx);
|
||||
|
||||
/**
|
||||
* Evaluate the sine and cosine with reduced argument plus correction
|
||||
*
|
||||
* @tparam T the type of the arguments.
|
||||
* @param[in] x reduced angle in degrees.
|
||||
* @param[in] t correction in degrees.
|
||||
* @param[out] sinx sin(<i>x</i> + <i>t</i>).
|
||||
* @param[out] cosx cos(<i>x</i> + <i>t</i>).
|
||||
*
|
||||
* This is a variant of Math::sincosd allowing a correction to the angle to
|
||||
* be supplied. \e x must be in [−180°, 180°] and \e t is
|
||||
* assumed to be a <i>small</i> correction. Math::AngRound is applied to
|
||||
* the reduced angle to prevent problems with \e x + \e t being extremely
|
||||
* close but not exactly equal to one of the four cardinal directions.
|
||||
**********************************************************************/
|
||||
template<typename T> static void sincosde(T x, T t, T& sinx, T& cosx);
|
||||
|
||||
/**
|
||||
* Evaluate the sine function with the argument in degrees
|
||||
*
|
||||
* @tparam T the type of the argument and the returned value.
|
||||
* @param[in] x in degrees.
|
||||
* @return sin(<i>x</i>).
|
||||
*
|
||||
* The result is +0 for \e x = +0 and positive multiples of 180°. The
|
||||
* result is −0 for \e x = -0 and negative multiples of 180°.
|
||||
**********************************************************************/
|
||||
template<typename T> static T sind(T x);
|
||||
|
||||
/**
|
||||
* Evaluate the cosine function with the argument in degrees
|
||||
*
|
||||
* @tparam T the type of the argument and the returned value.
|
||||
* @param[in] x in degrees.
|
||||
* @return cos(<i>x</i>).
|
||||
*
|
||||
* The result is +0 for \e x an odd multiple of 90°.
|
||||
**********************************************************************/
|
||||
template<typename T> static T cosd(T x);
|
||||
|
||||
/**
|
||||
* Evaluate the tangent function with the argument in degrees
|
||||
*
|
||||
* @tparam T the type of the argument and the returned value.
|
||||
* @param[in] x in degrees.
|
||||
* @return tan(<i>x</i>).
|
||||
*
|
||||
* If \e x is an odd multiple of 90°, then a suitably large (but
|
||||
* finite) value is returned.
|
||||
**********************************************************************/
|
||||
template<typename T> static T tand(T x);
|
||||
|
||||
/**
|
||||
* Evaluate the atan2 function with the result in degrees
|
||||
*
|
||||
* @tparam T the type of the arguments and the returned value.
|
||||
* @param[in] y
|
||||
* @param[in] x
|
||||
* @return atan2(<i>y</i>, <i>x</i>) in degrees.
|
||||
*
|
||||
* The result is in the range [−180° 180°]. N.B.,
|
||||
* atan2d(±0, −1) = ±180°.
|
||||
**********************************************************************/
|
||||
template<typename T> static T atan2d(T y, T x);
|
||||
|
||||
/**
|
||||
* Evaluate the atan function with the result in degrees
|
||||
*
|
||||
* @tparam T the type of the argument and the returned value.
|
||||
* @param[in] x
|
||||
* @return atan(<i>x</i>) in degrees.
|
||||
**********************************************************************/
|
||||
template<typename T> static T atand(T x);
|
||||
|
||||
/**
|
||||
* Evaluate <i>e</i> atanh(<i>e x</i>)
|
||||
*
|
||||
* @tparam T the type of the argument and the returned value.
|
||||
* @param[in] x
|
||||
* @param[in] es the signed eccentricity = sign(<i>e</i><sup>2</sup>)
|
||||
* sqrt(|<i>e</i><sup>2</sup>|)
|
||||
* @return <i>e</i> atanh(<i>e x</i>)
|
||||
*
|
||||
* If <i>e</i><sup>2</sup> is negative (<i>e</i> is imaginary), the
|
||||
* expression is evaluated in terms of atan.
|
||||
**********************************************************************/
|
||||
template<typename T> static T eatanhe(T x, T es);
|
||||
|
||||
/**
|
||||
* tanχ in terms of tanφ
|
||||
*
|
||||
* @tparam T the type of the argument and the returned value.
|
||||
* @param[in] tau τ = tanφ
|
||||
* @param[in] es the signed eccentricity = sign(<i>e</i><sup>2</sup>)
|
||||
* sqrt(|<i>e</i><sup>2</sup>|)
|
||||
* @return τ′ = tanχ
|
||||
*
|
||||
* See Eqs. (7--9) of
|
||||
* C. F. F. Karney,
|
||||
* <a href="https://doi.org/10.1007/s00190-011-0445-3">
|
||||
* Transverse Mercator with an accuracy of a few nanometers,</a>
|
||||
* J. Geodesy 85(8), 475--485 (Aug. 2011)
|
||||
* (preprint
|
||||
* <a href="https://arxiv.org/abs/1002.1417">arXiv:1002.1417</a>).
|
||||
**********************************************************************/
|
||||
template<typename T> static T taupf(T tau, T es);
|
||||
|
||||
/**
|
||||
* tanφ in terms of tanχ
|
||||
*
|
||||
* @tparam T the type of the argument and the returned value.
|
||||
* @param[in] taup τ′ = tanχ
|
||||
* @param[in] es the signed eccentricity = sign(<i>e</i><sup>2</sup>)
|
||||
* sqrt(|<i>e</i><sup>2</sup>|)
|
||||
* @return τ = tanφ
|
||||
*
|
||||
* See Eqs. (19--21) of
|
||||
* C. F. F. Karney,
|
||||
* <a href="https://doi.org/10.1007/s00190-011-0445-3">
|
||||
* Transverse Mercator with an accuracy of a few nanometers,</a>
|
||||
* J. Geodesy 85(8), 475--485 (Aug. 2011)
|
||||
* (preprint
|
||||
* <a href="https://arxiv.org/abs/1002.1417">arXiv:1002.1417</a>).
|
||||
**********************************************************************/
|
||||
template<typename T> static T tauf(T taup, T es);
|
||||
|
||||
/**
|
||||
* Implement hypot with 3 parameters
|
||||
*
|
||||
* @tparam T the type of the argument and the returned value.
|
||||
* @param[in] x
|
||||
* @param[in] y
|
||||
* @param[in] z
|
||||
* @return sqrt(<i>x</i><sup>2</sup> + <i>y</i><sup>2</sup> +
|
||||
* <i>z</i><sup>2</sup>).
|
||||
**********************************************************************/
|
||||
template<typename T> static T hypot3(T x, T y, T z);
|
||||
|
||||
/**
|
||||
* Implement work-alike to C++17 clamp function
|
||||
*
|
||||
* @tparam T the type of the argument and the returned value.
|
||||
* @param[in] x
|
||||
* @param[in] a
|
||||
* @param[in] b
|
||||
* @return \e x if it lies in [<i>a</i>, <i>b</i>]; otherise return the
|
||||
* nearest boundary value.
|
||||
*
|
||||
* Requires \e a ≤ \e b. Unlike std::clamp, \e x can be a NaN (and
|
||||
* this is then returned).
|
||||
**********************************************************************/
|
||||
template<typename T> static T clamp(T x, T a, T b);
|
||||
|
||||
/**
|
||||
* The NaN (not a number)
|
||||
*
|
||||
* @tparam T the type of the returned value.
|
||||
* @return NaN if available, otherwise return the max real of type T.
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T NaN();
|
||||
|
||||
/**
|
||||
* Infinity
|
||||
*
|
||||
* @tparam T the type of the returned value.
|
||||
* @return infinity if available, otherwise return the max real.
|
||||
**********************************************************************/
|
||||
template<typename T = real> static T infinity();
|
||||
|
||||
/**
|
||||
* Swap the bytes of a quantity
|
||||
*
|
||||
* @tparam T the type of the argument and the returned value.
|
||||
* @param[in] x
|
||||
* @return x with its bytes swapped.
|
||||
**********************************************************************/
|
||||
template<typename T> static T swab(T x) {
|
||||
union {
|
||||
T r;
|
||||
unsigned char c[sizeof(T)];
|
||||
} b;
|
||||
b.r = x;
|
||||
for (int i = sizeof(T)/2; i--; )
|
||||
std::swap(b.c[i], b.c[sizeof(T) - 1 - i]);
|
||||
return b.r;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_MATH_HPP
|
||||
+826
@@ -0,0 +1,826 @@
|
||||
/**
|
||||
* \file NearestNeighbor.hpp
|
||||
* \brief Header for GeographicLib::NearestNeighbor class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2016-2020) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_NEARESTNEIGHBOR_HPP)
|
||||
#define GEOGRAPHICLIB_NEARESTNEIGHBOR_HPP 1
|
||||
|
||||
#include <algorithm> // for nth_element, max_element, etc.
|
||||
#include <vector>
|
||||
#include <queue> // for priority_queue
|
||||
#include <utility> // for swap + pair
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
// Only for GeographicLib::GeographicErr
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
#if defined(GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION) && \
|
||||
GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION
|
||||
#include <boost/serialization/nvp.hpp>
|
||||
#include <boost/serialization/split_member.hpp>
|
||||
#include <boost/serialization/array.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Nearest-neighbor calculations
|
||||
*
|
||||
* This class solves the nearest-neighbor problm using a vantage-point tree
|
||||
* as described in \ref nearest.
|
||||
*
|
||||
* This class is templated so that it can handle arbitrary metric spaces as
|
||||
* follows:
|
||||
*
|
||||
* @tparam dist_t the type used for measuring distances; it can be a real or
|
||||
* signed integer type; in typical geodetic applications, \e dist_t might
|
||||
* be <code>double</code>.
|
||||
* @tparam pos_t the type for specifying the positions of points; geodetic
|
||||
* application might bundle the latitude and longitude into a
|
||||
* <code>std::pair<dist_t, dist_t></code>.
|
||||
* @tparam distfun_t the type of a function object which takes takes two
|
||||
* positions (of type \e pos_t) and returns the distance (of type \e
|
||||
* dist_t); in geodetic applications, this might be a class which is
|
||||
* constructed with a Geodesic object and which implements a member
|
||||
* function with a signature <code>dist_t operator() (const pos_t&, const
|
||||
* pos_t&) const</code>, which returns the geodesic distance between two
|
||||
* points.
|
||||
*
|
||||
* \note The distance measure must satisfy the triangle inequality, \f$
|
||||
* d(a,c) \le d(a,b) + d(b,c) \f$ for all points \e a, \e b, \e c. The
|
||||
* geodesic distance (given by Geodesic::Inverse) does, while the great
|
||||
* ellipse distance and the rhumb line distance <i>do not</i>. If you use
|
||||
* the ordinary Euclidean distance, i.e., \f$ \sqrt{(x_a-x_b)^2 +
|
||||
* (y_a-y_b)^2} \f$ for two dimensions, don't be tempted to leave out the
|
||||
* square root in the interests of "efficiency"; the squared distance does
|
||||
* not satisfy the triangle inequality!
|
||||
*
|
||||
* \note This is a "header-only" implementation and, as such, depends in a
|
||||
* minimal way on the rest of GeographicLib (the only dependency is through
|
||||
* the use of GeographicLib::GeographicErr for handling and run-time
|
||||
* exceptions). Therefore, it is easy to extract this class from the rest of
|
||||
* GeographicLib and use it as a stand-alone facility.
|
||||
*
|
||||
* The \e dist_t type must support numeric_limits queries (specifically:
|
||||
* is_signed, is_integer, max(), digits).
|
||||
*
|
||||
* The NearestNeighbor object is constructed with a vector of points (type \e
|
||||
* pos_t) and a distance function (type \e distfun_t). However the object
|
||||
* does \e not store the points. When querying the object with Search(),
|
||||
* it's necessary to supply the same vector of points and the same distance
|
||||
* function.
|
||||
*
|
||||
* There's no capability in this implementation to add or remove points from
|
||||
* the set. Instead Initialize() should be called to re-initialize the
|
||||
* object with the modified vector of points.
|
||||
*
|
||||
* Because of the overhead in constructing a NearestNeighbor object for a
|
||||
* large set of points, functions Save() and Load() are provided to save the
|
||||
* object to an external file. operator<<(), operator>>() and <a
|
||||
* href="https://www.boost.org/libs/serialization/doc"> Boost
|
||||
* serialization</a> can also be used to save and restore a NearestNeighbor
|
||||
* object. This is illustrated in the example.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-NearestNeighbor.cpp
|
||||
**********************************************************************/
|
||||
template<typename dist_t, typename pos_t, class distfun_t>
|
||||
class NearestNeighbor {
|
||||
// For tracking changes to the I/O format
|
||||
static const int version = 1;
|
||||
// This is what we get "free"; but if sizeof(dist_t) = 1 (unlikely), allow
|
||||
// 4 slots (and this accommodates the default value bucket = 4).
|
||||
static const int maxbucket =
|
||||
(2 + ((4 * sizeof(dist_t)) / sizeof(int) >= 2 ?
|
||||
(4 * sizeof(dist_t)) / sizeof(int) : 2));
|
||||
public:
|
||||
|
||||
/**
|
||||
* Default constructor for NearestNeighbor.
|
||||
*
|
||||
* This is equivalent to specifying an empty set of points.
|
||||
**********************************************************************/
|
||||
NearestNeighbor() : _numpoints(0), _bucket(0), _cost(0) {}
|
||||
|
||||
/**
|
||||
* Constructor for NearestNeighbor.
|
||||
*
|
||||
* @param[in] pts a vector of points to include in the set.
|
||||
* @param[in] dist the distance function object.
|
||||
* @param[in] bucket the size of the buckets at the leaf nodes; this must
|
||||
* lie in [0, 2 + 4*sizeof(dist_t)/sizeof(int)] (default 4).
|
||||
* @exception GeographicErr if the value of \e bucket is out of bounds or
|
||||
* the size of \e pts is too big for an int.
|
||||
* @exception std::bad_alloc if memory for the tree can't be allocated.
|
||||
*
|
||||
* \e pts may contain coincident points (i.e., the distance between them
|
||||
* vanishes); these are treated as distinct.
|
||||
*
|
||||
* The choice of \e bucket is a tradeoff between space and efficiency. A
|
||||
* larger \e bucket decreases the size of the NearestNeighbor object which
|
||||
* scales as pts.size() / max(1, bucket) and reduces the number of distance
|
||||
* calculations to construct the object by log2(bucket) * pts.size().
|
||||
* However each search then requires about bucket additional distance
|
||||
* calculations.
|
||||
*
|
||||
* \warning The distances computed by \e dist must satisfy the standard
|
||||
* metric conditions. If not, the results are undefined. Neither the data
|
||||
* in \e pts nor the query points should contain NaNs or infinities because
|
||||
* such data violates the metric conditions.
|
||||
*
|
||||
* \warning The same arguments \e pts and \e dist must be provided
|
||||
* to the Search() function.
|
||||
**********************************************************************/
|
||||
NearestNeighbor(const std::vector<pos_t>& pts, const distfun_t& dist,
|
||||
int bucket = 4) {
|
||||
Initialize(pts, dist, bucket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize or re-initialize NearestNeighbor.
|
||||
*
|
||||
* @param[in] pts a vector of points to include in the tree.
|
||||
* @param[in] dist the distance function object.
|
||||
* @param[in] bucket the size of the buckets at the leaf nodes; this must
|
||||
* lie in [0, 2 + 4*sizeof(dist_t)/sizeof(int)] (default 4).
|
||||
* @exception GeographicErr if the value of \e bucket is out of bounds or
|
||||
* the size of \e pts is too big for an int.
|
||||
* @exception std::bad_alloc if memory for the tree can't be allocated.
|
||||
*
|
||||
* See also the documentation on the constructor.
|
||||
*
|
||||
* If an exception is thrown, the state of the NearestNeighbor is
|
||||
* unchanged.
|
||||
**********************************************************************/
|
||||
void Initialize(const std::vector<pos_t>& pts, const distfun_t& dist,
|
||||
int bucket = 4) {
|
||||
static_assert(std::numeric_limits<dist_t>::is_signed,
|
||||
"dist_t must be a signed type");
|
||||
if (!( 0 <= bucket && bucket <= maxbucket ))
|
||||
throw GeographicLib::GeographicErr
|
||||
("bucket must lie in [0, 2 + 4*sizeof(dist_t)/sizeof(int)]");
|
||||
if (pts.size() > size_t(std::numeric_limits<int>::max()))
|
||||
throw GeographicLib::GeographicErr("pts array too big");
|
||||
// the pair contains distance+id
|
||||
std::vector<item> ids(pts.size());
|
||||
for (int k = int(ids.size()); k--;)
|
||||
ids[k] = std::make_pair(dist_t(0), k);
|
||||
int cost = 0;
|
||||
std::vector<Node> tree;
|
||||
init(pts, dist, bucket, tree, ids, cost,
|
||||
0, int(ids.size()), int(ids.size()/2));
|
||||
_tree.swap(tree);
|
||||
_numpoints = int(pts.size());
|
||||
_bucket = bucket;
|
||||
_mc = _sc = 0;
|
||||
_cost = cost; _c1 = _k = _cmax = 0;
|
||||
_cmin = std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the NearestNeighbor.
|
||||
*
|
||||
* @param[in] pts the vector of points used for initialization.
|
||||
* @param[in] dist the distance function object used for initialization.
|
||||
* @param[in] query the query point.
|
||||
* @param[out] ind a vector of indices to the closest points found.
|
||||
* @param[in] k the number of points to search for (default = 1).
|
||||
* @param[in] maxdist only return points with distances of \e maxdist or
|
||||
* less from \e query (default is the maximum \e dist_t).
|
||||
* @param[in] mindist only return points with distances of more than
|
||||
* \e mindist from \e query (default = −1).
|
||||
* @param[in] exhaustive whether to do an exhaustive search (default true).
|
||||
* @param[in] tol the tolerance on the results (default 0).
|
||||
* @return the distance to the closest point found (−1 if no points
|
||||
* are found).
|
||||
* @exception GeographicErr if \e pts has a different size from that used
|
||||
* to construct the object.
|
||||
*
|
||||
* The indices returned in \e ind are sorted by distance from \e query
|
||||
* (closest first).
|
||||
*
|
||||
* The simplest invocation is with just the 4 non-optional arguments. This
|
||||
* returns the closest distance and the index to the closest point in
|
||||
* <i>ind</i><sub>0</sub>. If there are several points equally close, then
|
||||
* <i>ind</i><sub>0</sub> gives the index of an arbirary one of them. If
|
||||
* there's no closest point (because the set of points is empty), then \e
|
||||
* ind is empty and −1 is returned.
|
||||
*
|
||||
* With \e exhaustive = true and \e tol = 0 (their default values), this
|
||||
* finds the indices of \e k closest neighbors to \e query whose distances
|
||||
* to \e query are in (\e mindist, \e maxdist]. If \e mindist and \e
|
||||
* maxdist have their default values, then these bounds have no effect. If
|
||||
* \e query is one of the points in the tree, then set \e mindist = 0 to
|
||||
* prevent this point (and other coincident points) from being returned.
|
||||
*
|
||||
* If \e exhaustive = false, exit as soon as \e k results satisfying the
|
||||
* distance criteria are found. If less than \e k results are returned
|
||||
* then the search was exhaustive even if \e exhaustive = false.
|
||||
*
|
||||
* If \e tol is positive, do an approximate search; in this case the
|
||||
* results are to be interpreted as follows: if the <i>k</i>'th distance is
|
||||
* \e dk, then all results with distances less than or equal \e dk −
|
||||
* \e tol are correct; all others are suspect — there may be other
|
||||
* closer results with distances greater or equal to \e dk − \e tol.
|
||||
* If less than \e k results are found, then the search is exact.
|
||||
*
|
||||
* \e mindist should be used to exclude a "small" neighborhood of the query
|
||||
* point (relative to the average spacing of the data). If \e mindist is
|
||||
* large, the efficiency of the search deteriorates.
|
||||
*
|
||||
* \note Only the shortest distance is returned (as as the function value).
|
||||
* The distances to other points (indexed by <i>ind</i><sub><i>j</i></sub>
|
||||
* for \e j > 0) can be found by invoking \e dist again.
|
||||
*
|
||||
* \warning The arguments \e pts and \e dist must be identical to those
|
||||
* used to initialize the NearestNeighbor; if not, this function will
|
||||
* return some meaningless result (however, if the size of \e pts is wrong,
|
||||
* this function throw an exception).
|
||||
*
|
||||
* \warning The query point cannot be a NaN or infinite because then the
|
||||
* metric conditions are violated.
|
||||
**********************************************************************/
|
||||
dist_t Search(const std::vector<pos_t>& pts, const distfun_t& dist,
|
||||
const pos_t& query,
|
||||
std::vector<int>& ind,
|
||||
int k = 1,
|
||||
dist_t maxdist = std::numeric_limits<dist_t>::max(),
|
||||
dist_t mindist = -1,
|
||||
bool exhaustive = true,
|
||||
dist_t tol = 0) const {
|
||||
if (_numpoints != int(pts.size()))
|
||||
throw GeographicLib::GeographicErr("pts array has wrong size");
|
||||
std::priority_queue<item> results;
|
||||
if (_numpoints > 0 && k > 0 && maxdist > mindist) {
|
||||
// distance to the kth closest point so far
|
||||
dist_t tau = maxdist;
|
||||
// first is negative of how far query is outside boundary of node
|
||||
// +1 if on boundary or inside
|
||||
// second is node index
|
||||
std::priority_queue<item> todo;
|
||||
todo.push(std::make_pair(dist_t(1), int(_tree.size()) - 1));
|
||||
int c = 0;
|
||||
while (!todo.empty()) {
|
||||
int n = todo.top().second;
|
||||
dist_t d = -todo.top().first;
|
||||
todo.pop();
|
||||
dist_t tau1 = tau - tol;
|
||||
// compare tau and d again since tau may have become smaller.
|
||||
if (!( n >= 0 && tau1 >= d )) continue;
|
||||
const Node& current = _tree[n];
|
||||
dist_t dst = 0; // to suppress warning about uninitialized variable
|
||||
bool exitflag = false, leaf = current.index < 0;
|
||||
for (int i = 0; i < (leaf ? _bucket : 1); ++i) {
|
||||
int index = leaf ? current.leaves[i] : current.index;
|
||||
if (index < 0) break;
|
||||
dst = dist(pts[index], query);
|
||||
++c;
|
||||
|
||||
if (dst > mindist && dst <= tau) {
|
||||
if (int(results.size()) == k) results.pop();
|
||||
results.push(std::make_pair(dst, index));
|
||||
if (int(results.size()) == k) {
|
||||
if (exhaustive)
|
||||
tau = results.top().first;
|
||||
else {
|
||||
exitflag = true;
|
||||
break;
|
||||
}
|
||||
if (tau <= tol) {
|
||||
exitflag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exitflag) break;
|
||||
|
||||
if (current.index < 0) continue;
|
||||
tau1 = tau - tol;
|
||||
for (int l = 0; l < 2; ++l) {
|
||||
if (current.data.child[l] >= 0 &&
|
||||
dst + current.data.upper[l] >= mindist) {
|
||||
if (dst < current.data.lower[l]) {
|
||||
d = current.data.lower[l] - dst;
|
||||
if (tau1 >= d)
|
||||
todo.push(std::make_pair(-d, current.data.child[l]));
|
||||
} else if (dst > current.data.upper[l]) {
|
||||
d = dst - current.data.upper[l];
|
||||
if (tau1 >= d)
|
||||
todo.push(std::make_pair(-d, current.data.child[l]));
|
||||
} else
|
||||
todo.push(std::make_pair(dist_t(1), current.data.child[l]));
|
||||
}
|
||||
}
|
||||
}
|
||||
++_k;
|
||||
_c1 += c;
|
||||
double omc = _mc;
|
||||
_mc += (c - omc) / _k;
|
||||
_sc += (c - omc) * (c - _mc);
|
||||
if (c > _cmax) _cmax = c;
|
||||
if (c < _cmin) _cmin = c;
|
||||
}
|
||||
|
||||
dist_t d = -1;
|
||||
ind.resize(results.size());
|
||||
|
||||
for (int i = int(ind.size()); i--;) {
|
||||
ind[i] = int(results.top().second);
|
||||
if (i == 0) d = results.top().first;
|
||||
results.pop();
|
||||
}
|
||||
return d;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the total number of points in the set.
|
||||
**********************************************************************/
|
||||
int NumPoints() const { return _numpoints; }
|
||||
|
||||
/**
|
||||
* Write the object to an I/O stream.
|
||||
*
|
||||
* @param[in,out] os the stream to write to.
|
||||
* @param[in] bin if true (the default) save in binary mode.
|
||||
* @exception std::bad_alloc if memory for the string representation of the
|
||||
* object can't be allocated.
|
||||
*
|
||||
* The counters tracking the statistics of searches are not saved; however
|
||||
* the initializtion cost is saved. The format of the binary saves is \e
|
||||
* not portable.
|
||||
*
|
||||
* \note <a href="https://www.boost.org/libs/serialization/doc">
|
||||
* Boost serialization</a> can also be used to save and restore a
|
||||
* NearestNeighbor object. This requires that the
|
||||
* GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION macro be defined.
|
||||
**********************************************************************/
|
||||
void Save(std::ostream& os, bool bin = true) const {
|
||||
int realspec = std::numeric_limits<dist_t>::digits *
|
||||
(std::numeric_limits<dist_t>::is_integer ? -1 : 1);
|
||||
if (bin) {
|
||||
char id[] = "NearestNeighbor_";
|
||||
os.write(id, 16);
|
||||
int buf[6];
|
||||
buf[0] = version;
|
||||
buf[1] = realspec;
|
||||
buf[2] = _bucket;
|
||||
buf[3] = _numpoints;
|
||||
buf[4] = int(_tree.size());
|
||||
buf[5] = _cost;
|
||||
os.write(reinterpret_cast<const char *>(buf), 6 * sizeof(int));
|
||||
for (int i = 0; i < int(_tree.size()); ++i) {
|
||||
const Node& node = _tree[i];
|
||||
os.write(reinterpret_cast<const char *>(&node.index), sizeof(int));
|
||||
if (node.index >= 0) {
|
||||
os.write(reinterpret_cast<const char *>(node.data.lower),
|
||||
2 * sizeof(dist_t));
|
||||
os.write(reinterpret_cast<const char *>(node.data.upper),
|
||||
2 * sizeof(dist_t));
|
||||
os.write(reinterpret_cast<const char *>(node.data.child),
|
||||
2 * sizeof(int));
|
||||
} else {
|
||||
os.write(reinterpret_cast<const char *>(node.leaves),
|
||||
_bucket * sizeof(int));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::stringstream ostring;
|
||||
// Ensure enough precision for type dist_t. With C++11, max_digits10
|
||||
// can be used instead.
|
||||
if (!std::numeric_limits<dist_t>::is_integer) {
|
||||
static const int prec
|
||||
= int(std::ceil(std::numeric_limits<dist_t>::digits *
|
||||
std::log10(2.0) + 1));
|
||||
ostring.precision(prec);
|
||||
}
|
||||
ostring << version << " " << realspec << " " << _bucket << " "
|
||||
<< _numpoints << " " << _tree.size() << " " << _cost;
|
||||
for (int i = 0; i < int(_tree.size()); ++i) {
|
||||
const Node& node = _tree[i];
|
||||
ostring << "\n" << node.index;
|
||||
if (node.index >= 0) {
|
||||
for (int l = 0; l < 2; ++l)
|
||||
ostring << " " << node.data.lower[l] << " " << node.data.upper[l]
|
||||
<< " " << node.data.child[l];
|
||||
} else {
|
||||
for (int l = 0; l < _bucket; ++l)
|
||||
ostring << " " << node.leaves[l];
|
||||
}
|
||||
}
|
||||
os << ostring.str();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the object from an I/O stream.
|
||||
*
|
||||
* @param[in,out] is the stream to read from
|
||||
* @param[in] bin if true (the default) load in binary mode.
|
||||
* @exception GeographicErr if the state read from \e is is illegal.
|
||||
* @exception std::bad_alloc if memory for the tree can't be allocated.
|
||||
*
|
||||
* The counters tracking the statistics of searches are reset by this
|
||||
* operation. Binary data must have been saved on a machine with the same
|
||||
* architecture. If an exception is thrown, the state of the
|
||||
* NearestNeighbor is unchanged.
|
||||
*
|
||||
* \note <a href="https://www.boost.org/libs/serialization/doc">
|
||||
* Boost serialization</a> can also be used to save and restore a
|
||||
* NearestNeighbor object. This requires that the
|
||||
* GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION macro be defined.
|
||||
*
|
||||
* \warning The same arguments \e pts and \e dist used for
|
||||
* initialization must be provided to the Search() function.
|
||||
**********************************************************************/
|
||||
void Load(std::istream& is, bool bin = true) {
|
||||
int version1, realspec, bucket, numpoints, treesize, cost;
|
||||
if (bin) {
|
||||
char id[17];
|
||||
is.read(id, 16);
|
||||
id[16] = '\0';
|
||||
if (!(std::strcmp(id, "NearestNeighbor_") == 0))
|
||||
throw GeographicLib::GeographicErr("Bad ID");
|
||||
is.read(reinterpret_cast<char *>(&version1), sizeof(int));
|
||||
is.read(reinterpret_cast<char *>(&realspec), sizeof(int));
|
||||
is.read(reinterpret_cast<char *>(&bucket), sizeof(int));
|
||||
is.read(reinterpret_cast<char *>(&numpoints), sizeof(int));
|
||||
is.read(reinterpret_cast<char *>(&treesize), sizeof(int));
|
||||
is.read(reinterpret_cast<char *>(&cost), sizeof(int));
|
||||
} else {
|
||||
if (!( is >> version1 >> realspec >> bucket >> numpoints >> treesize
|
||||
>> cost ))
|
||||
throw GeographicLib::GeographicErr("Bad header");
|
||||
}
|
||||
if (!( version1 == version ))
|
||||
throw GeographicLib::GeographicErr("Incompatible version");
|
||||
if (!( realspec == std::numeric_limits<dist_t>::digits *
|
||||
(std::numeric_limits<dist_t>::is_integer ? -1 : 1) ))
|
||||
throw GeographicLib::GeographicErr("Different dist_t types");
|
||||
if (!( 0 <= bucket && bucket <= maxbucket ))
|
||||
throw GeographicLib::GeographicErr("Bad bucket size");
|
||||
if (!( 0 <= treesize && treesize <= numpoints ))
|
||||
throw
|
||||
GeographicLib::GeographicErr("Bad number of points or tree size");
|
||||
if (!( 0 <= cost ))
|
||||
throw GeographicLib::GeographicErr("Bad value for cost");
|
||||
std::vector<Node> tree;
|
||||
tree.reserve(treesize);
|
||||
for (int i = 0; i < treesize; ++i) {
|
||||
Node node;
|
||||
if (bin) {
|
||||
is.read(reinterpret_cast<char *>(&node.index), sizeof(int));
|
||||
if (node.index >= 0) {
|
||||
is.read(reinterpret_cast<char *>(node.data.lower),
|
||||
2 * sizeof(dist_t));
|
||||
is.read(reinterpret_cast<char *>(node.data.upper),
|
||||
2 * sizeof(dist_t));
|
||||
is.read(reinterpret_cast<char *>(node.data.child),
|
||||
2 * sizeof(int));
|
||||
} else {
|
||||
is.read(reinterpret_cast<char *>(node.leaves),
|
||||
bucket * sizeof(int));
|
||||
for (int l = bucket; l < maxbucket; ++l)
|
||||
node.leaves[l] = 0;
|
||||
}
|
||||
} else {
|
||||
if (!( is >> node.index ))
|
||||
throw GeographicLib::GeographicErr("Bad index");
|
||||
if (node.index >= 0) {
|
||||
for (int l = 0; l < 2; ++l) {
|
||||
if (!( is >> node.data.lower[l] >> node.data.upper[l]
|
||||
>> node.data.child[l] ))
|
||||
throw GeographicLib::GeographicErr("Bad node data");
|
||||
}
|
||||
} else {
|
||||
// Must be at least one valid leaf followed by a sequence end
|
||||
// markers (-1).
|
||||
for (int l = 0; l < bucket; ++l) {
|
||||
if (!( is >> node.leaves[l] ))
|
||||
throw GeographicLib::GeographicErr("Bad leaf data");
|
||||
}
|
||||
for (int l = bucket; l < maxbucket; ++l)
|
||||
node.leaves[l] = 0;
|
||||
}
|
||||
}
|
||||
node.Check(numpoints, treesize, bucket);
|
||||
tree.push_back(node);
|
||||
}
|
||||
_tree.swap(tree);
|
||||
_numpoints = numpoints;
|
||||
_bucket = bucket;
|
||||
_mc = _sc = 0;
|
||||
_cost = cost; _c1 = _k = _cmax = 0;
|
||||
_cmin = std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the object to stream \e os as text.
|
||||
*
|
||||
* @param[in,out] os the output stream.
|
||||
* @param[in] t the NearestNeighbor object to be saved.
|
||||
* @exception std::bad_alloc if memory for the string representation of the
|
||||
* object can't be allocated.
|
||||
**********************************************************************/
|
||||
friend std::ostream& operator<<(std::ostream& os, const NearestNeighbor& t)
|
||||
{ t.Save(os, false); return os; }
|
||||
|
||||
/**
|
||||
* Read the object from stream \e is as text.
|
||||
*
|
||||
* @param[in,out] is the input stream.
|
||||
* @param[out] t the NearestNeighbor object to be loaded.
|
||||
* @exception GeographicErr if the state read from \e is is illegal.
|
||||
* @exception std::bad_alloc if memory for the tree can't be allocated.
|
||||
**********************************************************************/
|
||||
friend std::istream& operator>>(std::istream& is, NearestNeighbor& t)
|
||||
{ t.Load(is, false); return is; }
|
||||
|
||||
/**
|
||||
* Swap with another NearestNeighbor object.
|
||||
*
|
||||
* @param[in,out] t the NearestNeighbor object to swap with.
|
||||
**********************************************************************/
|
||||
void swap(NearestNeighbor& t) {
|
||||
std::swap(_numpoints, t._numpoints);
|
||||
std::swap(_bucket, t._bucket);
|
||||
std::swap(_cost, t._cost);
|
||||
_tree.swap(t._tree);
|
||||
std::swap(_mc, t._mc);
|
||||
std::swap(_sc, t._sc);
|
||||
std::swap(_c1, t._c1);
|
||||
std::swap(_k, t._k);
|
||||
std::swap(_cmin, t._cmin);
|
||||
std::swap(_cmax, t._cmax);
|
||||
}
|
||||
|
||||
/**
|
||||
* The accumulated statistics on the searches so far.
|
||||
*
|
||||
* @param[out] setupcost the cost of initializing the NearestNeighbor.
|
||||
* @param[out] numsearches the number of calls to Search().
|
||||
* @param[out] searchcost the total cost of the calls to Search().
|
||||
* @param[out] mincost the minimum cost of a Search().
|
||||
* @param[out] maxcost the maximum cost of a Search().
|
||||
* @param[out] mean the mean cost of a Search().
|
||||
* @param[out] sd the standard deviation in the cost of a Search().
|
||||
*
|
||||
* Here "cost" measures the number of distance calculations needed. Note
|
||||
* that the accumulation of statistics is \e not thread safe.
|
||||
**********************************************************************/
|
||||
void Statistics(int& setupcost, int& numsearches, int& searchcost,
|
||||
int& mincost, int& maxcost,
|
||||
double& mean, double& sd) const {
|
||||
setupcost = _cost; numsearches = _k; searchcost = _c1;
|
||||
mincost = _cmin; maxcost = _cmax;
|
||||
mean = _mc; sd = std::sqrt(_sc / (_k - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the counters for the accumulated statistics on the searches so
|
||||
* far.
|
||||
**********************************************************************/
|
||||
void ResetStatistics() const {
|
||||
_mc = _sc = 0;
|
||||
_c1 = _k = _cmax = 0;
|
||||
_cmin = std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
private:
|
||||
// Package up a dist_t and an int. We will want to sort on the dist_t so
|
||||
// put it first.
|
||||
typedef std::pair<dist_t, int> item;
|
||||
// \cond SKIP
|
||||
class Node {
|
||||
public:
|
||||
struct bounds {
|
||||
dist_t lower[2], upper[2]; // bounds on inner/outer distances
|
||||
int child[2];
|
||||
};
|
||||
union {
|
||||
bounds data;
|
||||
int leaves[maxbucket];
|
||||
};
|
||||
int index;
|
||||
|
||||
Node()
|
||||
: index(-1)
|
||||
{
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
data.lower[i] = data.upper[i] = 0;
|
||||
data.child[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check on a Node
|
||||
void Check(int numpoints, int treesize, int bucket) const {
|
||||
if (!( -1 <= index && index < numpoints ))
|
||||
throw GeographicLib::GeographicErr("Bad index");
|
||||
if (index >= 0) {
|
||||
if (!( -1 <= data.child[0] && data.child[0] < treesize &&
|
||||
-1 <= data.child[1] && data.child[1] < treesize ))
|
||||
throw GeographicLib::GeographicErr("Bad child pointers");
|
||||
if (!( 0 <= data.lower[0] && data.lower[0] <= data.upper[0] &&
|
||||
data.upper[0] <= data.lower[1] &&
|
||||
data.lower[1] <= data.upper[1] ))
|
||||
throw GeographicLib::GeographicErr("Bad bounds");
|
||||
} else {
|
||||
// Must be at least one valid leaf followed by a sequence end markers
|
||||
// (-1).
|
||||
bool start = true;
|
||||
for (int l = 0; l < bucket; ++l) {
|
||||
if (!( (start ?
|
||||
((l == 0 ? 0 : -1) <= leaves[l] && leaves[l] < numpoints) :
|
||||
leaves[l] == -1) ))
|
||||
throw GeographicLib::GeographicErr("Bad leaf data");
|
||||
start = leaves[l] >= 0;
|
||||
}
|
||||
for (int l = bucket; l < maxbucket; ++l) {
|
||||
if (leaves[l] != 0)
|
||||
throw GeographicLib::GeographicErr("Bad leaf data");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION) && \
|
||||
GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION
|
||||
friend class boost::serialization::access;
|
||||
template<class Archive>
|
||||
void save(Archive& ar, const unsigned int) const {
|
||||
ar & boost::serialization::make_nvp("index", index);
|
||||
if (index < 0)
|
||||
ar & boost::serialization::make_nvp("leaves", leaves);
|
||||
else
|
||||
ar & boost::serialization::make_nvp("lower", data.lower)
|
||||
& boost::serialization::make_nvp("upper", data.upper)
|
||||
& boost::serialization::make_nvp("child", data.child);
|
||||
}
|
||||
template<class Archive>
|
||||
void load(Archive& ar, const unsigned int) {
|
||||
ar & boost::serialization::make_nvp("index", index);
|
||||
if (index < 0)
|
||||
ar & boost::serialization::make_nvp("leaves", leaves);
|
||||
else
|
||||
ar & boost::serialization::make_nvp("lower", data.lower)
|
||||
& boost::serialization::make_nvp("upper", data.upper)
|
||||
& boost::serialization::make_nvp("child", data.child);
|
||||
}
|
||||
template<class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version)
|
||||
{ boost::serialization::split_member(ar, *this, file_version); }
|
||||
#endif
|
||||
};
|
||||
// \endcond
|
||||
#if defined(GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION) && \
|
||||
GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION
|
||||
friend class boost::serialization::access;
|
||||
template<class Archive> void save(Archive& ar, const unsigned) const {
|
||||
int realspec = std::numeric_limits<dist_t>::digits *
|
||||
(std::numeric_limits<dist_t>::is_integer ? -1 : 1);
|
||||
// Need to use version1, otherwise load error in debug mode on Linux:
|
||||
// undefined reference to GeographicLib::NearestNeighbor<...>::version.
|
||||
int version1 = version;
|
||||
ar & boost::serialization::make_nvp("version", version1)
|
||||
& boost::serialization::make_nvp("realspec", realspec)
|
||||
& boost::serialization::make_nvp("bucket", _bucket)
|
||||
& boost::serialization::make_nvp("numpoints", _numpoints)
|
||||
& boost::serialization::make_nvp("cost", _cost)
|
||||
& boost::serialization::make_nvp("tree", _tree);
|
||||
}
|
||||
template<class Archive> void load(Archive& ar, const unsigned) {
|
||||
int version1, realspec, bucket, numpoints, cost;
|
||||
ar & boost::serialization::make_nvp("version", version1);
|
||||
if (version1 != version)
|
||||
throw GeographicLib::GeographicErr("Incompatible version");
|
||||
std::vector<Node> tree;
|
||||
ar & boost::serialization::make_nvp("realspec", realspec);
|
||||
if (!( realspec == std::numeric_limits<dist_t>::digits *
|
||||
(std::numeric_limits<dist_t>::is_integer ? -1 : 1) ))
|
||||
throw GeographicLib::GeographicErr("Different dist_t types");
|
||||
ar & boost::serialization::make_nvp("bucket", bucket);
|
||||
if (!( 0 <= bucket && bucket <= maxbucket ))
|
||||
throw GeographicLib::GeographicErr("Bad bucket size");
|
||||
ar & boost::serialization::make_nvp("numpoints", numpoints)
|
||||
& boost::serialization::make_nvp("cost", cost)
|
||||
& boost::serialization::make_nvp("tree", tree);
|
||||
if (!( 0 <= int(tree.size()) && int(tree.size()) <= numpoints ))
|
||||
throw
|
||||
GeographicLib::GeographicErr("Bad number of points or tree size");
|
||||
for (int i = 0; i < int(tree.size()); ++i)
|
||||
tree[i].Check(numpoints, int(tree.size()), bucket);
|
||||
_tree.swap(tree);
|
||||
_numpoints = numpoints;
|
||||
_bucket = bucket;
|
||||
_mc = _sc = 0;
|
||||
_cost = cost; _c1 = _k = _cmax = 0;
|
||||
_cmin = std::numeric_limits<int>::max();
|
||||
}
|
||||
template<class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version)
|
||||
{ boost::serialization::split_member(ar, *this, file_version); }
|
||||
#endif
|
||||
|
||||
int _numpoints, _bucket, _cost;
|
||||
std::vector<Node> _tree;
|
||||
// Counters to track stastistics on the cost of searches
|
||||
mutable double _mc, _sc;
|
||||
mutable int _c1, _k, _cmin, _cmax;
|
||||
|
||||
int init(const std::vector<pos_t>& pts, const distfun_t& dist, int bucket,
|
||||
std::vector<Node>& tree, std::vector<item>& ids, int& cost,
|
||||
int l, int u, int vp) {
|
||||
|
||||
if (u == l)
|
||||
return -1;
|
||||
Node node;
|
||||
|
||||
if (u - l > (bucket == 0 ? 1 : bucket)) {
|
||||
|
||||
// choose a vantage point and move it to the start
|
||||
int i = vp;
|
||||
std::swap(ids[l], ids[i]);
|
||||
|
||||
int m = (u + l + 1) / 2;
|
||||
|
||||
for (int k = l + 1; k < u; ++k) {
|
||||
ids[k].first = dist(pts[ids[l].second], pts[ids[k].second]);
|
||||
++cost;
|
||||
}
|
||||
// partition around the median distance
|
||||
std::nth_element(ids.begin() + l + 1,
|
||||
ids.begin() + m,
|
||||
ids.begin() + u);
|
||||
node.index = ids[l].second;
|
||||
if (m > l + 1) { // node.child[0] is possibly empty
|
||||
typename std::vector<item>::iterator
|
||||
t = std::min_element(ids.begin() + l + 1, ids.begin() + m);
|
||||
node.data.lower[0] = t->first;
|
||||
t = std::max_element(ids.begin() + l + 1, ids.begin() + m);
|
||||
node.data.upper[0] = t->first;
|
||||
// Use point with max distance as vantage point; this point act as a
|
||||
// "corner" point and leads to a good partition.
|
||||
node.data.child[0] = init(pts, dist, bucket, tree, ids, cost,
|
||||
l + 1, m, int(t - ids.begin()));
|
||||
}
|
||||
typename std::vector<item>::iterator
|
||||
t = std::max_element(ids.begin() + m, ids.begin() + u);
|
||||
node.data.lower[1] = ids[m].first;
|
||||
node.data.upper[1] = t->first;
|
||||
// Use point with max distance as vantage point here too
|
||||
node.data.child[1] = init(pts, dist, bucket, tree, ids, cost,
|
||||
m, u, int(t - ids.begin()));
|
||||
} else {
|
||||
if (bucket == 0)
|
||||
node.index = ids[l].second;
|
||||
else {
|
||||
node.index = -1;
|
||||
// Sort the bucket entries so that the tree is independent of the
|
||||
// implementation of nth_element.
|
||||
std::sort(ids.begin() + l, ids.begin() + u);
|
||||
for (int i = l; i < u; ++i)
|
||||
node.leaves[i-l] = ids[i].second;
|
||||
for (int i = u - l; i < bucket; ++i)
|
||||
node.leaves[i] = -1;
|
||||
for (int i = bucket; i < maxbucket; ++i)
|
||||
node.leaves[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
tree.push_back(node);
|
||||
return int(tree.size()) - 1;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
namespace std {
|
||||
|
||||
/**
|
||||
* Swap two GeographicLib::NearestNeighbor objects.
|
||||
*
|
||||
* @tparam dist_t the type used for measuring distances.
|
||||
* @tparam pos_t the type for specifying the positions of points.
|
||||
* @tparam distfun_t the type for a function object which calculates
|
||||
* distances between points.
|
||||
* @param[in,out] a the first GeographicLib::NearestNeighbor to swap.
|
||||
* @param[in,out] b the second GeographicLib::NearestNeighbor to swap.
|
||||
**********************************************************************/
|
||||
template<typename dist_t, typename pos_t, class distfun_t>
|
||||
void swap(GeographicLib::NearestNeighbor<dist_t, pos_t, distfun_t>& a,
|
||||
GeographicLib::NearestNeighbor<dist_t, pos_t, distfun_t>& b) {
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // GEOGRAPHICLIB_NEARESTNEIGHBOR_HPP
|
||||
+394
@@ -0,0 +1,394 @@
|
||||
/**
|
||||
* \file NormalGravity.hpp
|
||||
* \brief Header for GeographicLib::NormalGravity class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2011-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_NORMALGRAVITY_HPP)
|
||||
#define GEOGRAPHICLIB_NORMALGRAVITY_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/Geocentric.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief The normal gravity of the earth
|
||||
*
|
||||
* "Normal" gravity refers to an idealization of the earth which is modeled
|
||||
* as an rotating ellipsoid. The eccentricity of the ellipsoid, the rotation
|
||||
* speed, and the distribution of mass within the ellipsoid are such that the
|
||||
* ellipsoid is a "level ellipsoid", a surface of constant potential
|
||||
* (gravitational plus centrifugal). The acceleration due to gravity is
|
||||
* therefore perpendicular to the surface of the ellipsoid.
|
||||
*
|
||||
* Because the distribution of mass within the ellipsoid is unspecified, only
|
||||
* the potential exterior to the ellipsoid is well defined. In this class,
|
||||
* the mass is assumed to be to concentrated on a "focal disc" of radius,
|
||||
* (<i>a</i><sup>2</sup> − <i>b</i><sup>2</sup>)<sup>1/2</sup>, where
|
||||
* \e a is the equatorial radius of the ellipsoid and \e b is its polar
|
||||
* semiaxis. In the case of an oblate ellipsoid, the mass is concentrated
|
||||
* on a "focal rod" of length 2(<i>b</i><sup>2</sup> −
|
||||
* <i>a</i><sup>2</sup>)<sup>1/2</sup>. As a result the potential is well
|
||||
* defined everywhere.
|
||||
*
|
||||
* There is a closed solution to this problem which is implemented here.
|
||||
* Series "approximations" are only used to evaluate certain combinations of
|
||||
* elementary functions where use of the closed expression results in a loss
|
||||
* of accuracy for small arguments due to cancellation of the leading terms.
|
||||
* However these series include sufficient terms to give full machine
|
||||
* precision.
|
||||
*
|
||||
* Although the formulation used in this class applies to ellipsoids with
|
||||
* arbitrary flattening, in practice, its use should be limited to about
|
||||
* <i>b</i>/\e a ∈ [0.01, 100] or \e f ∈ [−99, 0.99].
|
||||
*
|
||||
* Definitions:
|
||||
* - <i>V</i><sub>0</sub>, the gravitational contribution to the normal
|
||||
* potential;
|
||||
* - Φ, the rotational contribution to the normal potential;
|
||||
* - \e U = <i>V</i><sub>0</sub> + Φ, the total potential;
|
||||
* - <b>Γ</b> = ∇<i>V</i><sub>0</sub>, the acceleration due to
|
||||
* mass of the earth;
|
||||
* - <b>f</b> = ∇Φ, the centrifugal acceleration;
|
||||
* - <b>γ</b> = ∇\e U = <b>Γ</b> + <b>f</b>, the normal
|
||||
* acceleration;
|
||||
* - \e X, \e Y, \e Z, geocentric coordinates;
|
||||
* - \e x, \e y, \e z, local cartesian coordinates used to denote the east,
|
||||
* north and up directions.
|
||||
*
|
||||
* References:
|
||||
* - C. Somigliana, Teoria generale del campo gravitazionale dell'ellissoide
|
||||
* di rotazione, Mem. Soc. Astron. Ital, <b>4</b>, 541--599 (1929).
|
||||
* - W. A. Heiskanen and H. Moritz, Physical Geodesy (Freeman, San
|
||||
* Francisco, 1967), Secs. 1-19, 2-7, 2-8 (2-9, 2-10), 6-2 (6-3).
|
||||
* https://archive.org/details/HeiskanenMoritz1967PhysicalGeodesy
|
||||
* - B. Hofmann-Wellenhof, H. Moritz, Physical Geodesy (Second edition,
|
||||
* Springer, 2006). https://doi.org/10.1007/978-3-211-33545-1
|
||||
* - H. Moritz, Geodetic Reference System 1980, J. Geodesy 54(3), 395-405
|
||||
* (1980). https://doi.org/10.1007/BF02521480
|
||||
*
|
||||
* For more information on normal gravity see \ref normalgravity.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-NormalGravity.cpp
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT NormalGravity {
|
||||
private:
|
||||
static const int maxit_ = 20;
|
||||
typedef Math::real real;
|
||||
friend class GravityModel;
|
||||
real _a, _gGM, _omega, _f, _jJ2, _omega2, _aomega2;
|
||||
real _e2, _ep2, _b, _eE, _uU0, _gammae, _gammap, _qQ0, _k, _fstar;
|
||||
Geocentric _earth;
|
||||
static real atanzz(real x, bool alt) {
|
||||
// This routine obeys the identity
|
||||
// atanzz(x, alt) = atanzz(-x/(1+x), !alt)
|
||||
//
|
||||
// Require x >= -1. Best to call with alt, s.t. x >= 0; this results in
|
||||
// a call to atan, instead of asin, or to asinh, instead of atanh.
|
||||
using std::sqrt, std::fabs, std::atan, std::asin, std::asinh, std::atanh;
|
||||
real z = sqrt(fabs(x));
|
||||
return x == 0 ? 1 :
|
||||
(alt ?
|
||||
(!(x < 0) ? asinh(z) : asin(z)) / sqrt(fabs(x) / (1 + x)) :
|
||||
(!(x < 0) ? atan(z) : atanh(z)) / z);
|
||||
}
|
||||
static real atan7series(real x);
|
||||
static real atan5series(real x);
|
||||
static real Qf(real x, bool alt);
|
||||
static real Hf(real x, bool alt);
|
||||
static real QH3f(real x, bool alt);
|
||||
real Jn(int n) const;
|
||||
void Initialize(real a, real GM, real omega, real f_J2, bool geometricp);
|
||||
public:
|
||||
|
||||
/** \name Setting up the normal gravity
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Constructor for the normal gravity.
|
||||
*
|
||||
* @param[in] a equatorial radius (meters).
|
||||
* @param[in] GM mass constant of the ellipsoid
|
||||
* (meters<sup>3</sup>/seconds<sup>2</sup>); this is the product of \e G
|
||||
* the gravitational constant and \e M the mass of the earth (usually
|
||||
* including the mass of the earth's atmosphere).
|
||||
* @param[in] omega the angular velocity (rad s<sup>−1</sup>).
|
||||
* @param[in] f_J2 either the flattening of the ellipsoid \e f or the
|
||||
* the dynamical form factor \e J2.
|
||||
* @param[out] geometricp if true (the default), then \e f_J2 denotes the
|
||||
* flattening, else it denotes the dynamical form factor \e J2.
|
||||
* @exception if \e a is not positive or if the other parameters do not
|
||||
* obey the restrictions given below.
|
||||
*
|
||||
* The shape of the ellipsoid can be given in one of two ways:
|
||||
* - geometrically (\e geomtricp = true), the ellipsoid is defined by the
|
||||
* flattening \e f = (\e a − \e b) / \e a, where \e a and \e b are
|
||||
* the equatorial radius and the polar semiaxis. The parameters should
|
||||
* obey \e a > 0, \e f < 1. There are no restrictions on \e GM or
|
||||
* \e omega, in particular, \e GM need not be positive.
|
||||
* - physically (\e geometricp = false), the ellipsoid is defined by the
|
||||
* dynamical form factor <i>J</i><sub>2</sub> = (\e C − \e A) /
|
||||
* <i>Ma</i><sup>2</sup>, where \e A and \e C are the equatorial and
|
||||
* polar moments of inertia and \e M is the mass of the earth. The
|
||||
* parameters should obey \e a > 0, \e GM > 0 and \e J2 < 1/3
|
||||
* − (<i>omega</i><sup>2</sup><i>a</i><sup>3</sup>/<i>GM</i>)
|
||||
* 8/(45π). There is no restriction on \e omega.
|
||||
**********************************************************************/
|
||||
NormalGravity(real a, real GM, real omega, real f_J2,
|
||||
bool geometricp = true);
|
||||
|
||||
/**
|
||||
* A default constructor for the normal gravity. This sets up an
|
||||
* uninitialized object and is used by GravityModel which constructs this
|
||||
* object before it has read in the parameters for the reference ellipsoid.
|
||||
**********************************************************************/
|
||||
NormalGravity() : _a(-1) {}
|
||||
///@}
|
||||
|
||||
/** \name Compute the gravity
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Evaluate the gravity on the surface of the ellipsoid.
|
||||
*
|
||||
* @param[in] lat the geographic latitude (degrees).
|
||||
* @return γ the acceleration due to gravity, positive downwards
|
||||
* (m s<sup>−2</sup>).
|
||||
*
|
||||
* Due to the axial symmetry of the ellipsoid, the result is independent of
|
||||
* the value of the longitude. This acceleration is perpendicular to the
|
||||
* surface of the ellipsoid. It includes the effects of the earth's
|
||||
* rotation.
|
||||
**********************************************************************/
|
||||
Math::real SurfaceGravity(real lat) const;
|
||||
|
||||
/**
|
||||
* Evaluate the gravity at an arbitrary point above (or below) the
|
||||
* ellipsoid.
|
||||
*
|
||||
* @param[in] lat the geographic latitude (degrees).
|
||||
* @param[in] h the height above the ellipsoid (meters).
|
||||
* @param[out] gammay the northerly component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] gammaz the upward component of the acceleration
|
||||
* (m s<sup>−2</sup>); this is usually negative.
|
||||
* @return \e U the corresponding normal potential
|
||||
* (m<sup>2</sup> s<sup>−2</sup>).
|
||||
*
|
||||
* Due to the axial symmetry of the ellipsoid, the result is independent of
|
||||
* the value of the longitude and the easterly component of the
|
||||
* acceleration vanishes, \e gammax = 0. The function includes the effects
|
||||
* of the earth's rotation. When \e h = 0, this function gives \e gammay =
|
||||
* 0 and the returned value matches that of NormalGravity::SurfaceGravity.
|
||||
**********************************************************************/
|
||||
Math::real Gravity(real lat, real h, real& gammay, real& gammaz)
|
||||
const;
|
||||
|
||||
/**
|
||||
* Evaluate the components of the acceleration due to gravity and the
|
||||
* centrifugal acceleration in geocentric coordinates.
|
||||
*
|
||||
* @param[in] X geocentric coordinate of point (meters).
|
||||
* @param[in] Y geocentric coordinate of point (meters).
|
||||
* @param[in] Z geocentric coordinate of point (meters).
|
||||
* @param[out] gammaX the \e X component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] gammaY the \e Y component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] gammaZ the \e Z component of the acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @return \e U = <i>V</i><sub>0</sub> + Φ the sum of the
|
||||
* gravitational and centrifugal potentials
|
||||
* (m<sup>2</sup> s<sup>−2</sup>).
|
||||
*
|
||||
* The acceleration given by <b>γ</b> = ∇\e U =
|
||||
* ∇<i>V</i><sub>0</sub> + ∇Φ = <b>Γ</b> + <b>f</b>.
|
||||
**********************************************************************/
|
||||
Math::real U(real X, real Y, real Z,
|
||||
real& gammaX, real& gammaY, real& gammaZ) const;
|
||||
|
||||
/**
|
||||
* Evaluate the components of the acceleration due to the gravitational
|
||||
* force in geocentric coordinates.
|
||||
*
|
||||
* @param[in] X geocentric coordinate of point (meters).
|
||||
* @param[in] Y geocentric coordinate of point (meters).
|
||||
* @param[in] Z geocentric coordinate of point (meters).
|
||||
* @param[out] GammaX the \e X component of the acceleration due to the
|
||||
* gravitational force (m s<sup>−2</sup>).
|
||||
* @param[out] GammaY the \e Y component of the acceleration due to the
|
||||
* @param[out] GammaZ the \e Z component of the acceleration due to the
|
||||
* gravitational force (m s<sup>−2</sup>).
|
||||
* @return <i>V</i><sub>0</sub> the gravitational potential
|
||||
* (m<sup>2</sup> s<sup>−2</sup>).
|
||||
*
|
||||
* This function excludes the centrifugal acceleration and is appropriate
|
||||
* to use for space applications. In terrestrial applications, the
|
||||
* function NormalGravity::U (which includes this effect) should usually be
|
||||
* used.
|
||||
**********************************************************************/
|
||||
Math::real V0(real X, real Y, real Z,
|
||||
real& GammaX, real& GammaY, real& GammaZ) const;
|
||||
|
||||
/**
|
||||
* Evaluate the centrifugal acceleration in geocentric coordinates.
|
||||
*
|
||||
* @param[in] X geocentric coordinate of point (meters).
|
||||
* @param[in] Y geocentric coordinate of point (meters).
|
||||
* @param[out] fX the \e X component of the centrifugal acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @param[out] fY the \e Y component of the centrifugal acceleration
|
||||
* (m s<sup>−2</sup>).
|
||||
* @return Φ the centrifugal potential (m<sup>2</sup>
|
||||
* s<sup>−2</sup>).
|
||||
*
|
||||
* Φ is independent of \e Z, thus \e fZ = 0. This function
|
||||
* NormalGravity::U sums the results of NormalGravity::V0 and
|
||||
* NormalGravity::Phi.
|
||||
**********************************************************************/
|
||||
Math::real Phi(real X, real Y, real& fX, real& fY) const;
|
||||
///@}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return true if the object has been initialized.
|
||||
**********************************************************************/
|
||||
bool Init() const { return _a > 0; }
|
||||
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const
|
||||
{ return Init() ? _a : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return \e GM the mass constant of the ellipsoid
|
||||
* (m<sup>3</sup> s<sup>−2</sup>). This is the value used in the
|
||||
* constructor.
|
||||
**********************************************************************/
|
||||
Math::real MassConstant() const
|
||||
{ return Init() ? _gGM : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return <i>J</i><sub><i>n</i></sub> the dynamical form factors of the
|
||||
* ellipsoid.
|
||||
*
|
||||
* If \e n = 2 (the default), this is the value of <i>J</i><sub>2</sub>
|
||||
* used in the constructor. Otherwise it is the zonal coefficient of the
|
||||
* Legendre harmonic sum of the normal gravitational potential. Note that
|
||||
* <i>J</i><sub><i>n</i></sub> = 0 if \e n is odd. In most gravity
|
||||
* applications, fully normalized Legendre functions are used and the
|
||||
* corresponding coefficient is <i>C</i><sub><i>n</i>0</sub> =
|
||||
* −<i>J</i><sub><i>n</i></sub> / sqrt(2 \e n + 1).
|
||||
**********************************************************************/
|
||||
Math::real DynamicalFormFactor(int n = 2) const
|
||||
{ return Init() ? ( n == 2 ? _jJ2 : Jn(n)) : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return ω the angular velocity of the ellipsoid (rad
|
||||
* s<sup>−1</sup>). This is the value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real AngularVelocity() const
|
||||
{ return Init() ? _omega : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return <i>f</i> the flattening of the ellipsoid (\e a − \e b)/\e
|
||||
* a.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const
|
||||
{ return Init() ? _f : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return γ<sub>e</sub> the normal gravity at equator (m
|
||||
* s<sup>−2</sup>).
|
||||
**********************************************************************/
|
||||
Math::real EquatorialGravity() const
|
||||
{ return Init() ? _gammae : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return γ<sub>p</sub> the normal gravity at poles (m
|
||||
* s<sup>−2</sup>).
|
||||
**********************************************************************/
|
||||
Math::real PolarGravity() const
|
||||
{ return Init() ? _gammap : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return <i>f*</i> the gravity flattening (γ<sub>p</sub> −
|
||||
* γ<sub>e</sub>) / γ<sub>e</sub>.
|
||||
**********************************************************************/
|
||||
Math::real GravityFlattening() const
|
||||
{ return Init() ? _fstar : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return <i>U</i><sub>0</sub> the constant normal potential for the
|
||||
* surface of the ellipsoid (m<sup>2</sup> s<sup>−2</sup>).
|
||||
**********************************************************************/
|
||||
Math::real SurfacePotential() const
|
||||
{ return Init() ? _uU0 : Math::NaN(); }
|
||||
|
||||
/**
|
||||
* @return the Geocentric object used by this instance.
|
||||
**********************************************************************/
|
||||
const Geocentric& Earth() const { return _earth; }
|
||||
///@}
|
||||
|
||||
/**
|
||||
* A global instantiation of NormalGravity for the WGS84 ellipsoid.
|
||||
**********************************************************************/
|
||||
static const NormalGravity& WGS84();
|
||||
|
||||
/**
|
||||
* A global instantiation of NormalGravity for the GRS80 ellipsoid.
|
||||
**********************************************************************/
|
||||
static const NormalGravity& GRS80();
|
||||
|
||||
/**
|
||||
* Compute the flattening from the dynamical form factor.
|
||||
*
|
||||
* @param[in] a equatorial radius (meters).
|
||||
* @param[in] GM mass constant of the ellipsoid
|
||||
* (meters<sup>3</sup>/seconds<sup>2</sup>); this is the product of \e G
|
||||
* the gravitational constant and \e M the mass of the earth (usually
|
||||
* including the mass of the earth's atmosphere).
|
||||
* @param[in] omega the angular velocity (rad s<sup>−1</sup>).
|
||||
* @param[in] J2 the dynamical form factor.
|
||||
* @return \e f the flattening of the ellipsoid.
|
||||
*
|
||||
* This routine requires \e a > 0, \e GM > 0, \e J2 < 1/3 −
|
||||
* <i>omega</i><sup>2</sup><i>a</i><sup>3</sup>/<i>GM</i> 8/(45π). A
|
||||
* NaN is returned if these conditions do not hold. The restriction to
|
||||
* positive \e GM is made because for negative \e GM two solutions are
|
||||
* possible.
|
||||
**********************************************************************/
|
||||
static Math::real J2ToFlattening(real a, real GM, real omega, real J2);
|
||||
|
||||
/**
|
||||
* Compute the dynamical form factor from the flattening.
|
||||
*
|
||||
* @param[in] a equatorial radius (meters).
|
||||
* @param[in] GM mass constant of the ellipsoid
|
||||
* (meters<sup>3</sup>/seconds<sup>2</sup>); this is the product of \e G
|
||||
* the gravitational constant and \e M the mass of the earth (usually
|
||||
* including the mass of the earth's atmosphere).
|
||||
* @param[in] omega the angular velocity (rad s<sup>−1</sup>).
|
||||
* @param[in] f the flattening of the ellipsoid.
|
||||
* @return \e J2 the dynamical form factor.
|
||||
*
|
||||
* This routine requires \e a > 0, \e GM ≠ 0, \e f < 1. The
|
||||
* values of these parameters are not checked.
|
||||
**********************************************************************/
|
||||
static Math::real FlatteningToJ2(real a, real GM, real omega, real f);
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_NORMALGRAVITY_HPP
|
||||
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* \file OSGB.hpp
|
||||
* \brief Header for GeographicLib::OSGB class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2010-2024) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_OSGB_HPP)
|
||||
#define GEOGRAPHICLIB_OSGB_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/TransverseMercator.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Ordnance Survey grid system for Great Britain
|
||||
*
|
||||
* The class implements the coordinate system used by the Ordnance Survey for
|
||||
* maps of Great Britain and conversions to the grid reference system.
|
||||
*
|
||||
* See
|
||||
* - <a href="https://www.ordnancesurvey.co.uk/documents/resources/guide-coordinate-systems-great-britain.pdf">
|
||||
* A guide to coordinate systems in Great Britain</a>
|
||||
* - <a href="https://www.ordnancesurvey.co.uk/documents/resources/guide-to-nationalgrid.pdf">
|
||||
* Using the National Grid</a>
|
||||
*
|
||||
* \warning the latitudes and longitudes for the Ordnance Survey grid
|
||||
* system do not use the WGS84 datum. Do not use the values returned by this
|
||||
* class in the UTMUPS, MGRS, or Geoid classes without first converting the
|
||||
* datum (and vice versa).
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-OSGB.cpp
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT OSGB {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
static const char* const letters_;
|
||||
static const char* const digits_;
|
||||
static const TransverseMercator& OSGBTM();
|
||||
static constexpr int base_ = 10;
|
||||
static constexpr int tile_ = 100000;
|
||||
static constexpr int tilelevel_ = 5;
|
||||
static constexpr int tilegrid_ = 5;
|
||||
static constexpr int tileoffx_ = 2 * tilegrid_;
|
||||
static constexpr int tileoffy_ = 1 * tilegrid_;
|
||||
static constexpr int minx_ = - tileoffx_ * tile_;
|
||||
static constexpr int miny_ = - tileoffy_ * tile_;
|
||||
static constexpr int maxx_ = (tilegrid_*tilegrid_ - tileoffx_) * tile_;
|
||||
static constexpr int maxy_ = (tilegrid_*tilegrid_ - tileoffy_) * tile_;
|
||||
// Maximum precision is um
|
||||
static constexpr int maxprec_ = 5 + 6;
|
||||
static real computenorthoffset();
|
||||
static void CheckCoords(real x, real y);
|
||||
OSGB() = delete; // Disable constructor
|
||||
public:
|
||||
|
||||
/**
|
||||
* Forward projection, from geographic to OSGB coordinates.
|
||||
*
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[out] x easting of point (meters).
|
||||
* @param[out] y northing of point (meters).
|
||||
* @param[out] gamma meridian convergence at point (degrees).
|
||||
* @param[out] k scale of projection at point.
|
||||
*
|
||||
* \e lat should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
static void Forward(real lat, real lon,
|
||||
real& x, real& y, real& gamma, real& k) {
|
||||
OSGBTM().Forward(OriginLongitude(), lat, lon, x, y, gamma, k);
|
||||
x += FalseEasting();
|
||||
y += computenorthoffset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse projection, from OSGB coordinates to geographic.
|
||||
*
|
||||
* @param[in] x easting of point (meters).
|
||||
* @param[in] y northing of point (meters).
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] gamma meridian convergence at point (degrees).
|
||||
* @param[out] k scale of projection at point.
|
||||
*
|
||||
* The value of \e lon returned is in the range [−180°,
|
||||
* 180°].
|
||||
**********************************************************************/
|
||||
|
||||
static void Reverse(real x, real y,
|
||||
real& lat, real& lon, real& gamma, real& k) {
|
||||
x -= FalseEasting();
|
||||
y -= computenorthoffset();
|
||||
OSGBTM().Reverse(OriginLongitude(), x, y, lat, lon, gamma, k);
|
||||
}
|
||||
|
||||
/**
|
||||
* OSGB::Forward without returning the convergence and scale.
|
||||
**********************************************************************/
|
||||
static void Forward(real lat, real lon, real& x, real& y) {
|
||||
real gamma, k;
|
||||
Forward(lat, lon, x, y, gamma, k);
|
||||
}
|
||||
|
||||
/**
|
||||
* OSGB::Reverse without returning the convergence and scale.
|
||||
**********************************************************************/
|
||||
static void Reverse(real x, real y, real& lat, real& lon) {
|
||||
real gamma, k;
|
||||
Reverse(x, y, lat, lon, gamma, k);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert OSGB coordinates to a grid reference.
|
||||
*
|
||||
* @param[in] x easting of point (meters).
|
||||
* @param[in] y northing of point (meters).
|
||||
* @param[in] prec precision relative to 100 km.
|
||||
* @param[out] gridref National Grid reference.
|
||||
* @exception GeographicErr if \e prec, \e x, or \e y is outside its
|
||||
* allowed range.
|
||||
* @exception std::bad_alloc if the memory for \e gridref can't be
|
||||
* allocatied.
|
||||
*
|
||||
* \e prec specifies the precision of the grid reference string as follows:
|
||||
* - prec = 0 (min), 100km
|
||||
* - prec = 1, 10km
|
||||
* - prec = 2, 1km
|
||||
* - prec = 3, 100m
|
||||
* - prec = 4, 10m
|
||||
* - prec = 5, 1m
|
||||
* - prec = 6, 0.1m
|
||||
* - prec = 11 (max), 1μm
|
||||
*
|
||||
* The easting must be in the range [−1000 km, 1500 km) and the
|
||||
* northing must be in the range [−500 km, 2000 km). These bounds
|
||||
* are consistent with rules for the letter designations for the grid
|
||||
* system.
|
||||
*
|
||||
* If \e x or \e y is NaN, the returned grid reference is "INVALID".
|
||||
**********************************************************************/
|
||||
static void GridReference(real x, real y, int prec, std::string& gridref);
|
||||
|
||||
/**
|
||||
* Convert OSGB grid reference to coordinates.
|
||||
*
|
||||
* @param[in] gridref National Grid reference.
|
||||
* @param[out] x easting of point (meters).
|
||||
* @param[out] y northing of point (meters).
|
||||
* @param[out] prec precision relative to 100 km.
|
||||
* @param[in] centerp if true (default), return center of the grid square,
|
||||
* else return SW (lower left) corner.
|
||||
* @exception GeographicErr if \e gridref is illegal.
|
||||
*
|
||||
* The grid reference must be of the form: two letters (not including I)
|
||||
* followed by an even number of digits (up to 22).
|
||||
*
|
||||
* If the first 2 characters of \e gridref are "IN", then \e x and \e y are
|
||||
* set to NaN and \e prec is set to −2.
|
||||
**********************************************************************/
|
||||
static void GridReference(const std::string& gridref,
|
||||
real& x, real& y, int& prec,
|
||||
bool centerp = true);
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return \e a the equatorial radius of the Airy 1830 ellipsoid (meters).
|
||||
*
|
||||
* This is 20923713 ft converted to meters using the rule 1 ft =
|
||||
* 10<sup>9.48401603−10</sup> m. The Airy 1830 value is returned
|
||||
* because the OSGB projection is based on this ellipsoid. The conversion
|
||||
* factor from feet to meters is the one used for the 1936 retriangulation
|
||||
* of Britain; see Section A.1 (footnote 10 on p. 44) of <i>A guide to
|
||||
* coordinate systems in Great Britain</i>, v3.6 (2020).
|
||||
**********************************************************************/
|
||||
static Math::real EquatorialRadius() {
|
||||
// result is about 6377563.3960320664406 m
|
||||
using std::pow;
|
||||
return pow(real(10), real(48401603 - 100000000) / 100000000)
|
||||
* real(20923713);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \e f the inverse flattening of the Airy 1830 ellipsoid.
|
||||
*
|
||||
* For the Airy 1830 ellipsoid, \e a = 20923713 ft and \e b = 20853810 ft;
|
||||
* thus the flattening = (20923713 − 20853810)/20923713 =
|
||||
* 7767/2324857 = 1/299.32496459... (The Airy 1830 value is returned
|
||||
* because the OSGB projection is based on this ellipsoid.)
|
||||
**********************************************************************/
|
||||
static Math::real Flattening()
|
||||
{ return real(20923713 - 20853810) / real(20923713); }
|
||||
|
||||
/**
|
||||
* @return \e k0 central scale for the OSGB projection (0.9996012717...).
|
||||
*
|
||||
* C. J. Mugnier, Grids & Datums, PE&RS, Oct. 2003, states that
|
||||
* this is defined as 10<sup>9.9998268−10</sup>.
|
||||
**********************************************************************/
|
||||
static Math::real CentralScale() {
|
||||
using std::pow;
|
||||
return pow(real(10), real(9998268 - 10000000) / 10000000);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return latitude of the origin for the OSGB projection (49 degrees).
|
||||
**********************************************************************/
|
||||
static Math::real OriginLatitude() { return real(49); }
|
||||
|
||||
/**
|
||||
* @return longitude of the origin for the OSGB projection (−2
|
||||
* degrees).
|
||||
**********************************************************************/
|
||||
static Math::real OriginLongitude() { return real(-2); }
|
||||
|
||||
/**
|
||||
* @return false northing the OSGB projection (−100000 meters).
|
||||
**********************************************************************/
|
||||
static Math::real FalseNorthing() { return real(-100000); }
|
||||
|
||||
/**
|
||||
* @return false easting the OSGB projection (400000 meters).
|
||||
**********************************************************************/
|
||||
static Math::real FalseEasting() { return real(400000); }
|
||||
///@}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_OSGB_HPP
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* \file PolarStereographic.hpp
|
||||
* \brief Header for GeographicLib::PolarStereographic class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2008-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_POLARSTEREOGRAPHIC_HPP)
|
||||
#define GEOGRAPHICLIB_POLARSTEREOGRAPHIC_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Polar stereographic projection
|
||||
*
|
||||
* Implementation taken from the report,
|
||||
* - J. P. Snyder,
|
||||
* <a href="https://pubs.usgs.gov/publication/pp1395"> Map Projections: A
|
||||
* Working Manual</a>, USGS Professional Paper 1395 (1987),
|
||||
* pp. 160--163.
|
||||
*
|
||||
* This is a straightforward implementation of the equations in Snyder except
|
||||
* that Newton's method is used to invert the projection.
|
||||
*
|
||||
* This class also returns the meridian convergence \e gamma and scale \e k.
|
||||
* The meridian convergence is the bearing of grid north (the \e y axis)
|
||||
* measured clockwise from true north.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-PolarStereographic.cpp
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT PolarStereographic {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
real _a, _f, _e2, _es, _e2m, _c;
|
||||
real _k0;
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for an ellipsoid with
|
||||
*
|
||||
* @param[in] a equatorial radius (meters).
|
||||
* @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
|
||||
* Negative \e f gives a prolate ellipsoid.
|
||||
* @param[in] k0 central scale factor.
|
||||
* @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k0 is
|
||||
* not positive.
|
||||
**********************************************************************/
|
||||
PolarStereographic(real a, real f, real k0);
|
||||
|
||||
/**
|
||||
* Set the scale for the projection.
|
||||
*
|
||||
* @param[in] lat (degrees) assuming \e northp = true.
|
||||
* @param[in] k scale at latitude \e lat (default 1).
|
||||
* @exception GeographicErr \e k is not positive.
|
||||
* @exception GeographicErr if \e lat is not in (−90°,
|
||||
* 90°].
|
||||
**********************************************************************/
|
||||
void SetScale(real lat, real k = real(1));
|
||||
|
||||
/**
|
||||
* Forward projection, from geographic to polar stereographic.
|
||||
*
|
||||
* @param[in] northp the pole which is the center of projection (true means
|
||||
* north, false means south).
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[out] x easting of point (meters).
|
||||
* @param[out] y northing of point (meters).
|
||||
* @param[out] gamma meridian convergence at point (degrees).
|
||||
* @param[out] k scale of projection at point.
|
||||
*
|
||||
* No false easting or northing is added. \e lat should be in the range
|
||||
* (−90°, 90°] for \e northp = true and in the range
|
||||
* [−90°, 90°) for \e northp = false.
|
||||
**********************************************************************/
|
||||
void Forward(bool northp, real lat, real lon,
|
||||
real& x, real& y, real& gamma, real& k) const;
|
||||
|
||||
/**
|
||||
* Reverse projection, from polar stereographic to geographic.
|
||||
*
|
||||
* @param[in] northp the pole which is the center of projection (true means
|
||||
* north, false means south).
|
||||
* @param[in] x easting of point (meters).
|
||||
* @param[in] y northing of point (meters).
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] gamma meridian convergence at point (degrees).
|
||||
* @param[out] k scale of projection at point.
|
||||
*
|
||||
* No false easting or northing is added. The value of \e lon returned is
|
||||
* in the range [−180°, 180°].
|
||||
**********************************************************************/
|
||||
void Reverse(bool northp, real x, real y,
|
||||
real& lat, real& lon, real& gamma, real& k) const;
|
||||
|
||||
/**
|
||||
* PolarStereographic::Forward without returning the convergence and scale.
|
||||
**********************************************************************/
|
||||
void Forward(bool northp, real lat, real lon,
|
||||
real& x, real& y) const {
|
||||
real gamma, k;
|
||||
Forward(northp, lat, lon, x, y, gamma, k);
|
||||
}
|
||||
|
||||
/**
|
||||
* PolarStereographic::Reverse without returning the convergence and scale.
|
||||
**********************************************************************/
|
||||
void Reverse(bool northp, real x, real y,
|
||||
real& lat, real& lon) const {
|
||||
real gamma, k;
|
||||
Reverse(northp, x, y, lat, lon, gamma, k);
|
||||
}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _a; }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the value used in
|
||||
* the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _f; }
|
||||
|
||||
/**
|
||||
* The central scale for the projection. This is the value of \e k0 used
|
||||
* in the constructor and is the scale at the pole unless overridden by
|
||||
* PolarStereographic::SetScale.
|
||||
**********************************************************************/
|
||||
Math::real CentralScale() const { return _k0; }
|
||||
///@}
|
||||
|
||||
/**
|
||||
* A global instantiation of PolarStereographic with the WGS84 ellipsoid
|
||||
* and the UPS scale factor. However, unlike UPS, no false easting or
|
||||
* northing is added.
|
||||
**********************************************************************/
|
||||
static const PolarStereographic& UPS();
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_POLARSTEREOGRAPHIC_HPP
|
||||
+315
@@ -0,0 +1,315 @@
|
||||
/**
|
||||
* \file PolygonArea.hpp
|
||||
* \brief Header for GeographicLib::PolygonAreaT class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2010-2023) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_POLYGONAREA_HPP)
|
||||
#define GEOGRAPHICLIB_POLYGONAREA_HPP 1
|
||||
|
||||
#include <GeographicLib/Geodesic.hpp>
|
||||
#include <GeographicLib/GeodesicExact.hpp>
|
||||
#include <GeographicLib/Rhumb.hpp>
|
||||
#include <GeographicLib/Accumulator.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Polygon areas
|
||||
*
|
||||
* This computes the area of a polygon whose edges are geodesics using the
|
||||
* method given in Section 6 of
|
||||
* - C. F. F. Karney,
|
||||
* <a href="https://doi.org/10.1007/s00190-012-0578-z">
|
||||
* Algorithms for geodesics</a>,
|
||||
* J. Geodesy <b>87</b>, 43--55 (2013);
|
||||
* DOI: <a href="https://doi.org/10.1007/s00190-012-0578-z">
|
||||
* 10.1007/s00190-012-0578-z</a>;
|
||||
* addenda:
|
||||
* <a href="https://geographiclib.sourceforge.io/geod-addenda.html">
|
||||
* geod-addenda.html</a>.
|
||||
*
|
||||
* Arbitrarily complex polygons are allowed. In the case self-intersecting
|
||||
* of polygons the area is accumulated "algebraically", e.g., the areas of
|
||||
* the 2 loops in a figure-8 polygon will partially cancel.
|
||||
*
|
||||
* This class lets you add vertices and edges one at a time to the polygon.
|
||||
* The sequence must start with a vertex and thereafter vertices and edges
|
||||
* can be added in any order. Any vertex after the first creates a new edge
|
||||
* which is the \e shortest geodesic from the previous vertex. In some
|
||||
* cases there may be two or many such shortest geodesics and the area is
|
||||
* then not uniquely defined. In this case, either add an intermediate
|
||||
* vertex or add the edge \e as an edge (by defining its direction and
|
||||
* length).
|
||||
*
|
||||
* The area and perimeter are accumulated at two times the standard floating
|
||||
* point precision to guard against the loss of accuracy with many-sided
|
||||
* polygons. At any point you can ask for the perimeter and area so far.
|
||||
* There's an option to treat the points as defining a polyline instead of a
|
||||
* polygon; in that case, only the perimeter is computed.
|
||||
*
|
||||
* This is a templated class to allow it to be used with Geodesic,
|
||||
* GeodesicExact, and Rhumb. GeographicLib::PolygonArea,
|
||||
* GeographicLib::PolygonAreaExact, and GeographicLib::PolygonAreaRhumb are
|
||||
* typedefs for these cases.
|
||||
*
|
||||
* For GeographicLib::PolygonArea (edges defined by Geodesic), an upper bound
|
||||
* on the error is about 0.1 m<sup>2</sup> per vertex. However this is a
|
||||
* wildly pessimistic estimate in most cases. A more realistic estimate of
|
||||
* the error is given by a test involving 10<sup>7</sup> approximately
|
||||
* regular polygons on the WGS84 ellipsoid. The centers and the orientations
|
||||
* of the polygons were uniformly distributed, the number of vertices was
|
||||
* log-uniformly distributed in [3, 300], and the center to vertex distance
|
||||
* log-uniformly distributed in [0.1 m, 9000 km].
|
||||
*
|
||||
* Using double precision (the standard precision for GeographicLib), the
|
||||
* maximum error in the perimeter was 200 nm, and the maximum error in the
|
||||
* area was<pre>
|
||||
* 0.0013 m^2 for perimeter < 10 km
|
||||
* 0.0070 m^2 for perimeter < 100 km
|
||||
* 0.070 m^2 for perimeter < 1000 km
|
||||
* 0.11 m^2 for all perimeters
|
||||
* </pre>
|
||||
* The errors are given in terms of the perimeter, because it is expected
|
||||
* that the errors depend mainly on the number of edges and the edge lengths.
|
||||
*
|
||||
* Using long doubles (GEOGRPAHICLIB_PRECISION = 3), the maximum error in the
|
||||
* perimeter was 200 pm, and the maximum error in the area was<pre>
|
||||
* 0.7 mm^2 for perim < 10 km
|
||||
* 3.2 mm^2 for perimeter < 100 km
|
||||
* 21 mm^2 for perimeter < 1000 km
|
||||
* 45 mm^2 for all perimeters
|
||||
* </pre>
|
||||
*
|
||||
* @tparam GeodType the geodesic class to use.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-PolygonArea.cpp
|
||||
*
|
||||
* <a href="Planimeter.1.html">Planimeter</a> is a command-line utility
|
||||
* providing access to the functionality of PolygonAreaT.
|
||||
**********************************************************************/
|
||||
|
||||
template<class GeodType = Geodesic>
|
||||
class PolygonAreaT {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
GeodType _earth;
|
||||
real _area0; // Full ellipsoid area
|
||||
bool _polyline; // Assume polyline (don't close and skip area)
|
||||
unsigned _mask;
|
||||
unsigned _num;
|
||||
int _crossings;
|
||||
Accumulator<> _areasum, _perimetersum;
|
||||
real _lat0, _lon0, _lat1, _lon1;
|
||||
static int transit(real lon1, real lon2);
|
||||
// an alternate version of transit to deal with longitudes in the direct
|
||||
// problem.
|
||||
static int transitdirect(real lon1, real lon2);
|
||||
void Remainder(Accumulator<>& a) const { a.remainder(_area0); }
|
||||
void Remainder(real& a) const {
|
||||
using std::remainder;
|
||||
a = remainder(a, _area0);
|
||||
}
|
||||
template<typename T>
|
||||
void AreaReduce(T& area, int crossings, bool reverse, bool sign) const;
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for PolygonAreaT.
|
||||
*
|
||||
* @param[in] earth the Geodesic object to use for geodesic calculations.
|
||||
* @param[in] polyline if true that treat the points as defining a polyline
|
||||
* instead of a polygon (default = false).
|
||||
**********************************************************************/
|
||||
PolygonAreaT(const GeodType& earth, bool polyline = false)
|
||||
: _earth(earth)
|
||||
, _area0(_earth.EllipsoidArea())
|
||||
, _polyline(polyline)
|
||||
, _mask(GeodType::LATITUDE | GeodType::LONGITUDE | GeodType::DISTANCE |
|
||||
(_polyline ? GeodType::NONE :
|
||||
GeodType::AREA | GeodType::LONG_UNROLL))
|
||||
{ Clear(); }
|
||||
|
||||
/**
|
||||
* Clear PolygonAreaT, allowing a new polygon to be started.
|
||||
**********************************************************************/
|
||||
void Clear() {
|
||||
_num = 0;
|
||||
_crossings = 0;
|
||||
_areasum = 0;
|
||||
_perimetersum = 0;
|
||||
_lat0 = _lon0 = _lat1 = _lon1 = Math::NaN();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a point to the polygon or polyline.
|
||||
*
|
||||
* @param[in] lat the latitude of the point (degrees).
|
||||
* @param[in] lon the longitude of the point (degrees).
|
||||
*
|
||||
* \e lat should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
void AddPoint(real lat, real lon);
|
||||
|
||||
/**
|
||||
* Add an edge to the polygon or polyline.
|
||||
*
|
||||
* @param[in] azi azimuth at current point (degrees).
|
||||
* @param[in] s distance from current point to next point (meters).
|
||||
*
|
||||
* This does nothing if no points have been added yet. Use
|
||||
* PolygonAreaT::CurrentPoint to determine the position of the new vertex.
|
||||
**********************************************************************/
|
||||
void AddEdge(real azi, real s);
|
||||
|
||||
/**
|
||||
* Return the results so far.
|
||||
*
|
||||
* @param[in] reverse if true then clockwise (instead of counterclockwise)
|
||||
* traversal counts as a positive area.
|
||||
* @param[in] sign if true then return a signed result for the area if
|
||||
* the polygon is traversed in the "wrong" direction instead of returning
|
||||
* the area for the rest of the earth.
|
||||
* @param[out] perimeter the perimeter of the polygon or length of the
|
||||
* polyline (meters).
|
||||
* @param[out] area the area of the polygon (meters<sup>2</sup>); only set
|
||||
* if \e polyline is false in the constructor.
|
||||
* @return the number of points.
|
||||
*
|
||||
* More points can be added to the polygon after this call.
|
||||
**********************************************************************/
|
||||
unsigned Compute(bool reverse, bool sign,
|
||||
real& perimeter, real& area) const;
|
||||
|
||||
/**
|
||||
* Return the results assuming a tentative final test point is added;
|
||||
* however, the data for the test point is not saved. This lets you report
|
||||
* a running result for the perimeter and area as the user moves the mouse
|
||||
* cursor. Ordinary floating point arithmetic is used to accumulate the
|
||||
* data for the test point; thus the area and perimeter returned are less
|
||||
* accurate than if PolygonAreaT::AddPoint and PolygonAreaT::Compute are
|
||||
* used.
|
||||
*
|
||||
* @param[in] lat the latitude of the test point (degrees).
|
||||
* @param[in] lon the longitude of the test point (degrees).
|
||||
* @param[in] reverse if true then clockwise (instead of counterclockwise)
|
||||
* traversal counts as a positive area.
|
||||
* @param[in] sign if true then return a signed result for the area if
|
||||
* the polygon is traversed in the "wrong" direction instead of returning
|
||||
* the area for the rest of the earth.
|
||||
* @param[out] perimeter the approximate perimeter of the polygon or length
|
||||
* of the polyline (meters).
|
||||
* @param[out] area the approximate area of the polygon
|
||||
* (meters<sup>2</sup>); only set if polyline is false in the
|
||||
* constructor.
|
||||
* @return the number of points.
|
||||
*
|
||||
* \e lat should be in the range [−90°, 90°].
|
||||
**********************************************************************/
|
||||
unsigned TestPoint(real lat, real lon, bool reverse, bool sign,
|
||||
real& perimeter, real& area) const;
|
||||
|
||||
/**
|
||||
* Return the results assuming a tentative final test point is added via an
|
||||
* azimuth and distance; however, the data for the test point is not saved.
|
||||
* This lets you report a running result for the perimeter and area as the
|
||||
* user moves the mouse cursor. Ordinary floating point arithmetic is used
|
||||
* to accumulate the data for the test point; thus the area and perimeter
|
||||
* returned are less accurate than if PolygonAreaT::AddEdge and
|
||||
* PolygonAreaT::Compute are used.
|
||||
*
|
||||
* @param[in] azi azimuth at current point (degrees).
|
||||
* @param[in] s distance from current point to final test point (meters).
|
||||
* @param[in] reverse if true then clockwise (instead of counterclockwise)
|
||||
* traversal counts as a positive area.
|
||||
* @param[in] sign if true then return a signed result for the area if
|
||||
* the polygon is traversed in the "wrong" direction instead of returning
|
||||
* the area for the rest of the earth.
|
||||
* @param[out] perimeter the approximate perimeter of the polygon or length
|
||||
* of the polyline (meters).
|
||||
* @param[out] area the approximate area of the polygon
|
||||
* (meters<sup>2</sup>); only set if polyline is false in the
|
||||
* constructor.
|
||||
* @return the number of points.
|
||||
**********************************************************************/
|
||||
unsigned TestEdge(real azi, real s, bool reverse, bool sign,
|
||||
real& perimeter, real& area) const;
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value inherited from the Geodesic object used in the constructor.
|
||||
**********************************************************************/
|
||||
|
||||
Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the value
|
||||
* inherited from the Geodesic object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _earth.Flattening(); }
|
||||
|
||||
/**
|
||||
* Report the previous vertex added to the polygon or polyline.
|
||||
*
|
||||
* @param[out] lat the latitude of the point (degrees).
|
||||
* @param[out] lon the longitude of the point (degrees).
|
||||
*
|
||||
* If no points have been added, then NaNs are returned. Otherwise, \e lon
|
||||
* will be in the range [−180°, 180°].
|
||||
**********************************************************************/
|
||||
void CurrentPoint(real& lat, real& lon) const
|
||||
{ lat = _lat1; lon = _lon1; }
|
||||
|
||||
/**
|
||||
* Report the number of points currently in the polygon or polyline.
|
||||
*
|
||||
* @return the number of points.
|
||||
*
|
||||
* If no points have been added, then 0 is returned.
|
||||
**********************************************************************/
|
||||
unsigned NumberPoints() const { return _num; }
|
||||
|
||||
/**
|
||||
* Report whether the current object is a polygon or a polyline.
|
||||
*
|
||||
* @return true if the object is a polyline.
|
||||
**********************************************************************/
|
||||
bool Polyline() const { return _polyline; }
|
||||
///@}
|
||||
};
|
||||
|
||||
/**
|
||||
* @relates PolygonAreaT
|
||||
*
|
||||
* Polygon areas using Geodesic. This should be used if the flattening is
|
||||
* small.
|
||||
**********************************************************************/
|
||||
typedef PolygonAreaT<Geodesic> PolygonArea;
|
||||
|
||||
/**
|
||||
* @relates PolygonAreaT
|
||||
*
|
||||
* Polygon areas using GeodesicExact.
|
||||
*
|
||||
* \deprecated Instead of using PolygonAreaExact, use PolygonArea
|
||||
* with a Geodesic object constructed with \e exact = true.
|
||||
**********************************************************************/
|
||||
typedef PolygonAreaT<GeodesicExact> PolygonAreaExact;
|
||||
|
||||
/**
|
||||
* @relates PolygonAreaT
|
||||
*
|
||||
* Polygon areas using Rhumb.
|
||||
**********************************************************************/
|
||||
typedef PolygonAreaT<Rhumb> PolygonAreaRhumb;
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_POLYGONAREA_HPP
|
||||
@@ -0,0 +1,549 @@
|
||||
/**
|
||||
* \file Rhumb.hpp
|
||||
* \brief Header for GeographicLib::Rhumb and GeographicLib::RhumbLine classes
|
||||
*
|
||||
* Copyright (c) Charles Karney (2014-2023) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_RHUMB_HPP)
|
||||
#define GEOGRAPHICLIB_RHUMB_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/DAuxLatitude.hpp>
|
||||
#include <vector>
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_RHUMBAREA_ORDER)
|
||||
/**
|
||||
* The order of the series approximation used in rhumb area calculations.
|
||||
* GEOGRAPHICLIB_RHUMBAREA_ORDER can be set to one of [4, 5, 6, 7, 8].
|
||||
**********************************************************************/
|
||||
# define GEOGRAPHICLIB_RHUMBAREA_ORDER \
|
||||
(GEOGRAPHICLIB_PRECISION == 2 ? 6 : \
|
||||
(GEOGRAPHICLIB_PRECISION == 1 ? 4 : 8))
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Squelch warnings about dll vs vector
|
||||
# pragma warning (push)
|
||||
# pragma warning (disable: 4251)
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
class RhumbLine;
|
||||
|
||||
/**
|
||||
* \brief Solve of the direct and inverse rhumb problems.
|
||||
*
|
||||
* The path of constant azimuth between two points on an ellipsoid at (\e
|
||||
* lat1, \e lon1) and (\e lat2, \e lon2) is called the rhumb line (also
|
||||
* called the loxodrome). Its length is \e s12 and its azimuth is \e azi12.
|
||||
* (The azimuth is the heading measured clockwise from north.)
|
||||
*
|
||||
* Given \e lat1, \e lon1, \e azi12, and \e s12, we can determine \e lat2,
|
||||
* and \e lon2. This is the \e direct rhumb problem and its solution is
|
||||
* given by the function Rhumb::Direct.
|
||||
*
|
||||
* Given \e lat1, \e lon1, \e lat2, and \e lon2, we can determine \e azi12
|
||||
* and \e s12. This is the \e inverse rhumb problem, whose solution is given
|
||||
* by Rhumb::Inverse. This finds the shortest such rhumb line, i.e., the one
|
||||
* that wraps no more than half way around the earth. If the end points are
|
||||
* on opposite meridians, there are two shortest rhumb lines and the
|
||||
* east-going one is chosen.
|
||||
*
|
||||
* These routines also optionally calculate the area under the rhumb line, \e
|
||||
* S12. This is the area, measured counterclockwise, of the rhumb line
|
||||
* quadrilateral with corners (<i>lat1</i>,<i>lon1</i>), (0,<i>lon1</i>),
|
||||
* (0,<i>lon2</i>), and (<i>lat2</i>,<i>lon2</i>).
|
||||
*
|
||||
* Note that rhumb lines may be appreciably longer (up to 50%) than the
|
||||
* corresponding Geodesic. For example the distance between London Heathrow
|
||||
* and Tokyo Narita via the rhumb line is 11400 km which is 18% longer than
|
||||
* the geodesic distance 9600 km.
|
||||
*
|
||||
* This implementation is described in
|
||||
* - C. F. F. Karney,<br>
|
||||
* <a href="https://doi.org/10.1007/s11200-024-0709-z">
|
||||
* <i>The area of rhumb polygons</i></a>,<br>
|
||||
* Stud. Geophys. Geod. 68(3--4), 99--120 (2024);
|
||||
* DOI: <a href="https://doi.org/10.1007/s11200-024-0709-z">
|
||||
* 10.1007/s11200-024-0709-z</a>.
|
||||
* .
|
||||
* For more information on rhumb lines see \ref rhumb.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Rhumb.cpp
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT Rhumb {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
friend class RhumbLine;
|
||||
template<class T> friend class PolygonAreaT;
|
||||
DAuxLatitude _aux;
|
||||
bool _exact;
|
||||
real _a, _f, _n, _rm, _c2;
|
||||
int _lL; // N.B. names of the form _[A-Z].* are reserved in C++
|
||||
std::vector<real> _pP; // The Fourier coefficients P_l
|
||||
static const int Lmax_ = GEOGRAPHICLIB_RHUMBAREA_ORDER;
|
||||
void AreaCoeffs();
|
||||
class qIntegrand {
|
||||
const AuxLatitude& _aux;
|
||||
public:
|
||||
qIntegrand(const AuxLatitude& aux);
|
||||
real operator()(real chi) const;
|
||||
};
|
||||
|
||||
real MeanSinXi(const AuxAngle& chix, const AuxAngle& chiy) const;
|
||||
|
||||
// The following two functions (with lots of ignored arguments) mimic the
|
||||
// interface to the corresponding Geodesic function. These are needed by
|
||||
// PolygonAreaT.
|
||||
void GenDirect(real lat1, real lon1, real azi12,
|
||||
bool, real s12, unsigned outmask,
|
||||
real& lat2, real& lon2, real&, real&, real&, real&, real&,
|
||||
real& S12) const {
|
||||
GenDirect(lat1, lon1, azi12, s12, outmask, lat2, lon2, S12);
|
||||
}
|
||||
void GenInverse(real lat1, real lon1, real lat2, real lon2,
|
||||
unsigned outmask, real& s12, real& azi12,
|
||||
real&, real& , real& , real& , real& S12) const {
|
||||
GenInverse(lat1, lon1, lat2, lon2, outmask, s12, azi12, S12);
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Bit masks for what calculations to do. They specify which results to
|
||||
* return in the general routines Rhumb::GenDirect and Rhumb::GenInverse
|
||||
* routines. RhumbLine::mask is a duplication of this enum.
|
||||
**********************************************************************/
|
||||
enum mask {
|
||||
/**
|
||||
* No output.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
NONE = 0U,
|
||||
/**
|
||||
* Calculate latitude \e lat2.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LATITUDE = 1U<<7,
|
||||
/**
|
||||
* Calculate longitude \e lon2.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LONGITUDE = 1U<<8,
|
||||
/**
|
||||
* Calculate azimuth \e azi12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
AZIMUTH = 1U<<9,
|
||||
/**
|
||||
* Calculate distance \e s12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
DISTANCE = 1U<<10,
|
||||
/**
|
||||
* Calculate area \e S12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
AREA = 1U<<14,
|
||||
/**
|
||||
* Unroll \e lon2 in the direct calculation.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LONG_UNROLL = 1U<<15,
|
||||
/**
|
||||
* Calculate everything. (LONG_UNROLL is not included in this mask.)
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
ALL = 0x7F80U,
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor for an ellipsoid with
|
||||
*
|
||||
* @param[in] a equatorial radius (meters).
|
||||
* @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
|
||||
* Negative \e f gives a prolate ellipsoid.
|
||||
* @param[in] exact if true use the exact expressions for the auxiliary
|
||||
* latitudes; otherwise use series expansion (accurate for |<i>f</i>| <
|
||||
* 0.01) [default false].
|
||||
* @exception GeographicErr if \e a or (1 − \e f) \e a is not
|
||||
* positive.
|
||||
**********************************************************************/
|
||||
Rhumb(real a, real f, bool exact = false);
|
||||
|
||||
/**
|
||||
* Solve the direct rhumb problem returning also the area.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi12 azimuth of the rhumb line (degrees).
|
||||
* @param[in] s12 distance between point 1 and point 2 (meters); it can be
|
||||
* negative.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees).
|
||||
* @param[out] S12 area under the rhumb line (meters<sup>2</sup>).
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°]. The value of
|
||||
* \e lon2 returned is in the range [−180°, 180°].
|
||||
*
|
||||
* If point 1 is a pole, the cosine of its latitude is taken to be
|
||||
* 1/ε<sup>2</sup> (where ε is 2<sup>-52</sup>). This
|
||||
* position, which is extremely close to the actual pole, allows the
|
||||
* calculation to be carried out in finite terms. If \e s12 is large
|
||||
* enough that the rhumb line crosses a pole, the longitude of point 2
|
||||
* is indeterminate (a NaN is returned for \e lon2 and \e S12).
|
||||
**********************************************************************/
|
||||
void Direct(real lat1, real lon1, real azi12, real s12,
|
||||
real& lat2, real& lon2, real& S12) const {
|
||||
GenDirect(lat1, lon1, azi12, s12,
|
||||
LATITUDE | LONGITUDE | AREA, lat2, lon2, S12);
|
||||
}
|
||||
|
||||
/**
|
||||
* Solve the direct rhumb problem without the area.
|
||||
**********************************************************************/
|
||||
void Direct(real lat1, real lon1, real azi12, real s12,
|
||||
real& lat2, real& lon2) const {
|
||||
real t;
|
||||
GenDirect(lat1, lon1, azi12, s12, LATITUDE | LONGITUDE, lat2, lon2, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* The general direct rhumb problem. Rhumb::Direct is defined in terms
|
||||
* of this function.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi12 azimuth of the rhumb line (degrees).
|
||||
* @param[in] s12 distance between point 1 and point 2 (meters); it can be
|
||||
* negative.
|
||||
* @param[in] outmask a bitor'ed combination of Rhumb::mask values
|
||||
* specifying which of the following parameters should be set.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees).
|
||||
* @param[out] S12 area under the rhumb line (meters<sup>2</sup>).
|
||||
*
|
||||
* The Rhumb::mask values possible for \e outmask are
|
||||
* - \e outmask |= Rhumb::LATITUDE for the latitude \e lat2;
|
||||
* - \e outmask |= Rhumb::LONGITUDE for the latitude \e lon2;
|
||||
* - \e outmask |= Rhumb::AREA for the area \e S12;
|
||||
* - \e outmask |= Rhumb::ALL for all of the above;
|
||||
* - \e outmask |= Rhumb::LONG_UNROLL to unroll \e lon2 instead of wrapping
|
||||
* it into the range [−180°, 180°].
|
||||
* .
|
||||
* With the Rhumb::LONG_UNROLL bit set, the quantity \e lon2 −
|
||||
* \e lon1 indicates how many times and in what sense the rhumb line
|
||||
* encircles the ellipsoid.
|
||||
**********************************************************************/
|
||||
void GenDirect(real lat1, real lon1, real azi12, real s12,
|
||||
unsigned outmask, real& lat2, real& lon2, real& S12) const;
|
||||
|
||||
/**
|
||||
* Solve the inverse rhumb problem returning also the area.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] lat2 latitude of point 2 (degrees).
|
||||
* @param[in] lon2 longitude of point 2 (degrees).
|
||||
* @param[out] s12 rhumb distance between point 1 and point 2 (meters).
|
||||
* @param[out] azi12 azimuth of the rhumb line (degrees).
|
||||
* @param[out] S12 area under the rhumb line (meters<sup>2</sup>).
|
||||
*
|
||||
* The shortest rhumb line is found. If the end points are on opposite
|
||||
* meridians, there are two shortest rhumb lines and the east-going one is
|
||||
* chosen. \e lat1 and \e lat2 should be in the range [−90°,
|
||||
* 90°]. The value of \e azi12 returned is in the range
|
||||
* [−180°, 180°].
|
||||
*
|
||||
* If either point is a pole, the cosine of its latitude is taken to be
|
||||
* 1/ε<sup>2</sup> (where ε is 2<sup>-52</sup>). This
|
||||
* position, which is extremely close to the actual pole, allows the
|
||||
* calculation to be carried out in finite terms.
|
||||
**********************************************************************/
|
||||
void Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& s12, real& azi12, real& S12) const {
|
||||
GenInverse(lat1, lon1, lat2, lon2,
|
||||
DISTANCE | AZIMUTH | AREA, s12, azi12, S12);
|
||||
}
|
||||
|
||||
/**
|
||||
* Solve the inverse rhumb problem without the area.
|
||||
**********************************************************************/
|
||||
void Inverse(real lat1, real lon1, real lat2, real lon2,
|
||||
real& s12, real& azi12) const {
|
||||
real t;
|
||||
GenInverse(lat1, lon1, lat2, lon2, DISTANCE | AZIMUTH, s12, azi12, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* The general inverse rhumb problem. Rhumb::Inverse is defined in terms
|
||||
* of this function.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] lat2 latitude of point 2 (degrees).
|
||||
* @param[in] lon2 longitude of point 2 (degrees).
|
||||
* @param[in] outmask a bitor'ed combination of Rhumb::mask values
|
||||
* specifying which of the following parameters should be set.
|
||||
* @param[out] s12 rhumb distance between point 1 and point 2 (meters).
|
||||
* @param[out] azi12 azimuth of the rhumb line (degrees).
|
||||
* @param[out] S12 area under the rhumb line (meters<sup>2</sup>).
|
||||
*
|
||||
* The Rhumb::mask values possible for \e outmask are
|
||||
* - \e outmask |= Rhumb::DISTANCE for the latitude \e s12;
|
||||
* - \e outmask |= Rhumb::AZIMUTH for the latitude \e azi12;
|
||||
* - \e outmask |= Rhumb::AREA for the area \e S12;
|
||||
* - \e outmask |= Rhumb::ALL for all of the above;
|
||||
**********************************************************************/
|
||||
void GenInverse(real lat1, real lon1, real lat2, real lon2,
|
||||
unsigned outmask,
|
||||
real& s12, real& azi12, real& S12) const;
|
||||
|
||||
/**
|
||||
* Typedef for the class for computing multiple points on a rhumb line.
|
||||
**********************************************************************/
|
||||
typedef RhumbLine LineClass;
|
||||
|
||||
/**
|
||||
* Set up to compute several points on a single rhumb line.
|
||||
*
|
||||
* @param[in] lat1 latitude of point 1 (degrees).
|
||||
* @param[in] lon1 longitude of point 1 (degrees).
|
||||
* @param[in] azi12 azimuth of the rhumb line (degrees).
|
||||
* @return a RhumbLine object.
|
||||
*
|
||||
* \e lat1 should be in the range [−90°, 90°].
|
||||
*
|
||||
* If point 1 is a pole, the cosine of its latitude is taken to be
|
||||
* 1/ε<sup>2</sup> (where ε is 2<sup>-52</sup>). This
|
||||
* position, which is extremely close to the actual pole, allows the
|
||||
* calculation to be carried out in finite terms.
|
||||
**********************************************************************/
|
||||
RhumbLine Line(real lat1, real lon1, real azi12) const;
|
||||
|
||||
/** \name Inspector functions.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _a; }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the
|
||||
* value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _f; }
|
||||
|
||||
/**
|
||||
* @return total area of ellipsoid in meters<sup>2</sup>. The area of a
|
||||
* polygon encircling a pole can be found by adding
|
||||
* Geodesic::EllipsoidArea()/2 to the sum of \e S12 for each side of the
|
||||
* polygon.
|
||||
**********************************************************************/
|
||||
Math::real EllipsoidArea() const {
|
||||
// _c2 contains a Math::degrees() factor, so 4*pi -> 2*Math::td.
|
||||
return 2 * real(Math::td) * _c2;
|
||||
}
|
||||
///@}
|
||||
|
||||
/**
|
||||
* A global instantiation of Rhumb with the parameters for the WGS84
|
||||
* ellipsoid.
|
||||
**********************************************************************/
|
||||
static const Rhumb& WGS84();
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Find a sequence of points on a single rhumb line.
|
||||
*
|
||||
* RhumbLine facilitates the determination of a series of points on a single
|
||||
* rhumb line. The starting point (\e lat1, \e lon1) and the azimuth \e
|
||||
* azi12 are specified in the call to Rhumb::Line which returns a RhumbLine
|
||||
* object. RhumbLine.Position returns the location of point 2 (and,
|
||||
* optionally, the corresponding area, \e S12) a distance \e s12 along the
|
||||
* rhumb line.
|
||||
*
|
||||
* There is no public constructor for this class. (Use Rhumb::Line to create
|
||||
* an instance.) The Rhumb object used to create a RhumbLine must stay in
|
||||
* scope as long as the RhumbLine.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-RhumbLine.cpp
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT RhumbLine {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
friend class Rhumb;
|
||||
const Rhumb& _rh;
|
||||
real _lat1, _lon1, _azi12, _salp, _calp, _mu1, _psi1;
|
||||
AuxAngle _phi1, _chi1;
|
||||
// copy assignment not allowed
|
||||
RhumbLine& operator=(const RhumbLine&) = delete;
|
||||
RhumbLine(const Rhumb& rh, real lat1, real lon1, real azi12);
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construction is via default copy constructor.
|
||||
**********************************************************************/
|
||||
RhumbLine(const RhumbLine&) = default;
|
||||
/**
|
||||
* This is a duplication of Rhumb::mask.
|
||||
**********************************************************************/
|
||||
enum mask {
|
||||
/**
|
||||
* No output.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
NONE = Rhumb::NONE,
|
||||
/**
|
||||
* Calculate latitude \e lat2.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LATITUDE = Rhumb::LATITUDE,
|
||||
/**
|
||||
* Calculate longitude \e lon2.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LONGITUDE = Rhumb::LONGITUDE,
|
||||
/**
|
||||
* Calculate azimuth \e azi12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
AZIMUTH = Rhumb::AZIMUTH,
|
||||
/**
|
||||
* Calculate distance \e s12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
DISTANCE = Rhumb::DISTANCE,
|
||||
/**
|
||||
* Calculate area \e S12.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
AREA = Rhumb::AREA,
|
||||
/**
|
||||
* Unroll \e lon2 in the direct calculation.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
LONG_UNROLL = Rhumb::LONG_UNROLL,
|
||||
/**
|
||||
* Calculate everything. (LONG_UNROLL is not included in this mask.)
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
ALL = Rhumb::ALL,
|
||||
};
|
||||
|
||||
/**
|
||||
* Typedef for the base class implementing rhumb lines.
|
||||
**********************************************************************/
|
||||
typedef Rhumb BaseClass;
|
||||
|
||||
/**
|
||||
* Compute the position of point 2 which is a distance \e s12 (meters) from
|
||||
* point 1. The area is also computed.
|
||||
*
|
||||
* @param[in] s12 distance between point 1 and point 2 (meters); it can be
|
||||
* negative.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees).
|
||||
* @param[out] S12 area under the rhumb line (meters<sup>2</sup>).
|
||||
*
|
||||
* The value of \e lon2 returned is in the range [−180°,
|
||||
* 180°].
|
||||
*
|
||||
* If \e s12 is large enough that the rhumb line crosses a pole, the
|
||||
* longitude of point 2 is indeterminate (a NaN is returned for \e lon2 and
|
||||
* \e S12).
|
||||
**********************************************************************/
|
||||
void Position(real s12, real& lat2, real& lon2, real& S12) const {
|
||||
GenPosition(s12, LATITUDE | LONGITUDE | AREA, lat2, lon2, S12);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the position of point 2 which is a distance \e s12 (meters) from
|
||||
* point 1. The area is not computed.
|
||||
**********************************************************************/
|
||||
void Position(real s12, real& lat2, real& lon2) const {
|
||||
real t;
|
||||
GenPosition(s12, LATITUDE | LONGITUDE, lat2, lon2, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* The general position routine. RhumbLine::Position is defined in term so
|
||||
* this function.
|
||||
*
|
||||
* @param[in] s12 distance between point 1 and point 2 (meters); it can be
|
||||
* negative.
|
||||
* @param[in] outmask a bitor'ed combination of RhumbLine::mask values
|
||||
* specifying which of the following parameters should be set.
|
||||
* @param[out] lat2 latitude of point 2 (degrees).
|
||||
* @param[out] lon2 longitude of point 2 (degrees).
|
||||
* @param[out] S12 area under the rhumb line (meters<sup>2</sup>).
|
||||
*
|
||||
* The RhumbLine::mask values possible for \e outmask are
|
||||
* - \e outmask |= RhumbLine::LATITUDE for the latitude \e lat2;
|
||||
* - \e outmask |= RhumbLine::LONGITUDE for the latitude \e lon2;
|
||||
* - \e outmask |= RhumbLine::AREA for the area \e S12;
|
||||
* - \e outmask |= RhumbLine::ALL for all of the above;
|
||||
* - \e outmask |= RhumbLine::LONG_UNROLL to unroll \e lon2 instead of
|
||||
* wrapping it into the range [−180°, 180°].
|
||||
* .
|
||||
* With the RhumbLine::LONG_UNROLL bit set, the quantity \e lon2 − \e
|
||||
* lon1 indicates how many times and in what sense the rhumb line encircles
|
||||
* the ellipsoid.
|
||||
*
|
||||
* If \e s12 is large enough that the rhumb line crosses a pole, the
|
||||
* longitude of point 2 is indeterminate (a NaN is returned for \e lon2 and
|
||||
* \e S12).
|
||||
**********************************************************************/
|
||||
void GenPosition(real s12, unsigned outmask,
|
||||
real& lat2, real& lon2, real& S12) const;
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @return \e lat1 the latitude of point 1 (degrees).
|
||||
**********************************************************************/
|
||||
Math::real Latitude() const { return _lat1; }
|
||||
|
||||
/**
|
||||
* @return \e lon1 the longitude of point 1 (degrees).
|
||||
**********************************************************************/
|
||||
Math::real Longitude() const { return _lon1; }
|
||||
|
||||
/**
|
||||
* @return \e azi12 the azimuth of the rhumb line (degrees).
|
||||
**********************************************************************/
|
||||
Math::real Azimuth() const { return _azi12; }
|
||||
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value inherited from the Rhumb object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _rh.EquatorialRadius(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the value
|
||||
* inherited from the Rhumb object used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _rh.Flattening(); }
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#endif // GEOGRAPHICLIB_RHUMB_HPP
|
||||
+384
@@ -0,0 +1,384 @@
|
||||
/**
|
||||
* \file SphericalEngine.hpp
|
||||
* \brief Header for GeographicLib::SphericalEngine class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2011-2019) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_SPHERICALENGINE_HPP)
|
||||
#define GEOGRAPHICLIB_SPHERICALENGINE_HPP 1
|
||||
|
||||
#include <vector>
|
||||
#include <istream>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Squelch warnings about dll vs vector
|
||||
# pragma warning (push)
|
||||
# pragma warning (disable: 4251)
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
class CircularEngine;
|
||||
|
||||
/**
|
||||
* \brief The evaluation engine for SphericalHarmonic
|
||||
*
|
||||
* This serves as the backend to SphericalHarmonic, SphericalHarmonic1, and
|
||||
* SphericalHarmonic2. Typically end-users will not have to access this
|
||||
* class directly.
|
||||
*
|
||||
* See SphericalEngine.cpp for more information on the implementation.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-SphericalEngine.cpp
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT SphericalEngine {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
// CircularEngine needs access to sqrttable, scale
|
||||
friend class CircularEngine;
|
||||
// Return the table of the square roots of integers
|
||||
static std::vector<real>& sqrttable();
|
||||
// An internal scaling of the coefficients to avoid overflow in
|
||||
// intermediate calculations.
|
||||
static real scale() {
|
||||
using std::pow;
|
||||
static const real
|
||||
// Need extra real because, since C++11, pow(float, int) returns double
|
||||
s = real(pow(real(std::numeric_limits<real>::radix),
|
||||
-3 * (std::numeric_limits<real>::max_exponent < (1<<14) ?
|
||||
std::numeric_limits<real>::max_exponent : (1<<14))
|
||||
/ 5));
|
||||
return s;
|
||||
}
|
||||
// Move latitudes near the pole off the axis by this amount.
|
||||
static real eps() {
|
||||
using std::sqrt;
|
||||
return std::numeric_limits<real>::epsilon() *
|
||||
sqrt(std::numeric_limits<real>::epsilon());
|
||||
}
|
||||
SphericalEngine(); // Disable constructor
|
||||
public:
|
||||
/**
|
||||
* Supported normalizations for associated Legendre polynomials.
|
||||
**********************************************************************/
|
||||
enum normalization {
|
||||
/**
|
||||
* Fully normalized associated Legendre polynomials. See
|
||||
* SphericalHarmonic::FULL for documentation.
|
||||
*
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
FULL = 0,
|
||||
/**
|
||||
* Schmidt semi-normalized associated Legendre polynomials. See
|
||||
* SphericalHarmonic::SCHMIDT for documentation.
|
||||
*
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
SCHMIDT = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Package up coefficients for SphericalEngine
|
||||
*
|
||||
* This packages up the \e C, \e S coefficients and information about how
|
||||
* the coefficients are stored into a single structure. This allows a
|
||||
* vector of type SphericalEngine::coeff to be passed to
|
||||
* SphericalEngine::Value. This class also includes functions to aid
|
||||
* indexing into \e C and \e S.
|
||||
*
|
||||
* The storage layout of the coefficients is documented in
|
||||
* SphericalHarmonic and SphericalHarmonic::SphericalHarmonic.
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT coeff {
|
||||
private:
|
||||
int _nNx, _nmx, _mmx;
|
||||
std::vector<real>::const_iterator _cCnm;
|
||||
std::vector<real>::const_iterator _sSnm;
|
||||
public:
|
||||
/**
|
||||
* A default constructor
|
||||
**********************************************************************/
|
||||
coeff() : _nNx(-1) , _nmx(-1) , _mmx(-1) {}
|
||||
/**
|
||||
* The general constructor.
|
||||
*
|
||||
* @param[in] C a vector of coefficients for the cosine terms.
|
||||
* @param[in] S a vector of coefficients for the sine terms.
|
||||
* @param[in] N the degree giving storage layout for \e C and \e S.
|
||||
* @param[in] nmx the maximum degree to be used.
|
||||
* @param[in] mmx the maximum order to be used.
|
||||
* @exception GeographicErr if \e N, \e nmx, and \e mmx do not satisfy
|
||||
* \e N ≥ \e nmx ≥ \e mmx ≥ −1.
|
||||
* @exception GeographicErr if \e C or \e S is not big enough to hold the
|
||||
* coefficients.
|
||||
* @exception std::bad_alloc if the memory for the square root table
|
||||
* can't be allocated.
|
||||
**********************************************************************/
|
||||
coeff(const std::vector<real>& C,
|
||||
const std::vector<real>& S,
|
||||
int N, int nmx, int mmx)
|
||||
: _nNx(N)
|
||||
, _nmx(nmx)
|
||||
, _mmx(mmx)
|
||||
, _cCnm(C.begin())
|
||||
, _sSnm(S.begin())
|
||||
{
|
||||
if (!((_nNx >= _nmx && _nmx >= _mmx && _mmx >= 0) ||
|
||||
// If mmx = -1 then the sums are empty so require nmx = -1 also.
|
||||
(_nmx == -1 && _mmx == -1)))
|
||||
throw GeographicErr("Bad indices for coeff");
|
||||
if (!(index(_nmx, _mmx) < int(C.size()) &&
|
||||
index(_nmx, _mmx) < int(S.size()) + (_nNx + 1)))
|
||||
throw GeographicErr("Arrays too small in coeff");
|
||||
SphericalEngine::RootTable(_nmx);
|
||||
}
|
||||
/**
|
||||
* The constructor for full coefficient vectors.
|
||||
*
|
||||
* @param[in] C a vector of coefficients for the cosine terms.
|
||||
* @param[in] S a vector of coefficients for the sine terms.
|
||||
* @param[in] N the maximum degree and order.
|
||||
* @exception GeographicErr if \e N does not satisfy \e N ≥ −1.
|
||||
* @exception GeographicErr if \e C or \e S is not big enough to hold the
|
||||
* coefficients.
|
||||
* @exception std::bad_alloc if the memory for the square root table
|
||||
* can't be allocated.
|
||||
**********************************************************************/
|
||||
coeff(const std::vector<real>& C,
|
||||
const std::vector<real>& S,
|
||||
int N)
|
||||
: _nNx(N)
|
||||
, _nmx(N)
|
||||
, _mmx(N)
|
||||
, _cCnm(C.begin())
|
||||
, _sSnm(S.begin())
|
||||
{
|
||||
if (!(_nNx >= -1))
|
||||
throw GeographicErr("Bad indices for coeff");
|
||||
if (!(index(_nmx, _mmx) < int(C.size()) &&
|
||||
index(_nmx, _mmx) < int(S.size()) + (_nNx + 1)))
|
||||
throw GeographicErr("Arrays too small in coeff");
|
||||
SphericalEngine::RootTable(_nmx);
|
||||
}
|
||||
/**
|
||||
* @return \e N the degree giving storage layout for \e C and \e S.
|
||||
**********************************************************************/
|
||||
int N() const { return _nNx; }
|
||||
/**
|
||||
* @return \e nmx the maximum degree to be used.
|
||||
**********************************************************************/
|
||||
int nmx() const { return _nmx; }
|
||||
/**
|
||||
* @return \e mmx the maximum order to be used.
|
||||
**********************************************************************/
|
||||
int mmx() const { return _mmx; }
|
||||
/**
|
||||
* The one-dimensional index into \e C and \e S.
|
||||
*
|
||||
* @param[in] n the degree.
|
||||
* @param[in] m the order.
|
||||
* @return the one-dimensional index.
|
||||
**********************************************************************/
|
||||
int index(int n, int m) const
|
||||
{ return m * _nNx - m * (m - 1) / 2 + n; }
|
||||
/**
|
||||
* An element of \e C.
|
||||
*
|
||||
* @param[in] k the one-dimensional index.
|
||||
* @return the value of the \e C coefficient.
|
||||
**********************************************************************/
|
||||
Math::real Cv(int k) const { return *(_cCnm + k); }
|
||||
/**
|
||||
* An element of \e S.
|
||||
*
|
||||
* @param[in] k the one-dimensional index.
|
||||
* @return the value of the \e S coefficient.
|
||||
**********************************************************************/
|
||||
Math::real Sv(int k) const { return *(_sSnm + (k - (_nNx + 1))); }
|
||||
/**
|
||||
* An element of \e C with checking.
|
||||
*
|
||||
* @param[in] k the one-dimensional index.
|
||||
* @param[in] n the requested degree.
|
||||
* @param[in] m the requested order.
|
||||
* @param[in] f a multiplier.
|
||||
* @return the value of the \e C coefficient multiplied by \e f in \e n
|
||||
* and \e m are in range else 0.
|
||||
**********************************************************************/
|
||||
Math::real Cv(int k, int n, int m, real f) const
|
||||
{ return m > _mmx || n > _nmx ? 0 : *(_cCnm + k) * f; }
|
||||
/**
|
||||
* An element of \e S with checking.
|
||||
*
|
||||
* @param[in] k the one-dimensional index.
|
||||
* @param[in] n the requested degree.
|
||||
* @param[in] m the requested order.
|
||||
* @param[in] f a multiplier.
|
||||
* @return the value of the \e S coefficient multiplied by \e f in \e n
|
||||
* and \e m are in range else 0.
|
||||
**********************************************************************/
|
||||
Math::real Sv(int k, int n, int m, real f) const
|
||||
{ return m > _mmx || n > _nmx ? 0 : *(_sSnm + (k - (_nNx + 1))) * f; }
|
||||
|
||||
/**
|
||||
* The size of the coefficient vector for the cosine terms.
|
||||
*
|
||||
* @param[in] N the maximum degree.
|
||||
* @param[in] M the maximum order.
|
||||
* @return the size of the vector of cosine terms as stored in column
|
||||
* major order.
|
||||
**********************************************************************/
|
||||
static int Csize(int N, int M)
|
||||
{ return (M + 1) * (2 * N - M + 2) / 2; }
|
||||
|
||||
/**
|
||||
* The size of the coefficient vector for the sine terms.
|
||||
*
|
||||
* @param[in] N the maximum degree.
|
||||
* @param[in] M the maximum order.
|
||||
* @return the size of the vector of cosine terms as stored in column
|
||||
* major order.
|
||||
**********************************************************************/
|
||||
static int Ssize(int N, int M)
|
||||
{ return Csize(N, M) - (N + 1); }
|
||||
|
||||
/**
|
||||
* Load coefficients from a binary stream.
|
||||
*
|
||||
* @param[in] stream the input stream.
|
||||
* @param[in,out] N The maximum degree of the coefficients.
|
||||
* @param[in,out] M The maximum order of the coefficients.
|
||||
* @param[out] C The vector of cosine coefficients.
|
||||
* @param[out] S The vector of sine coefficients.
|
||||
* @param[in] truncate if false (the default) then \e N and \e M are
|
||||
* determined by the values in the binary stream; otherwise, the input
|
||||
* values of \e N and \e M are used to truncate the coefficients read
|
||||
* from the stream at the given degree and order.
|
||||
* @exception GeographicErr if \e N and \e M do not satisfy \e N ≥
|
||||
* \e M ≥ −1.
|
||||
* @exception GeographicErr if there's an error reading the data.
|
||||
* @exception std::bad_alloc if the memory for \e C or \e S can't be
|
||||
* allocated.
|
||||
*
|
||||
* \e N and \e M are read as 4-byte ints. \e C and \e S are resized to
|
||||
* accommodate all the coefficients (with the \e m = 0 coefficients for
|
||||
* \e S excluded) and the data for these coefficients read as 8-byte
|
||||
* doubles. The coefficients are stored in column major order. The
|
||||
* bytes in the stream should use little-endian ordering. IEEE floating
|
||||
* point is assumed for the coefficients.
|
||||
**********************************************************************/
|
||||
static void readcoeffs(std::istream& stream, int& N, int& M,
|
||||
std::vector<real>& C, std::vector<real>& S,
|
||||
bool truncate = false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate a spherical harmonic sum and its gradient.
|
||||
*
|
||||
* @tparam gradp should the gradient be calculated.
|
||||
* @tparam norm the normalization for the associated Legendre polynomials.
|
||||
* @tparam L the number of terms in the coefficients.
|
||||
* @param[in] c an array of coeff objects.
|
||||
* @param[in] f array of coefficient multipliers. f[0] should be 1.
|
||||
* @param[in] x the \e x component of the cartesian position.
|
||||
* @param[in] y the \e y component of the cartesian position.
|
||||
* @param[in] z the \e z component of the cartesian position.
|
||||
* @param[in] a the normalizing radius.
|
||||
* @param[out] gradx the \e x component of the gradient.
|
||||
* @param[out] grady the \e y component of the gradient.
|
||||
* @param[out] gradz the \e z component of the gradient.
|
||||
* @result the spherical harmonic sum.
|
||||
*
|
||||
* See the SphericalHarmonic class for the definition of the sum. The
|
||||
* coefficients used by this function are, for example, c[0].Cv + f[1] *
|
||||
* c[1].Cv + ... + f[L−1] * c[L−1].Cv. (Note that f[0] is \e
|
||||
* not used.) The upper limits on the sum are determined by c[0].nmx() and
|
||||
* c[0].mmx(); these limits apply to \e all the components of the
|
||||
* coefficients. The parameters \e gradp, \e norm, and \e L are template
|
||||
* parameters, to allow more optimization to be done at compile time.
|
||||
*
|
||||
* Clenshaw summation is used which permits the evaluation of the sum
|
||||
* without the need to allocate temporary arrays. Thus this function never
|
||||
* throws an exception.
|
||||
**********************************************************************/
|
||||
template<bool gradp, normalization norm, int L>
|
||||
static Math::real Value(const coeff c[], const real f[],
|
||||
real x, real y, real z, real a,
|
||||
real& gradx, real& grady, real& gradz);
|
||||
|
||||
/**
|
||||
* Create a CircularEngine object
|
||||
*
|
||||
* @tparam gradp should the gradient be calculated.
|
||||
* @tparam norm the normalization for the associated Legendre polynomials.
|
||||
* @tparam L the number of terms in the coefficients.
|
||||
* @param[in] c an array of coeff objects.
|
||||
* @param[in] f array of coefficient multipliers. f[0] should be 1.
|
||||
* @param[in] p the radius of the circle = sqrt(<i>x</i><sup>2</sup> +
|
||||
* <i>y</i><sup>2</sup>).
|
||||
* @param[in] z the height of the circle.
|
||||
* @param[in] a the normalizing radius.
|
||||
* @exception std::bad_alloc if the memory for the CircularEngine can't be
|
||||
* allocated.
|
||||
* @result the CircularEngine object.
|
||||
*
|
||||
* If you need to evaluate the spherical harmonic sum for several points
|
||||
* with constant \e f, \e p = sqrt(<i>x</i><sup>2</sup> +
|
||||
* <i>y</i><sup>2</sup>), \e z, and \e a, it is more efficient to construct
|
||||
* call SphericalEngine::Circle to give a CircularEngine object and then
|
||||
* call CircularEngine::operator()() with arguments <i>x</i>/\e p and
|
||||
* <i>y</i>/\e p.
|
||||
**********************************************************************/
|
||||
template<bool gradp, normalization norm, int L>
|
||||
static CircularEngine Circle(const coeff c[], const real f[],
|
||||
real p, real z, real a);
|
||||
/**
|
||||
* Check that the static table of square roots is big enough and enlarge it
|
||||
* if necessary.
|
||||
*
|
||||
* @param[in] N the maximum degree to be used in SphericalEngine.
|
||||
* @exception std::bad_alloc if the memory for the square root table can't
|
||||
* be allocated.
|
||||
*
|
||||
* Typically, there's no need for an end-user to call this routine, because
|
||||
* the constructors for SphericalEngine::coeff do so. However, since this
|
||||
* updates a static table, there's a possible race condition in a
|
||||
* multi-threaded environment. Because this routine does nothing if the
|
||||
* table is already large enough, one way to avoid race conditions is to
|
||||
* call this routine at program start up (when it's still single threaded),
|
||||
* supplying the largest degree that your program will use. E.g., \code
|
||||
GeographicLib::SphericalEngine::RootTable(2190);
|
||||
\endcode
|
||||
* suffices to accommodate extant magnetic and gravity models.
|
||||
**********************************************************************/
|
||||
static void RootTable(int N);
|
||||
|
||||
/**
|
||||
* Clear the static table of square roots and release the memory. Call
|
||||
* this only when you are sure you no longer will be using SphericalEngine.
|
||||
* Your program will crash if you call SphericalEngine after calling this
|
||||
* routine.
|
||||
*
|
||||
* \warning It's safest not to call this routine at all. (The space used
|
||||
* by the table is modest.)
|
||||
**********************************************************************/
|
||||
static void ClearRootTable() {
|
||||
std::vector<real> temp(0);
|
||||
sqrttable().swap(temp);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#endif // GEOGRAPHICLIB_SPHERICALENGINE_HPP
|
||||
+358
@@ -0,0 +1,358 @@
|
||||
/**
|
||||
* \file SphericalHarmonic.hpp
|
||||
* \brief Header for GeographicLib::SphericalHarmonic class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2011-2019) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_SPHERICALHARMONIC_HPP)
|
||||
#define GEOGRAPHICLIB_SPHERICALHARMONIC_HPP 1
|
||||
|
||||
#include <vector>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/SphericalEngine.hpp>
|
||||
#include <GeographicLib/CircularEngine.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Spherical harmonic series
|
||||
*
|
||||
* This class evaluates the spherical harmonic sum \verbatim
|
||||
V(x, y, z) = sum(n = 0..N)[ q^(n+1) * sum(m = 0..n)[
|
||||
(C[n,m] * cos(m*lambda) + S[n,m] * sin(m*lambda)) *
|
||||
P[n,m](cos(theta)) ] ]
|
||||
\endverbatim
|
||||
* where
|
||||
* - <i>p</i><sup>2</sup> = <i>x</i><sup>2</sup> + <i>y</i><sup>2</sup>,
|
||||
* - <i>r</i><sup>2</sup> = <i>p</i><sup>2</sup> + <i>z</i><sup>2</sup>,
|
||||
* - \e q = <i>a</i>/<i>r</i>,
|
||||
* - θ = atan2(\e p, \e z) = the spherical \e colatitude,
|
||||
* - λ = atan2(\e y, \e x) = the longitude.
|
||||
* - P<sub><i>nm</i></sub>(\e t) is the associated Legendre polynomial of
|
||||
* degree \e n and order \e m.
|
||||
*
|
||||
* Two normalizations are supported for P<sub><i>nm</i></sub>
|
||||
* - fully normalized denoted by SphericalHarmonic::FULL.
|
||||
* - Schmidt semi-normalized denoted by SphericalHarmonic::SCHMIDT.
|
||||
*
|
||||
* Clenshaw summation is used for the sums over both \e n and \e m. This
|
||||
* allows the computation to be carried out without the need for any
|
||||
* temporary arrays. See SphericalEngine.cpp for more information on the
|
||||
* implementation.
|
||||
*
|
||||
* References:
|
||||
* - C. W. Clenshaw,
|
||||
* <a href="https://doi.org/10.1090/S0025-5718-1955-0071856-0">
|
||||
* A note on the summation of Chebyshev series</a>,
|
||||
* %Math. Tables Aids Comput. 9(51), 118--120 (1955).
|
||||
* - R. E. Deakin, Derivatives of the earth's potentials, Geomatics
|
||||
* Research Australasia 68, 31--60, (June 1998).
|
||||
* - W. A. Heiskanen and H. Moritz, Physical Geodesy, (Freeman, San
|
||||
* Francisco, 1967).
|
||||
* https://archive.org/details/HeiskanenMoritz1967PhysicalGeodesy
|
||||
* (See Sec. 1-14, for a definition of Pbar.)
|
||||
* - S. A. Holmes and W. E. Featherstone,
|
||||
* <a href="https://doi.org/10.1007/s00190-002-0216-2">
|
||||
* A unified approach to the Clenshaw summation and the recursive
|
||||
* computation of very high degree and order normalised associated Legendre
|
||||
* functions</a>, J. Geodesy 76(5), 279--299 (2002).
|
||||
* - C. C. Tscherning and K. Poder,
|
||||
* <a href="http://cct.gfy.ku.dk/publ_cct/cct80.pdf">
|
||||
* Some geodetic applications of Clenshaw summation</a>,
|
||||
* Boll. Geod. Sci. Aff. 41(4), 349--375 (1982).
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-SphericalHarmonic.cpp
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT SphericalHarmonic {
|
||||
public:
|
||||
/**
|
||||
* Supported normalizations for the associated Legendre polynomials.
|
||||
**********************************************************************/
|
||||
enum normalization {
|
||||
/**
|
||||
* Fully normalized associated Legendre polynomials.
|
||||
*
|
||||
* These are defined by
|
||||
* <i>P</i><sub><i>nm</i></sub><sup>full</sup>(\e z)
|
||||
* = (−1)<sup><i>m</i></sup>
|
||||
* sqrt(\e k (2\e n + 1) (\e n − \e m)! / (\e n + \e m)!)
|
||||
* <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z), where
|
||||
* <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z) is Ferrers
|
||||
* function (also known as the Legendre function on the cut or the
|
||||
* associated Legendre polynomial) https://dlmf.nist.gov/14.7.E10 and
|
||||
* \e k = 1 for \e m = 0 and \e k = 2 otherwise.
|
||||
*
|
||||
* The mean squared value of
|
||||
* <i>P</i><sub><i>nm</i></sub><sup>full</sup>(cosθ)
|
||||
* cos(<i>m</i>λ) and
|
||||
* <i>P</i><sub><i>nm</i></sub><sup>full</sup>(cosθ)
|
||||
* sin(<i>m</i>λ) over the sphere is 1.
|
||||
*
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
FULL = SphericalEngine::FULL,
|
||||
/**
|
||||
* Schmidt semi-normalized associated Legendre polynomials.
|
||||
*
|
||||
* These are defined by
|
||||
* <i>P</i><sub><i>nm</i></sub><sup>schmidt</sup>(\e z)
|
||||
* = (−1)<sup><i>m</i></sup>
|
||||
* sqrt(\e k (\e n − \e m)! / (\e n + \e m)!)
|
||||
* <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z), where
|
||||
* <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z) is Ferrers
|
||||
* function (also known as the Legendre function on the cut or the
|
||||
* associated Legendre polynomial) https://dlmf.nist.gov/14.7.E10 and
|
||||
* \e k = 1 for \e m = 0 and \e k = 2 otherwise.
|
||||
*
|
||||
* The mean squared value of
|
||||
* <i>P</i><sub><i>nm</i></sub><sup>schmidt</sup>(cosθ)
|
||||
* cos(<i>m</i>λ) and
|
||||
* <i>P</i><sub><i>nm</i></sub><sup>schmidt</sup>(cosθ)
|
||||
* sin(<i>m</i>λ) over the sphere is 1/(2\e n + 1).
|
||||
*
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
SCHMIDT = SphericalEngine::SCHMIDT,
|
||||
};
|
||||
|
||||
private:
|
||||
typedef Math::real real;
|
||||
SphericalEngine::coeff _c[1];
|
||||
real _a;
|
||||
unsigned _norm;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor with a full set of coefficients specified.
|
||||
*
|
||||
* @param[in] C the coefficients <i>C</i><sub><i>nm</i></sub>.
|
||||
* @param[in] S the coefficients <i>S</i><sub><i>nm</i></sub>.
|
||||
* @param[in] N the maximum degree and order of the sum
|
||||
* @param[in] a the reference radius appearing in the definition of the
|
||||
* sum.
|
||||
* @param[in] norm the normalization for the associated Legendre
|
||||
* polynomials, either SphericalHarmonic::FULL (the default) or
|
||||
* SphericalHarmonic::SCHMIDT.
|
||||
* @exception GeographicErr if \e N does not satisfy \e N ≥ −1.
|
||||
* @exception GeographicErr if \e C or \e S is not big enough to hold the
|
||||
* coefficients.
|
||||
*
|
||||
* The coefficients <i>C</i><sub><i>nm</i></sub> and
|
||||
* <i>S</i><sub><i>nm</i></sub> are stored in the one-dimensional vectors
|
||||
* \e C and \e S which must contain (\e N + 1)(\e N + 2)/2 and \e N (\e N +
|
||||
* 1)/2 elements, respectively, stored in "column-major" order. Thus for
|
||||
* \e N = 3, the order would be:
|
||||
* <i>C</i><sub>00</sub>,
|
||||
* <i>C</i><sub>10</sub>,
|
||||
* <i>C</i><sub>20</sub>,
|
||||
* <i>C</i><sub>30</sub>,
|
||||
* <i>C</i><sub>11</sub>,
|
||||
* <i>C</i><sub>21</sub>,
|
||||
* <i>C</i><sub>31</sub>,
|
||||
* <i>C</i><sub>22</sub>,
|
||||
* <i>C</i><sub>32</sub>,
|
||||
* <i>C</i><sub>33</sub>.
|
||||
* In general the (\e n,\e m) element is at index \e m \e N − \e m
|
||||
* (\e m − 1)/2 + \e n. The layout of \e S is the same except that
|
||||
* the first column is omitted (since the \e m = 0 terms never contribute
|
||||
* to the sum) and the 0th element is <i>S</i><sub>11</sub>
|
||||
*
|
||||
* The class stores <i>pointers</i> to the first elements of \e C and \e S.
|
||||
* These arrays should not be altered or destroyed during the lifetime of a
|
||||
* SphericalHarmonic object.
|
||||
**********************************************************************/
|
||||
SphericalHarmonic(const std::vector<real>& C,
|
||||
const std::vector<real>& S,
|
||||
int N, real a, unsigned norm = FULL)
|
||||
: _a(a)
|
||||
, _norm(norm)
|
||||
{ _c[0] = SphericalEngine::coeff(C, S, N); }
|
||||
|
||||
/**
|
||||
* Constructor with a subset of coefficients specified.
|
||||
*
|
||||
* @param[in] C the coefficients <i>C</i><sub><i>nm</i></sub>.
|
||||
* @param[in] S the coefficients <i>S</i><sub><i>nm</i></sub>.
|
||||
* @param[in] N the degree used to determine the layout of \e C and \e S.
|
||||
* @param[in] nmx the maximum degree used in the sum. The sum over \e n is
|
||||
* from 0 thru \e nmx.
|
||||
* @param[in] mmx the maximum order used in the sum. The sum over \e m is
|
||||
* from 0 thru min(\e n, \e mmx).
|
||||
* @param[in] a the reference radius appearing in the definition of the
|
||||
* sum.
|
||||
* @param[in] norm the normalization for the associated Legendre
|
||||
* polynomials, either SphericalHarmonic::FULL (the default) or
|
||||
* SphericalHarmonic::SCHMIDT.
|
||||
* @exception GeographicErr if \e N, \e nmx, and \e mmx do not satisfy
|
||||
* \e N ≥ \e nmx ≥ \e mmx ≥ −1.
|
||||
* @exception GeographicErr if \e C or \e S is not big enough to hold the
|
||||
* coefficients.
|
||||
*
|
||||
* The class stores <i>pointers</i> to the first elements of \e C and \e S.
|
||||
* These arrays should not be altered or destroyed during the lifetime of a
|
||||
* SphericalHarmonic object.
|
||||
**********************************************************************/
|
||||
SphericalHarmonic(const std::vector<real>& C,
|
||||
const std::vector<real>& S,
|
||||
int N, int nmx, int mmx,
|
||||
real a, unsigned norm = FULL)
|
||||
: _a(a)
|
||||
, _norm(norm)
|
||||
{ _c[0] = SphericalEngine::coeff(C, S, N, nmx, mmx); }
|
||||
|
||||
/**
|
||||
* A default constructor so that the object can be created when the
|
||||
* constructor for another object is initialized. This default object can
|
||||
* then be reset with the default copy assignment operator.
|
||||
**********************************************************************/
|
||||
SphericalHarmonic() {}
|
||||
|
||||
/**
|
||||
* Compute the spherical harmonic sum.
|
||||
*
|
||||
* @param[in] x cartesian coordinate.
|
||||
* @param[in] y cartesian coordinate.
|
||||
* @param[in] z cartesian coordinate.
|
||||
* @return \e V the spherical harmonic sum.
|
||||
*
|
||||
* This routine requires constant memory and thus never throws an
|
||||
* exception.
|
||||
**********************************************************************/
|
||||
Math::real operator()(real x, real y, real z) const {
|
||||
real f[] = {1};
|
||||
real v = 0;
|
||||
real dummy;
|
||||
switch (_norm) {
|
||||
case FULL:
|
||||
v = SphericalEngine::Value<false, SphericalEngine::FULL, 1>
|
||||
(_c, f, x, y, z, _a, dummy, dummy, dummy);
|
||||
break;
|
||||
case SCHMIDT:
|
||||
default: // To avoid compiler warnings
|
||||
v = SphericalEngine::Value<false, SphericalEngine::SCHMIDT, 1>
|
||||
(_c, f, x, y, z, _a, dummy, dummy, dummy);
|
||||
break;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a spherical harmonic sum and its gradient.
|
||||
*
|
||||
* @param[in] x cartesian coordinate.
|
||||
* @param[in] y cartesian coordinate.
|
||||
* @param[in] z cartesian coordinate.
|
||||
* @param[out] gradx \e x component of the gradient
|
||||
* @param[out] grady \e y component of the gradient
|
||||
* @param[out] gradz \e z component of the gradient
|
||||
* @return \e V the spherical harmonic sum.
|
||||
*
|
||||
* This is the same as the previous function, except that the components of
|
||||
* the gradients of the sum in the \e x, \e y, and \e z directions are
|
||||
* computed. This routine requires constant memory and thus never throws
|
||||
* an exception.
|
||||
**********************************************************************/
|
||||
Math::real operator()(real x, real y, real z,
|
||||
real& gradx, real& grady, real& gradz) const {
|
||||
real f[] = {1};
|
||||
real v = 0;
|
||||
switch (_norm) {
|
||||
case FULL:
|
||||
v = SphericalEngine::Value<true, SphericalEngine::FULL, 1>
|
||||
(_c, f, x, y, z, _a, gradx, grady, gradz);
|
||||
break;
|
||||
case SCHMIDT:
|
||||
default: // To avoid compiler warnings
|
||||
v = SphericalEngine::Value<true, SphericalEngine::SCHMIDT, 1>
|
||||
(_c, f, x, y, z, _a, gradx, grady, gradz);
|
||||
break;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a CircularEngine to allow the efficient evaluation of several
|
||||
* points on a circle of latitude.
|
||||
*
|
||||
* @param[in] p the radius of the circle.
|
||||
* @param[in] z the height of the circle above the equatorial plane.
|
||||
* @param[in] gradp if true the returned object will be able to compute the
|
||||
* gradient of the sum.
|
||||
* @exception std::bad_alloc if the memory for the CircularEngine can't be
|
||||
* allocated.
|
||||
* @return the CircularEngine object.
|
||||
*
|
||||
* SphericalHarmonic::operator()() exchanges the order of the sums in the
|
||||
* definition, i.e., ∑<sub><i>n</i> = 0..<i>N</i></sub>
|
||||
* ∑<sub><i>m</i> = 0..<i>n</i></sub> becomes ∑<sub><i>m</i> =
|
||||
* 0..<i>N</i></sub> ∑<sub><i>n</i> = <i>m</i>..<i>N</i></sub>.
|
||||
* SphericalHarmonic::Circle performs the inner sum over degree \e n (which
|
||||
* entails about <i>N</i><sup>2</sup> operations). Calling
|
||||
* CircularEngine::operator()() on the returned object performs the outer
|
||||
* sum over the order \e m (about \e N operations).
|
||||
*
|
||||
* Here's an example of computing the spherical sum at a sequence of
|
||||
* longitudes without using a CircularEngine object \code
|
||||
SphericalHarmonic h(...); // Create the SphericalHarmonic object
|
||||
double r = 2, lat = 33, lon0 = 44, dlon = 0.01;
|
||||
double
|
||||
phi = lat * Math::degree<double>(),
|
||||
z = r * sin(phi), p = r * cos(phi);
|
||||
for (int i = 0; i <= 100; ++i) {
|
||||
real
|
||||
lon = lon0 + i * dlon,
|
||||
lam = lon * Math::degree<double>();
|
||||
std::cout << lon << " " << h(p * cos(lam), p * sin(lam), z) << "\n";
|
||||
}
|
||||
\endcode
|
||||
* Here is the same calculation done using a CircularEngine object. This
|
||||
* will be about <i>N</i>/2 times faster. \code
|
||||
SphericalHarmonic h(...); // Create the SphericalHarmonic object
|
||||
double r = 2, lat = 33, lon0 = 44, dlon = 0.01;
|
||||
double
|
||||
phi = lat * Math::degree<double>(),
|
||||
z = r * sin(phi), p = r * cos(phi);
|
||||
CircularEngine c(h(p, z, false)); // Create the CircularEngine object
|
||||
for (int i = 0; i <= 100; ++i) {
|
||||
real
|
||||
lon = lon0 + i * dlon;
|
||||
std::cout << lon << " " << c(lon) << "\n";
|
||||
}
|
||||
\endcode
|
||||
**********************************************************************/
|
||||
CircularEngine Circle(real p, real z, bool gradp) const {
|
||||
real f[] = {1};
|
||||
switch (_norm) {
|
||||
case FULL:
|
||||
return gradp ?
|
||||
SphericalEngine::Circle<true, SphericalEngine::FULL, 1>
|
||||
(_c, f, p, z, _a) :
|
||||
SphericalEngine::Circle<false, SphericalEngine::FULL, 1>
|
||||
(_c, f, p, z, _a);
|
||||
break;
|
||||
case SCHMIDT:
|
||||
default: // To avoid compiler warnings
|
||||
return gradp ?
|
||||
SphericalEngine::Circle<true, SphericalEngine::SCHMIDT, 1>
|
||||
(_c, f, p, z, _a) :
|
||||
SphericalEngine::Circle<false, SphericalEngine::SCHMIDT, 1>
|
||||
(_c, f, p, z, _a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the zeroth SphericalEngine::coeff object.
|
||||
**********************************************************************/
|
||||
const SphericalEngine::coeff& Coefficients() const
|
||||
{ return _c[0]; }
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_SPHERICALHARMONIC_HPP
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
/**
|
||||
* \file SphericalHarmonic1.hpp
|
||||
* \brief Header for GeographicLib::SphericalHarmonic1 class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2011) <karney@alum.mit.edu> and licensed under
|
||||
* the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_SPHERICALHARMONIC1_HPP)
|
||||
#define GEOGRAPHICLIB_SPHERICALHARMONIC1_HPP 1
|
||||
|
||||
#include <vector>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/SphericalEngine.hpp>
|
||||
#include <GeographicLib/CircularEngine.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Spherical harmonic series with a correction to the coefficients
|
||||
*
|
||||
* This classes is similar to SphericalHarmonic, except that the coefficients
|
||||
* <i>C</i><sub><i>nm</i></sub> are replaced by
|
||||
* <i>C</i><sub><i>nm</i></sub> + \e tau <i>C'</i><sub><i>nm</i></sub> (and
|
||||
* similarly for <i>S</i><sub><i>nm</i></sub>).
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-SphericalHarmonic1.cpp
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT SphericalHarmonic1 {
|
||||
public:
|
||||
/**
|
||||
* Supported normalizations for associate Legendre polynomials.
|
||||
**********************************************************************/
|
||||
enum normalization {
|
||||
/**
|
||||
* Fully normalized associated Legendre polynomials. See
|
||||
* SphericalHarmonic::FULL for documentation.
|
||||
*
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
FULL = SphericalEngine::FULL,
|
||||
/**
|
||||
* Schmidt semi-normalized associated Legendre polynomials. See
|
||||
* SphericalHarmonic::SCHMIDT for documentation.
|
||||
*
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
SCHMIDT = SphericalEngine::SCHMIDT,
|
||||
};
|
||||
|
||||
private:
|
||||
typedef Math::real real;
|
||||
SphericalEngine::coeff _c[2];
|
||||
real _a;
|
||||
unsigned _norm;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor with a full set of coefficients specified.
|
||||
*
|
||||
* @param[in] C the coefficients <i>C</i><sub><i>nm</i></sub>.
|
||||
* @param[in] S the coefficients <i>S</i><sub><i>nm</i></sub>.
|
||||
* @param[in] N the maximum degree and order of the sum
|
||||
* @param[in] C1 the coefficients <i>C'</i><sub><i>nm</i></sub>.
|
||||
* @param[in] S1 the coefficients <i>S'</i><sub><i>nm</i></sub>.
|
||||
* @param[in] N1 the maximum degree and order of the correction
|
||||
* coefficients <i>C'</i><sub><i>nm</i></sub> and
|
||||
* <i>S'</i><sub><i>nm</i></sub>.
|
||||
* @param[in] a the reference radius appearing in the definition of the
|
||||
* sum.
|
||||
* @param[in] norm the normalization for the associated Legendre
|
||||
* polynomials, either SphericalHarmonic1::FULL (the default) or
|
||||
* SphericalHarmonic1::SCHMIDT.
|
||||
* @exception GeographicErr if \e N and \e N1 do not satisfy \e N ≥
|
||||
* \e N1 ≥ −1.
|
||||
* @exception GeographicErr if any of the vectors of coefficients is not
|
||||
* large enough.
|
||||
*
|
||||
* See SphericalHarmonic for the way the coefficients should be stored.
|
||||
*
|
||||
* The class stores <i>pointers</i> to the first elements of \e C, \e S, \e
|
||||
* C', and \e S'. These arrays should not be altered or destroyed during
|
||||
* the lifetime of a SphericalHarmonic object.
|
||||
**********************************************************************/
|
||||
SphericalHarmonic1(const std::vector<real>& C,
|
||||
const std::vector<real>& S,
|
||||
int N,
|
||||
const std::vector<real>& C1,
|
||||
const std::vector<real>& S1,
|
||||
int N1,
|
||||
real a, unsigned norm = FULL)
|
||||
: _a(a)
|
||||
, _norm(norm) {
|
||||
if (!(N1 <= N))
|
||||
throw GeographicErr("N1 cannot be larger that N");
|
||||
_c[0] = SphericalEngine::coeff(C, S, N);
|
||||
_c[1] = SphericalEngine::coeff(C1, S1, N1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with a subset of coefficients specified.
|
||||
*
|
||||
* @param[in] C the coefficients <i>C</i><sub><i>nm</i></sub>.
|
||||
* @param[in] S the coefficients <i>S</i><sub><i>nm</i></sub>.
|
||||
* @param[in] N the degree used to determine the layout of \e C and \e S.
|
||||
* @param[in] nmx the maximum degree used in the sum. The sum over \e n is
|
||||
* from 0 thru \e nmx.
|
||||
* @param[in] mmx the maximum order used in the sum. The sum over \e m is
|
||||
* from 0 thru min(\e n, \e mmx).
|
||||
* @param[in] C1 the coefficients <i>C'</i><sub><i>nm</i></sub>.
|
||||
* @param[in] S1 the coefficients <i>S'</i><sub><i>nm</i></sub>.
|
||||
* @param[in] N1 the degree used to determine the layout of \e C' and \e
|
||||
* S'.
|
||||
* @param[in] nmx1 the maximum degree used for \e C' and \e S'.
|
||||
* @param[in] mmx1 the maximum order used for \e C' and \e S'.
|
||||
* @param[in] a the reference radius appearing in the definition of the
|
||||
* sum.
|
||||
* @param[in] norm the normalization for the associated Legendre
|
||||
* polynomials, either SphericalHarmonic1::FULL (the default) or
|
||||
* SphericalHarmonic1::SCHMIDT.
|
||||
* @exception GeographicErr if the parameters do not satisfy \e N ≥ \e
|
||||
* nmx ≥ \e mmx ≥ −1; \e N1 ≥ \e nmx1 ≥ \e mmx1 ≥
|
||||
* −1; \e N ≥ \e N1; \e nmx ≥ \e nmx1; \e mmx ≥ \e mmx1.
|
||||
* @exception GeographicErr if any of the vectors of coefficients is not
|
||||
* large enough.
|
||||
*
|
||||
* The class stores <i>pointers</i> to the first elements of \e C, \e S, \e
|
||||
* C', and \e S'. These arrays should not be altered or destroyed during
|
||||
* the lifetime of a SphericalHarmonic object.
|
||||
**********************************************************************/
|
||||
SphericalHarmonic1(const std::vector<real>& C,
|
||||
const std::vector<real>& S,
|
||||
int N, int nmx, int mmx,
|
||||
const std::vector<real>& C1,
|
||||
const std::vector<real>& S1,
|
||||
int N1, int nmx1, int mmx1,
|
||||
real a, unsigned norm = FULL)
|
||||
: _a(a)
|
||||
, _norm(norm) {
|
||||
if (!(nmx1 <= nmx))
|
||||
throw GeographicErr("nmx1 cannot be larger that nmx");
|
||||
if (!(mmx1 <= mmx))
|
||||
throw GeographicErr("mmx1 cannot be larger that mmx");
|
||||
_c[0] = SphericalEngine::coeff(C, S, N, nmx, mmx);
|
||||
_c[1] = SphericalEngine::coeff(C1, S1, N1, nmx1, mmx1);
|
||||
}
|
||||
|
||||
/**
|
||||
* A default constructor so that the object can be created when the
|
||||
* constructor for another object is initialized. This default object can
|
||||
* then be reset with the default copy assignment operator.
|
||||
**********************************************************************/
|
||||
SphericalHarmonic1() {}
|
||||
|
||||
/**
|
||||
* Compute a spherical harmonic sum with a correction term.
|
||||
*
|
||||
* @param[in] tau multiplier for correction coefficients \e C' and \e S'.
|
||||
* @param[in] x cartesian coordinate.
|
||||
* @param[in] y cartesian coordinate.
|
||||
* @param[in] z cartesian coordinate.
|
||||
* @return \e V the spherical harmonic sum.
|
||||
*
|
||||
* This routine requires constant memory and thus never throws
|
||||
* an exception.
|
||||
**********************************************************************/
|
||||
Math::real operator()(real tau, real x, real y, real z) const {
|
||||
real f[] = {1, tau};
|
||||
real v = 0;
|
||||
real dummy;
|
||||
switch (_norm) {
|
||||
case FULL:
|
||||
v = SphericalEngine::Value<false, SphericalEngine::FULL, 2>
|
||||
(_c, f, x, y, z, _a, dummy, dummy, dummy);
|
||||
break;
|
||||
case SCHMIDT:
|
||||
default: // To avoid compiler warnings
|
||||
v = SphericalEngine::Value<false, SphericalEngine::SCHMIDT, 2>
|
||||
(_c, f, x, y, z, _a, dummy, dummy, dummy);
|
||||
break;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a spherical harmonic sum with a correction term and its
|
||||
* gradient.
|
||||
*
|
||||
* @param[in] tau multiplier for correction coefficients \e C' and \e S'.
|
||||
* @param[in] x cartesian coordinate.
|
||||
* @param[in] y cartesian coordinate.
|
||||
* @param[in] z cartesian coordinate.
|
||||
* @param[out] gradx \e x component of the gradient
|
||||
* @param[out] grady \e y component of the gradient
|
||||
* @param[out] gradz \e z component of the gradient
|
||||
* @return \e V the spherical harmonic sum.
|
||||
*
|
||||
* This is the same as the previous function, except that the components of
|
||||
* the gradients of the sum in the \e x, \e y, and \e z directions are
|
||||
* computed. This routine requires constant memory and thus never throws
|
||||
* an exception.
|
||||
**********************************************************************/
|
||||
Math::real operator()(real tau, real x, real y, real z,
|
||||
real& gradx, real& grady, real& gradz) const {
|
||||
real f[] = {1, tau};
|
||||
real v = 0;
|
||||
switch (_norm) {
|
||||
case FULL:
|
||||
v = SphericalEngine::Value<true, SphericalEngine::FULL, 2>
|
||||
(_c, f, x, y, z, _a, gradx, grady, gradz);
|
||||
break;
|
||||
case SCHMIDT:
|
||||
default: // To avoid compiler warnings
|
||||
v = SphericalEngine::Value<true, SphericalEngine::SCHMIDT, 2>
|
||||
(_c, f, x, y, z, _a, gradx, grady, gradz);
|
||||
break;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a CircularEngine to allow the efficient evaluation of several
|
||||
* points on a circle of latitude at a fixed value of \e tau.
|
||||
*
|
||||
* @param[in] tau the multiplier for the correction coefficients.
|
||||
* @param[in] p the radius of the circle.
|
||||
* @param[in] z the height of the circle above the equatorial plane.
|
||||
* @param[in] gradp if true the returned object will be able to compute the
|
||||
* gradient of the sum.
|
||||
* @exception std::bad_alloc if the memory for the CircularEngine can't be
|
||||
* allocated.
|
||||
* @return the CircularEngine object.
|
||||
*
|
||||
* SphericalHarmonic1::operator()() exchanges the order of the sums in the
|
||||
* definition, i.e., ∑<sub><i>n</i> = 0..<i>N</i></sub>
|
||||
* ∑<sub><i>m</i> = 0..<i>n</i></sub> becomes ∑<sub><i>m</i> =
|
||||
* 0..<i>N</i></sub> ∑<sub><i>n</i> = <i>m</i>..<i>N</i></sub>.
|
||||
* SphericalHarmonic1::Circle performs the inner sum over degree \e n
|
||||
* (which entails about <i>N</i><sup>2</sup> operations). Calling
|
||||
* CircularEngine::operator()() on the returned object performs the outer
|
||||
* sum over the order \e m (about \e N operations).
|
||||
*
|
||||
* See SphericalHarmonic::Circle for an example of its use.
|
||||
**********************************************************************/
|
||||
CircularEngine Circle(real tau, real p, real z, bool gradp) const {
|
||||
real f[] = {1, tau};
|
||||
switch (_norm) {
|
||||
case FULL:
|
||||
return gradp ?
|
||||
SphericalEngine::Circle<true, SphericalEngine::FULL, 2>
|
||||
(_c, f, p, z, _a) :
|
||||
SphericalEngine::Circle<false, SphericalEngine::FULL, 2>
|
||||
(_c, f, p, z, _a);
|
||||
break;
|
||||
case SCHMIDT:
|
||||
default: // To avoid compiler warnings
|
||||
return gradp ?
|
||||
SphericalEngine::Circle<true, SphericalEngine::SCHMIDT, 2>
|
||||
(_c, f, p, z, _a) :
|
||||
SphericalEngine::Circle<false, SphericalEngine::SCHMIDT, 2>
|
||||
(_c, f, p, z, _a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the zeroth SphericalEngine::coeff object.
|
||||
**********************************************************************/
|
||||
const SphericalEngine::coeff& Coefficients() const
|
||||
{ return _c[0]; }
|
||||
/**
|
||||
* @return the first SphericalEngine::coeff object.
|
||||
**********************************************************************/
|
||||
const SphericalEngine::coeff& Coefficients1() const
|
||||
{ return _c[1]; }
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_SPHERICALHARMONIC1_HPP
|
||||
+320
@@ -0,0 +1,320 @@
|
||||
/**
|
||||
* \file SphericalHarmonic2.hpp
|
||||
* \brief Header for GeographicLib::SphericalHarmonic2 class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2011-2012) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_SPHERICALHARMONIC2_HPP)
|
||||
#define GEOGRAPHICLIB_SPHERICALHARMONIC2_HPP 1
|
||||
|
||||
#include <vector>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/SphericalEngine.hpp>
|
||||
#include <GeographicLib/CircularEngine.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Spherical harmonic series with two corrections to the coefficients
|
||||
*
|
||||
* This classes is similar to SphericalHarmonic, except that the coefficients
|
||||
* <i>C</i><sub><i>nm</i></sub> are replaced by
|
||||
* <i>C</i><sub><i>nm</i></sub> + \e tau' <i>C'</i><sub><i>nm</i></sub> + \e
|
||||
* tau'' <i>C''</i><sub><i>nm</i></sub> (and similarly for
|
||||
* <i>S</i><sub><i>nm</i></sub>).
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-SphericalHarmonic2.cpp
|
||||
**********************************************************************/
|
||||
|
||||
// Don't include the GEOGRPAHIC_EXPORT because this header-only class isn't
|
||||
// used by any other classes in the library.
|
||||
class /*GEOGRAPHICLIB_EXPORT*/ SphericalHarmonic2 {
|
||||
public:
|
||||
/**
|
||||
* Supported normalizations for associate Legendre polynomials.
|
||||
**********************************************************************/
|
||||
enum normalization {
|
||||
/**
|
||||
* Fully normalized associated Legendre polynomials. See
|
||||
* SphericalHarmonic::FULL for documentation.
|
||||
*
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
FULL = SphericalEngine::FULL,
|
||||
/**
|
||||
* Schmidt semi-normalized associated Legendre polynomials. See
|
||||
* SphericalHarmonic::SCHMIDT for documentation.
|
||||
*
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
SCHMIDT = SphericalEngine::SCHMIDT,
|
||||
};
|
||||
|
||||
private:
|
||||
typedef Math::real real;
|
||||
SphericalEngine::coeff _c[3];
|
||||
real _a;
|
||||
unsigned _norm;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor with a full set of coefficients specified.
|
||||
*
|
||||
* @param[in] C the coefficients <i>C</i><sub><i>nm</i></sub>.
|
||||
* @param[in] S the coefficients <i>S</i><sub><i>nm</i></sub>.
|
||||
* @param[in] N the maximum degree and order of the sum
|
||||
* @param[in] C1 the coefficients <i>C'</i><sub><i>nm</i></sub>.
|
||||
* @param[in] S1 the coefficients <i>S'</i><sub><i>nm</i></sub>.
|
||||
* @param[in] N1 the maximum degree and order of the first correction
|
||||
* coefficients <i>C'</i><sub><i>nm</i></sub> and
|
||||
* <i>S'</i><sub><i>nm</i></sub>.
|
||||
* @param[in] C2 the coefficients <i>C''</i><sub><i>nm</i></sub>.
|
||||
* @param[in] S2 the coefficients <i>S''</i><sub><i>nm</i></sub>.
|
||||
* @param[in] N2 the maximum degree and order of the second correction
|
||||
* coefficients <i>C'</i><sub><i>nm</i></sub> and
|
||||
* <i>S'</i><sub><i>nm</i></sub>.
|
||||
* @param[in] a the reference radius appearing in the definition of the
|
||||
* sum.
|
||||
* @param[in] norm the normalization for the associated Legendre
|
||||
* polynomials, either SphericalHarmonic2::FULL (the default) or
|
||||
* SphericalHarmonic2::SCHMIDT.
|
||||
* @exception GeographicErr if \e N and \e N1 do not satisfy \e N ≥
|
||||
* \e N1 ≥ −1, and similarly for \e N2.
|
||||
* @exception GeographicErr if any of the vectors of coefficients is not
|
||||
* large enough.
|
||||
*
|
||||
* See SphericalHarmonic for the way the coefficients should be stored. \e
|
||||
* N1 and \e N2 should satisfy \e N1 ≤ \e N and \e N2 ≤ \e N.
|
||||
*
|
||||
* The class stores <i>pointers</i> to the first elements of \e C, \e S, \e
|
||||
* C', \e S', \e C'', and \e S''. These arrays should not be altered or
|
||||
* destroyed during the lifetime of a SphericalHarmonic object.
|
||||
**********************************************************************/
|
||||
SphericalHarmonic2(const std::vector<real>& C,
|
||||
const std::vector<real>& S,
|
||||
int N,
|
||||
const std::vector<real>& C1,
|
||||
const std::vector<real>& S1,
|
||||
int N1,
|
||||
const std::vector<real>& C2,
|
||||
const std::vector<real>& S2,
|
||||
int N2,
|
||||
real a, unsigned norm = FULL)
|
||||
: _a(a)
|
||||
, _norm(norm) {
|
||||
if (!(N1 <= N && N2 <= N))
|
||||
throw GeographicErr("N1 and N2 cannot be larger that N");
|
||||
_c[0] = SphericalEngine::coeff(C, S, N);
|
||||
_c[1] = SphericalEngine::coeff(C1, S1, N1);
|
||||
_c[2] = SphericalEngine::coeff(C2, S2, N2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with a subset of coefficients specified.
|
||||
*
|
||||
* @param[in] C the coefficients <i>C</i><sub><i>nm</i></sub>.
|
||||
* @param[in] S the coefficients <i>S</i><sub><i>nm</i></sub>.
|
||||
* @param[in] N the degree used to determine the layout of \e C and \e S.
|
||||
* @param[in] nmx the maximum degree used in the sum. The sum over \e n is
|
||||
* from 0 thru \e nmx.
|
||||
* @param[in] mmx the maximum order used in the sum. The sum over \e m is
|
||||
* from 0 thru min(\e n, \e mmx).
|
||||
* @param[in] C1 the coefficients <i>C'</i><sub><i>nm</i></sub>.
|
||||
* @param[in] S1 the coefficients <i>S'</i><sub><i>nm</i></sub>.
|
||||
* @param[in] N1 the degree used to determine the layout of \e C' and \e
|
||||
* S'.
|
||||
* @param[in] nmx1 the maximum degree used for \e C' and \e S'.
|
||||
* @param[in] mmx1 the maximum order used for \e C' and \e S'.
|
||||
* @param[in] C2 the coefficients <i>C''</i><sub><i>nm</i></sub>.
|
||||
* @param[in] S2 the coefficients <i>S''</i><sub><i>nm</i></sub>.
|
||||
* @param[in] N2 the degree used to determine the layout of \e C'' and \e
|
||||
* S''.
|
||||
* @param[in] nmx2 the maximum degree used for \e C'' and \e S''.
|
||||
* @param[in] mmx2 the maximum order used for \e C'' and \e S''.
|
||||
* @param[in] a the reference radius appearing in the definition of the
|
||||
* sum.
|
||||
* @param[in] norm the normalization for the associated Legendre
|
||||
* polynomials, either SphericalHarmonic2::FULL (the default) or
|
||||
* SphericalHarmonic2::SCHMIDT.
|
||||
* @exception GeographicErr if the parameters do not satisfy \e N ≥ \e
|
||||
* nmx ≥ \e mmx ≥ −1; \e N1 ≥ \e nmx1 ≥ \e mmx1 ≥
|
||||
* −1; \e N ≥ \e N1; \e nmx ≥ \e nmx1; \e mmx ≥ \e mmx1;
|
||||
* and similarly for \e N2, \e nmx2, and \e mmx2.
|
||||
* @exception GeographicErr if any of the vectors of coefficients is not
|
||||
* large enough.
|
||||
*
|
||||
* The class stores <i>pointers</i> to the first elements of \e C, \e S, \e
|
||||
* C', \e S', \e C'', and \e S''. These arrays should not be altered or
|
||||
* destroyed during the lifetime of a SphericalHarmonic object.
|
||||
**********************************************************************/
|
||||
SphericalHarmonic2(const std::vector<real>& C,
|
||||
const std::vector<real>& S,
|
||||
int N, int nmx, int mmx,
|
||||
const std::vector<real>& C1,
|
||||
const std::vector<real>& S1,
|
||||
int N1, int nmx1, int mmx1,
|
||||
const std::vector<real>& C2,
|
||||
const std::vector<real>& S2,
|
||||
int N2, int nmx2, int mmx2,
|
||||
real a, unsigned norm = FULL)
|
||||
: _a(a)
|
||||
, _norm(norm) {
|
||||
if (!(nmx1 <= nmx && nmx2 <= nmx))
|
||||
throw GeographicErr("nmx1 and nmx2 cannot be larger that nmx");
|
||||
if (!(mmx1 <= mmx && mmx2 <= mmx))
|
||||
throw GeographicErr("mmx1 and mmx2 cannot be larger that mmx");
|
||||
_c[0] = SphericalEngine::coeff(C, S, N, nmx, mmx);
|
||||
_c[1] = SphericalEngine::coeff(C1, S1, N1, nmx1, mmx1);
|
||||
_c[2] = SphericalEngine::coeff(C2, S2, N2, nmx2, mmx2);
|
||||
}
|
||||
|
||||
/**
|
||||
* A default constructor so that the object can be created when the
|
||||
* constructor for another object is initialized. This default object can
|
||||
* then be reset with the default copy assignment operator.
|
||||
**********************************************************************/
|
||||
SphericalHarmonic2() {}
|
||||
|
||||
/**
|
||||
* Compute a spherical harmonic sum with two correction terms.
|
||||
*
|
||||
* @param[in] tau1 multiplier for correction coefficients \e C' and \e S'.
|
||||
* @param[in] tau2 multiplier for correction coefficients \e C'' and \e
|
||||
* S''.
|
||||
* @param[in] x cartesian coordinate.
|
||||
* @param[in] y cartesian coordinate.
|
||||
* @param[in] z cartesian coordinate.
|
||||
* @return \e V the spherical harmonic sum.
|
||||
*
|
||||
* This routine requires constant memory and thus never throws an
|
||||
* exception.
|
||||
**********************************************************************/
|
||||
Math::real operator()(real tau1, real tau2, real x, real y, real z)
|
||||
const {
|
||||
real f[] = {1, tau1, tau2};
|
||||
real v = 0;
|
||||
real dummy;
|
||||
switch (_norm) {
|
||||
case FULL:
|
||||
v = SphericalEngine::Value<false, SphericalEngine::FULL, 3>
|
||||
(_c, f, x, y, z, _a, dummy, dummy, dummy);
|
||||
break;
|
||||
case SCHMIDT:
|
||||
default: // To avoid compiler warnings
|
||||
v = SphericalEngine::Value<false, SphericalEngine::SCHMIDT, 3>
|
||||
(_c, f, x, y, z, _a, dummy, dummy, dummy);
|
||||
break;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a spherical harmonic sum with two correction terms and its
|
||||
* gradient.
|
||||
*
|
||||
* @param[in] tau1 multiplier for correction coefficients \e C' and \e S'.
|
||||
* @param[in] tau2 multiplier for correction coefficients \e C'' and \e
|
||||
* S''.
|
||||
* @param[in] x cartesian coordinate.
|
||||
* @param[in] y cartesian coordinate.
|
||||
* @param[in] z cartesian coordinate.
|
||||
* @param[out] gradx \e x component of the gradient
|
||||
* @param[out] grady \e y component of the gradient
|
||||
* @param[out] gradz \e z component of the gradient
|
||||
* @return \e V the spherical harmonic sum.
|
||||
*
|
||||
* This is the same as the previous function, except that the components of
|
||||
* the gradients of the sum in the \e x, \e y, and \e z directions are
|
||||
* computed. This routine requires constant memory and thus never throws
|
||||
* an exception.
|
||||
**********************************************************************/
|
||||
Math::real operator()(real tau1, real tau2, real x, real y, real z,
|
||||
real& gradx, real& grady, real& gradz) const {
|
||||
real f[] = {1, tau1, tau2};
|
||||
real v = 0;
|
||||
switch (_norm) {
|
||||
case FULL:
|
||||
v = SphericalEngine::Value<true, SphericalEngine::FULL, 3>
|
||||
(_c, f, x, y, z, _a, gradx, grady, gradz);
|
||||
break;
|
||||
case SCHMIDT:
|
||||
default: // To avoid compiler warnings
|
||||
v = SphericalEngine::Value<true, SphericalEngine::SCHMIDT, 3>
|
||||
(_c, f, x, y, z, _a, gradx, grady, gradz);
|
||||
break;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a CircularEngine to allow the efficient evaluation of several
|
||||
* points on a circle of latitude at fixed values of \e tau1 and \e tau2.
|
||||
*
|
||||
* @param[in] tau1 multiplier for correction coefficients \e C' and \e S'.
|
||||
* @param[in] tau2 multiplier for correction coefficients \e C'' and \e
|
||||
* S''.
|
||||
* @param[in] p the radius of the circle.
|
||||
* @param[in] z the height of the circle above the equatorial plane.
|
||||
* @param[in] gradp if true the returned object will be able to compute the
|
||||
* gradient of the sum.
|
||||
* @exception std::bad_alloc if the memory for the CircularEngine can't be
|
||||
* allocated.
|
||||
* @return the CircularEngine object.
|
||||
*
|
||||
* SphericalHarmonic2::operator()() exchanges the order of the sums in the
|
||||
* definition, i.e., ∑<sub><i>n</i> = 0..<i>N</i></sub>
|
||||
* ∑<sub><i>m</i> = 0..<i>n</i></sub> becomes ∑<sub><i>m</i> =
|
||||
* 0..<i>N</i></sub> ∑<sub><i>n</i> = <i>m</i>..<i>N</i></sub>..
|
||||
* SphericalHarmonic2::Circle performs the inner sum over degree \e n
|
||||
* (which entails about <i>N</i><sup>2</sup> operations). Calling
|
||||
* CircularEngine::operator()() on the returned object performs the outer
|
||||
* sum over the order \e m (about \e N operations).
|
||||
*
|
||||
* See SphericalHarmonic::Circle for an example of its use.
|
||||
**********************************************************************/
|
||||
CircularEngine Circle(real tau1, real tau2, real p, real z, bool gradp)
|
||||
const {
|
||||
real f[] = {1, tau1, tau2};
|
||||
switch (_norm) {
|
||||
case FULL:
|
||||
return gradp ?
|
||||
SphericalEngine::Circle<true, SphericalEngine::FULL, 3>
|
||||
(_c, f, p, z, _a) :
|
||||
SphericalEngine::Circle<false, SphericalEngine::FULL, 3>
|
||||
(_c, f, p, z, _a);
|
||||
break;
|
||||
case SCHMIDT:
|
||||
default: // To avoid compiler warnings
|
||||
return gradp ?
|
||||
SphericalEngine::Circle<true, SphericalEngine::SCHMIDT, 3>
|
||||
(_c, f, p, z, _a) :
|
||||
SphericalEngine::Circle<false, SphericalEngine::SCHMIDT, 3>
|
||||
(_c, f, p, z, _a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the zeroth SphericalEngine::coeff object.
|
||||
**********************************************************************/
|
||||
const SphericalEngine::coeff& Coefficients() const
|
||||
{ return _c[0]; }
|
||||
/**
|
||||
* @return the first SphericalEngine::coeff object.
|
||||
**********************************************************************/
|
||||
const SphericalEngine::coeff& Coefficients1() const
|
||||
{ return _c[1]; }
|
||||
/**
|
||||
* @return the second SphericalEngine::coeff object.
|
||||
**********************************************************************/
|
||||
const SphericalEngine::coeff& Coefficients2() const
|
||||
{ return _c[2]; }
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_SPHERICALHARMONIC2_HPP
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* \file TransverseMercator.hpp
|
||||
* \brief Header for GeographicLib::TransverseMercator class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2008-2023) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_TRANSVERSEMERCATOR_HPP)
|
||||
#define GEOGRAPHICLIB_TRANSVERSEMERCATOR_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/TransverseMercatorExact.hpp>
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER)
|
||||
/**
|
||||
* The order of the series approximation used in TransverseMercator.
|
||||
* GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER can be set to any integer in [4, 8].
|
||||
**********************************************************************/
|
||||
# define GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER \
|
||||
(GEOGRAPHICLIB_PRECISION == 2 ? 6 : \
|
||||
(GEOGRAPHICLIB_PRECISION == 1 ? 4 : 8))
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Transverse Mercator projection
|
||||
*
|
||||
* This uses Krüger's method which evaluates the projection and its
|
||||
* inverse in terms of a series. See
|
||||
* - L. Krüger,
|
||||
* <a href="https://doi.org/10.2312/GFZ.b103-krueger28"> Konforme
|
||||
* Abbildung des Erdellipsoids in der Ebene</a> (Conformal mapping of the
|
||||
* ellipsoidal earth to the plane), Royal Prussian Geodetic Institute, New
|
||||
* Series 52, 172 pp. (1912).
|
||||
* - C. F. F. Karney,
|
||||
* <a href="https://doi.org/10.1007/s00190-011-0445-3">
|
||||
* Transverse Mercator with an accuracy of a few nanometers,</a>
|
||||
* J. Geodesy 85(8), 475--485 (Aug. 2011);
|
||||
* preprint
|
||||
* <a href="https://arxiv.org/abs/1002.1417">arXiv:1002.1417</a>.
|
||||
*
|
||||
* Krüger's method has been extended from 4th to 6th order. The maximum
|
||||
* error is 5 nm (5 nanometers), ground distance, for all positions within 35
|
||||
* degrees of the central meridian. The error in the convergence is 2
|
||||
* × 10<sup>−15</sup>" and the relative error in the scale
|
||||
* is 6 × 10<sup>−12</sup>%%. See Sec. 4 of
|
||||
* <a href="https://arxiv.org/abs/1002.1417">arXiv:1002.1417</a> for details.
|
||||
* The speed penalty in going to 6th order is only about 1%.
|
||||
*
|
||||
* There's a singularity in the projection at φ = 0°, λ
|
||||
* − λ<sub>0</sub> = ±(1 − \e e)90° (≈
|
||||
* ±82.6° for the WGS84 ellipsoid), where \e e is the
|
||||
* eccentricity. Beyond this point, the series ceases to converge and the
|
||||
* results from this method will be garbage. To be on the safe side, don't
|
||||
* use this method if the angular distance from the central meridian exceeds
|
||||
* (1 − 2e)90° (≈ 75° for the WGS84 ellipsoid)
|
||||
*
|
||||
* TransverseMercatorExact is an alternative implementation of the projection
|
||||
* using exact formulas which yield accurate (to 8 nm) results over the
|
||||
* entire ellipsoid. This formulation is accessible in this class by calling
|
||||
* the constructor with \e exact = true.
|
||||
*
|
||||
* The ellipsoid parameters and the central scale are set in the constructor.
|
||||
* The central meridian (which is a trivial shift of the longitude) is
|
||||
* specified as the \e lon0 argument of the TransverseMercator::Forward and
|
||||
* TransverseMercator::Reverse functions. The latitude of origin is taken to
|
||||
* be the equator. There is no provision in this class for specifying a
|
||||
* false easting or false northing or a different latitude of origin.
|
||||
* However these are can be simply included by the calling function. For
|
||||
* example, the UTMUPS class applies the false easting and false northing for
|
||||
* the UTM projections. A more complicated example is the British National
|
||||
* Grid (<a href="https://www.spatialreference.org/ref/epsg/7405/">
|
||||
* EPSG:7405</a>) which requires the use of a latitude of origin. This is
|
||||
* implemented by the GeographicLib::OSGB class.
|
||||
*
|
||||
* This class also returns the meridian convergence \e gamma and scale \e k.
|
||||
* The meridian convergence is the bearing of grid north (the \e y axis)
|
||||
* measured clockwise from true north.
|
||||
*
|
||||
* See TransverseMercator.cpp for more information on the implementation.
|
||||
*
|
||||
* See \ref transversemercator for a discussion of this projection.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-TransverseMercator.cpp
|
||||
*
|
||||
* <a href="TransverseMercatorProj.1.html">TransverseMercatorProj</a> is a
|
||||
* command-line utility providing access to the functionality of this class.
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT TransverseMercator {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
static const int maxpow_ = GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER;
|
||||
static const int numit_ = 5;
|
||||
real _a, _f, _k0;
|
||||
bool _exact;
|
||||
real _e2, _es, _e2m, _c, _n;
|
||||
// _alp[0] and _bet[0] unused
|
||||
real _a1, _b1, _alp[maxpow_ + 1], _bet[maxpow_ + 1];
|
||||
TransverseMercatorExact _tmexact;
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for an ellipsoid with
|
||||
*
|
||||
* @param[in] a equatorial radius (meters).
|
||||
* @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
|
||||
* Negative \e f gives a prolate ellipsoid.
|
||||
* @param[in] k0 central scale factor.
|
||||
* @param[in] exact if true use exact formulation in terms of elliptic
|
||||
* functions instead of series expansions (default false).
|
||||
* @param[in] extendp use extended domain (default false); should only be
|
||||
* used if \e exact = true;
|
||||
* @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k0 is
|
||||
* not positive.
|
||||
*
|
||||
* With \e exact = true, this class delegates the calculations to the
|
||||
* TransverseMercatorExact classes which compute the projection in terms of
|
||||
* elliptic functions.
|
||||
**********************************************************************/
|
||||
TransverseMercator(real a, real f, real k0,
|
||||
bool exact = false, bool extendp = false);
|
||||
|
||||
/**
|
||||
* Forward projection, from geographic to transverse Mercator.
|
||||
*
|
||||
* @param[in] lon0 central meridian of the projection (degrees).
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[out] x easting of point (meters).
|
||||
* @param[out] y northing of point (meters).
|
||||
* @param[out] gamma meridian convergence at point (degrees).
|
||||
* @param[out] k scale of projection at point.
|
||||
*
|
||||
* No false easting or northing is added. \e lat should be in the range
|
||||
* [−90°, 90°].
|
||||
**********************************************************************/
|
||||
void Forward(real lon0, real lat, real lon,
|
||||
real& x, real& y, real& gamma, real& k) const;
|
||||
|
||||
/**
|
||||
* Reverse projection, from transverse Mercator to geographic.
|
||||
*
|
||||
* @param[in] lon0 central meridian of the projection (degrees).
|
||||
* @param[in] x easting of point (meters).
|
||||
* @param[in] y northing of point (meters).
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] gamma meridian convergence at point (degrees).
|
||||
* @param[out] k scale of projection at point.
|
||||
*
|
||||
* No false easting or northing is added. The value of \e lon returned is
|
||||
* in the range [−180°, 180°].
|
||||
**********************************************************************/
|
||||
void Reverse(real lon0, real x, real y,
|
||||
real& lat, real& lon, real& gamma, real& k) const;
|
||||
|
||||
/**
|
||||
* TransverseMercator::Forward without returning the convergence and scale.
|
||||
**********************************************************************/
|
||||
void Forward(real lon0, real lat, real lon,
|
||||
real& x, real& y) const {
|
||||
real gamma, k;
|
||||
Forward(lon0, lat, lon, x, y, gamma, k);
|
||||
}
|
||||
|
||||
/**
|
||||
* TransverseMercator::Reverse without returning the convergence and scale.
|
||||
**********************************************************************/
|
||||
void Reverse(real lon0, real x, real y,
|
||||
real& lat, real& lon) const {
|
||||
real gamma, k;
|
||||
Reverse(lon0, x, y, lat, lon, gamma, k);
|
||||
}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _a; }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the value used in
|
||||
* the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _f; }
|
||||
|
||||
/**
|
||||
* @return \e k0 central scale for the projection. This is the value of \e
|
||||
* k0 used in the constructor and is the scale on the central meridian.
|
||||
**********************************************************************/
|
||||
Math::real CentralScale() const { return _k0; }
|
||||
|
||||
/**
|
||||
* @return \e exact whether the exact formulation is used. This is the
|
||||
* value used in the constructor.
|
||||
**********************************************************************/
|
||||
bool Exact() const { return _exact; }
|
||||
///@}
|
||||
|
||||
/**
|
||||
* A global instantiation of TransverseMercator with the WGS84 ellipsoid
|
||||
* and the UTM scale factor. However, unlike UTM, no false easting or
|
||||
* northing is added.
|
||||
**********************************************************************/
|
||||
static const TransverseMercator& UTM();
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_TRANSVERSEMERCATOR_HPP
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
/**
|
||||
* \file TransverseMercatorExact.hpp
|
||||
* \brief Header for GeographicLib::TransverseMercatorExact class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2008-2023) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_TRANSVERSEMERCATOREXACT_HPP)
|
||||
#define GEOGRAPHICLIB_TRANSVERSEMERCATOREXACT_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/EllipticFunction.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief An exact implementation of the transverse Mercator projection
|
||||
*
|
||||
* Implementation of the Transverse Mercator Projection given in
|
||||
* - L. P. Lee,
|
||||
* <a href="https://doi.org/10.3138/X687-1574-4325-WM62"> Conformal
|
||||
* Projections Based On Jacobian Elliptic Functions</a>, Part V of
|
||||
* Conformal Projections Based on Elliptic Functions,
|
||||
* (B. V. Gutsell, Toronto, 1976), 128pp.,
|
||||
* ISBN: 0919870163
|
||||
* (also appeared as:
|
||||
* Monograph 16, Suppl. No. 1 to Canadian Cartographer, Vol 13);
|
||||
* <a href="https://archive.org/details/conformalproject0000leel/page/92">
|
||||
* borrow from archive.org</a>.
|
||||
* - C. F. F. Karney,
|
||||
* <a href="https://doi.org/10.1007/s00190-011-0445-3">
|
||||
* Transverse Mercator with an accuracy of a few nanometers,</a>
|
||||
* J. Geodesy 85(8), 475--485 (Aug. 2011);
|
||||
* preprint
|
||||
* <a href="https://arxiv.org/abs/1002.1417">arXiv:1002.1417</a>.
|
||||
*
|
||||
* Lee gives the correct results for forward and reverse transformations
|
||||
* subject to the branch cut rules (see the description of the \e extendp
|
||||
* argument to the constructor). The maximum error is about 8 nm (8
|
||||
* nanometers), ground distance, for the forward and reverse transformations.
|
||||
* The error in the convergence is 2 × 10<sup>−15</sup>",
|
||||
* the relative error in the scale is 7 × 10<sup>−12</sup>%%.
|
||||
* See Sec. 3 of
|
||||
* <a href="https://arxiv.org/abs/1002.1417">arXiv:1002.1417</a> for details.
|
||||
* The method is "exact" in the sense that the errors are close to the
|
||||
* round-off limit and that no changes are needed in the algorithms for them
|
||||
* to be used with reals of a higher precision. Thus the errors using long
|
||||
* double (with a 64-bit fraction) are about 2000 times smaller than using
|
||||
* double (with a 53-bit fraction).
|
||||
*
|
||||
* This algorithm is about 4.5 times slower than the 6th-order Krüger
|
||||
* method, TransverseMercator, taking about 11 us for a combined forward and
|
||||
* reverse projection on a 2.66 GHz Intel machine (g++, version 4.3.0, -O3).
|
||||
*
|
||||
* The ellipsoid parameters and the central scale are set in the constructor.
|
||||
* The central meridian (which is a trivial shift of the longitude) is
|
||||
* specified as the \e lon0 argument of the TransverseMercatorExact::Forward
|
||||
* and TransverseMercatorExact::Reverse functions. The latitude of origin is
|
||||
* taken to be the equator. See the documentation on TransverseMercator for
|
||||
* how to include a false easting, false northing, or a latitude of origin.
|
||||
*
|
||||
* See <a href="https://geographiclib.sourceforge.io/tm-grid.kmz"
|
||||
* type="application/vnd.google-earth.kmz"> tm-grid.kmz</a>, for an
|
||||
* illustration of the transverse Mercator grid in Google Earth.
|
||||
*
|
||||
* This class also returns the meridian convergence \e gamma and scale \e k.
|
||||
* The meridian convergence is the bearing of grid north (the \e y axis)
|
||||
* measured clockwise from true north.
|
||||
*
|
||||
* See TransverseMercatorExact.cpp for more information on the
|
||||
* implementation.
|
||||
*
|
||||
* See \ref transversemercator for a discussion of this projection.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-TransverseMercatorExact.cpp
|
||||
*
|
||||
* <a href="TransverseMercatorProj.1.html">TransverseMercatorProj</a> is a
|
||||
* command-line utility providing access to the functionality of
|
||||
* TransverseMercator and TransverseMercatorExact.
|
||||
**********************************************************************/
|
||||
|
||||
class GEOGRAPHICLIB_EXPORT TransverseMercatorExact {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
friend class TransverseMercator; // Allow TM to call the default constructor
|
||||
// Private default constructor to support TransverseMercator(a, f, exact)
|
||||
TransverseMercatorExact() {}; // Do nothing; used with exact = false.
|
||||
static const int numit_ = 10;
|
||||
real tol_, tol2_, taytol_;
|
||||
real _a, _f, _k0, _mu, _mv, _e;
|
||||
bool _extendp;
|
||||
EllipticFunction _eEu, _eEv;
|
||||
|
||||
void zeta(real u, real snu, real cnu, real dnu,
|
||||
real v, real snv, real cnv, real dnv,
|
||||
real& taup, real& lam) const;
|
||||
|
||||
void dwdzeta(real u, real snu, real cnu, real dnu,
|
||||
real v, real snv, real cnv, real dnv,
|
||||
real& du, real& dv) const;
|
||||
|
||||
bool zetainv0(real psi, real lam, real& u, real& v) const;
|
||||
void zetainv(real taup, real lam, real& u, real& v) const;
|
||||
|
||||
void sigma(real u, real snu, real cnu, real dnu,
|
||||
real v, real snv, real cnv, real dnv,
|
||||
real& xi, real& eta) const;
|
||||
|
||||
void dwdsigma(real u, real snu, real cnu, real dnu,
|
||||
real v, real snv, real cnv, real dnv,
|
||||
real& du, real& dv) const;
|
||||
|
||||
bool sigmainv0(real xi, real eta, real& u, real& v) const;
|
||||
void sigmainv(real xi, real eta, real& u, real& v) const;
|
||||
|
||||
void Scale(real tau, real lam,
|
||||
real snu, real cnu, real dnu,
|
||||
real snv, real cnv, real dnv,
|
||||
real& gamma, real& k) const;
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for an ellipsoid with
|
||||
*
|
||||
* @param[in] a equatorial radius (meters).
|
||||
* @param[in] f flattening of ellipsoid.
|
||||
* @param[in] k0 central scale factor.
|
||||
* @param[in] extendp if true, use extended domain (default false).
|
||||
* @exception GeographicErr if \e a, \e f, or \e k0 is not positive.
|
||||
*
|
||||
* The transverse Mercator projection has a branch point singularity at \e
|
||||
* lat = 0 and \e lon − \e lon0 = 90 (1 − \e e) or (for
|
||||
* TransverseMercatorExact::UTM) x = 18381 km, y = 0m. The \e extendp
|
||||
* argument governs where the branch cut is placed. With \e extendp =
|
||||
* false, the "standard" convention is followed, namely the cut is placed
|
||||
* along \e x > 18381 km, \e y = 0m. Forward can be called with any \e lat
|
||||
* and \e lon then produces the transformation shown in Lee, Fig 46.
|
||||
* Reverse analytically continues this in the ± \e x direction. As
|
||||
* a consequence, Reverse may map multiple points to the same geographic
|
||||
* location; for example, for TransverseMercatorExact::UTM, \e x =
|
||||
* 22051449.037349 m, \e y = −7131237.022729 m and \e x =
|
||||
* 29735142.378357 m, \e y = 4235043.607933 m both map to \e lat =
|
||||
* −2°, \e lon = 88°.
|
||||
*
|
||||
* With \e extendp = true, the branch cut is moved to the lower left
|
||||
* quadrant. The various symmetries of the transverse Mercator projection
|
||||
* can be used to explore the projection on any sheet. In this mode the
|
||||
* domains of \e lat, \e lon, \e x, and \e y are restricted to
|
||||
* - the union of
|
||||
* - \e lat in [0, 90] and \e lon − \e lon0 in [0, 90]
|
||||
* - \e lat in (-90, 0] and \e lon − \e lon0 in [90 (1 − \e
|
||||
e), 90]
|
||||
* - the union of
|
||||
* - <i>x</i>/(\e k0 \e a) in [0, ∞) and
|
||||
* <i>y</i>/(\e k0 \e a) in [0, E(<i>e</i><sup>2</sup>)]
|
||||
* - <i>x</i>/(\e k0 \e a) in [K(1 − <i>e</i><sup>2</sup>) −
|
||||
* E(1 − <i>e</i><sup>2</sup>), ∞) and <i>y</i>/(\e k0 \e
|
||||
* a) in (−∞, 0]
|
||||
* .
|
||||
* See Sec. 5 of
|
||||
* <a href="https://arxiv.org/abs/1002.1417">arXiv:1002.1417</a> for a full
|
||||
* discussion of the treatment of the branch cut.
|
||||
*
|
||||
* The method will work for all ellipsoids used in terrestrial geodesy.
|
||||
* The method cannot be applied directly to the case of a sphere (\e f = 0)
|
||||
* because some the constants characterizing this method diverge in that
|
||||
* limit, and in practice, \e f should be larger than about
|
||||
* numeric_limits<real>::epsilon(). However, TransverseMercator treats the
|
||||
* sphere exactly.
|
||||
**********************************************************************/
|
||||
TransverseMercatorExact(real a, real f, real k0, bool extendp = false);
|
||||
|
||||
/**
|
||||
* Forward projection, from geographic to transverse Mercator.
|
||||
*
|
||||
* @param[in] lon0 central meridian of the projection (degrees).
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[out] x easting of point (meters).
|
||||
* @param[out] y northing of point (meters).
|
||||
* @param[out] gamma meridian convergence at point (degrees).
|
||||
* @param[out] k scale of projection at point.
|
||||
*
|
||||
* No false easting or northing is added. \e lat should be in the range
|
||||
* [−90°, 90°].
|
||||
**********************************************************************/
|
||||
void Forward(real lon0, real lat, real lon,
|
||||
real& x, real& y, real& gamma, real& k) const;
|
||||
|
||||
/**
|
||||
* Reverse projection, from transverse Mercator to geographic.
|
||||
*
|
||||
* @param[in] lon0 central meridian of the projection (degrees).
|
||||
* @param[in] x easting of point (meters).
|
||||
* @param[in] y northing of point (meters).
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] gamma meridian convergence at point (degrees).
|
||||
* @param[out] k scale of projection at point.
|
||||
*
|
||||
* No false easting or northing is added. The value of \e lon returned is
|
||||
* in the range [−180°, 180°].
|
||||
**********************************************************************/
|
||||
void Reverse(real lon0, real x, real y,
|
||||
real& lat, real& lon, real& gamma, real& k) const;
|
||||
|
||||
/**
|
||||
* TransverseMercatorExact::Forward without returning the convergence and
|
||||
* scale.
|
||||
**********************************************************************/
|
||||
void Forward(real lon0, real lat, real lon,
|
||||
real& x, real& y) const {
|
||||
real gamma, k;
|
||||
Forward(lon0, lat, lon, x, y, gamma, k);
|
||||
}
|
||||
|
||||
/**
|
||||
* TransverseMercatorExact::Reverse without returning the convergence and
|
||||
* scale.
|
||||
**********************************************************************/
|
||||
void Reverse(real lon0, real x, real y,
|
||||
real& lat, real& lon) const {
|
||||
real gamma, k;
|
||||
Reverse(lon0, x, y, lat, lon, gamma, k);
|
||||
}
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return \e a the equatorial radius of the ellipsoid (meters). This is
|
||||
* the value used in the constructor.
|
||||
**********************************************************************/
|
||||
Math::real EquatorialRadius() const { return _a; }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the ellipsoid. This is the value used in
|
||||
* the constructor.
|
||||
**********************************************************************/
|
||||
Math::real Flattening() const { return _f; }
|
||||
|
||||
/**
|
||||
* @return \e k0 central scale for the projection. This is the value of \e
|
||||
* k0 used in the constructor and is the scale on the central meridian.
|
||||
**********************************************************************/
|
||||
Math::real CentralScale() const { return _k0; }
|
||||
///@}
|
||||
|
||||
/**
|
||||
* A global instantiation of TransverseMercatorExact with the WGS84
|
||||
* ellipsoid and the UTM scale factor. However, unlike UTM, no false
|
||||
* easting or northing is added.
|
||||
**********************************************************************/
|
||||
static const TransverseMercatorExact& UTM();
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_TRANSVERSEMERCATOREXACT_HPP
|
||||
+597
@@ -0,0 +1,597 @@
|
||||
/**
|
||||
* \file Cartesian3.hpp
|
||||
* \brief Header for GeographicLib::Triaxial::Cartesian3 class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2025) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_CARTESIAN3_HPP)
|
||||
#define GEOGRAPHICLIB_CARTESIAN3_HPP 1
|
||||
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include <random>
|
||||
#include <GeographicLib/Triaxial/Ellipsoid3.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Squelch warnings about dll vs random
|
||||
# pragma warning (push)
|
||||
# pragma warning (disable: 4251)
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
namespace Triaxial {
|
||||
|
||||
/**
|
||||
* \brief Transformations between cartesian and triaxial coordinates
|
||||
*
|
||||
* The Cartesian3 class supports transformations between cartesian
|
||||
* coordinates and various coordinates for a triaxial ellipsoid. This is
|
||||
* covered in Appendices A and B of
|
||||
* - C. F. F. Karney,<br>
|
||||
* <a href="https://arxiv.org/abs/2511.01621">
|
||||
* Jacobi's solution for geodesics on a triaxial ellipsoid</a>,<br>
|
||||
* Technical Report, SRI International, Nov. 2025.<br>
|
||||
* <a href="https://arxiv.org/abs/2511.01621">arxiv:2511.01621</a>
|
||||
*
|
||||
* Besides ellipsoidal coordinates defined in Ellipsoid3, the following
|
||||
* coordinates are supported:
|
||||
* * geodetic coordinates \f$(\phi, \lambda)\f$ defined by
|
||||
* \f[
|
||||
* \hat{\mathbf U} =
|
||||
* [\cos\phi \cos\lambda, \cos\phi \sin\lambda, \sin\phi]^T,
|
||||
* \f]
|
||||
* where \f$\hat{\mathbf U}\f$ is the normal to the surface of the
|
||||
* ellipsoid.
|
||||
* * parametric coordinates \f$(\phi', \lambda')\f$ defined by
|
||||
* \f[
|
||||
* \mathbf R =
|
||||
* [a \cos\phi' \cos\lambda', b \cos\phi' \sin\lambda',
|
||||
* c \sin\phi']^T,
|
||||
* \f]
|
||||
* * geocentric coordinates \f$(\phi'', \lambda'')\f$ defined by
|
||||
* \f[
|
||||
* \hat{\mathbf R} =
|
||||
* [\cos\phi'' \cos\lambda'', \cos\phi'' \sin\lambda'', \sin\phi'']^T.
|
||||
* \f]
|
||||
* .
|
||||
* For each of these 3 coordinates, the "north pole" is at \f$[0, 0, c]^T\f$
|
||||
* and the origin for longitudes is \f$[a, 0, 0]^T\f$. We also define
|
||||
* alternate versions (named "geodetic*", etc., where the north pole is
|
||||
* placed at \f$[a, 0, 0]^T\f$ and the origin for longitude is \f$[0, 0,
|
||||
* -c]\f$. This latter set of coordinates is appropriate for ellipsoids that
|
||||
* are nearly prolate.
|
||||
*
|
||||
* Directions on the ellipsoid are easily specified in cartesian coordinates
|
||||
* as a vector tangent to the surface of the ellipsoid. This is converted to
|
||||
* a heading by defined the angle the vector makes (measured clockwise) from
|
||||
* the coordinate-specific north. This is defined as the direction of a line
|
||||
* of constant (coordinate-specific) longitude. The resulting heading is
|
||||
* denoted by \f$\alpha\f$ for ellipsoidal coordinates and by \f$\zeta\f$ for
|
||||
* the other coordinates. The unstarred coordinates all share the same
|
||||
* direction for north, and likewise for the starred coordinates. Note that
|
||||
* the lines of constant longitude and latitude are only orthogonal (in
|
||||
* general) for ellipsoidal coordinates.
|
||||
*
|
||||
* Arbitrary points (not necessarily lying on the ellipsoid) an additional
|
||||
* "height" is required to specify the position. For ellipsoidal
|
||||
* coordinates, we find the confocal ellipsoid on which the point lies and
|
||||
* the height is then defined as \f$H = u - c\f$ where \f$u\f$ is the
|
||||
* semiminor axes of the confocal ellipsoid; the ellipsoid latitude and
|
||||
* longitude are those for the confocal ellipsoid For the other coordinates
|
||||
* systems, we define \f$h\f$ a the height above the closest point on the
|
||||
* ellipsoid and the latitude and longitude refer to the closest point.
|
||||
*
|
||||
* \note The family of confocal ellipsoids has semiaxes \f$[\sqrt{a^2 - c^2 +
|
||||
* u^2}, \sqrt{b^2 - c^2 + u^2}, u]\f$.
|
||||
*
|
||||
* \note In the function names "any" stands for any of the seven coordinate
|
||||
* systems enumerated by Cartesian3::coord. "cart2" refers to a point
|
||||
* given in cartesian coordinates that lies on the ellipsoid. On the other
|
||||
* hand, "cart" refers to an arbitrary point.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Cartesian3.cpp
|
||||
*
|
||||
* <a href="Cart3Convert.1.html">Cart3Convert</a> is a command-line utility
|
||||
* providing access to the functionality of Cartestian3.
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT Cartesian3 {
|
||||
public:
|
||||
/**
|
||||
* A type to hold three-dimensional positions and directions in cartesian
|
||||
* coordinates.
|
||||
**********************************************************************/
|
||||
using vec3 = Ellipsoid3::vec3;
|
||||
private:
|
||||
using real = Math::real;
|
||||
#if GEOGRAPHICLIB_PRECISION > 3
|
||||
// <random> only supports "standard" floating point types
|
||||
using random_prec = Math::extended;
|
||||
#else
|
||||
using random_prec = Math::real;
|
||||
#endif
|
||||
using ang = Angle;
|
||||
static constexpr int maxit_ = 20;
|
||||
static constexpr bool throw_ = true; // exception on convergence failure
|
||||
const Ellipsoid3 _t;
|
||||
const vec3 _axes, _axes2, _linecc2;
|
||||
// mutable because using these objects in a non-const operation
|
||||
mutable std::normal_distribution<random_prec> _norm;
|
||||
mutable std::uniform_real_distribution<random_prec> _uni;
|
||||
|
||||
static void roty(vec3& R, int n) {
|
||||
// require n = -1, 0, 1
|
||||
// Prolate convention has major axis in z direction, minor axis in -x
|
||||
// direction, median axis is unchanged.
|
||||
// If n = 0, do nothing otherwise...
|
||||
// With n = +1, multiply by
|
||||
// [ 0 0 -1]
|
||||
// [ 0 1 0]
|
||||
// [ 1 0 0]
|
||||
// which transforms original x, y, z to prolate convention.
|
||||
// With n = -1, multiply by
|
||||
// [ 0 0 1]
|
||||
// [ 0 1 0]
|
||||
// [-1 0 0]
|
||||
// which transforms prolate convention to original x, y, z.
|
||||
if (n != 0) {
|
||||
using std::swap;
|
||||
R[1+n] = -R[1+n];
|
||||
swap(R[0], R[2]);
|
||||
}
|
||||
}
|
||||
|
||||
template<int n>
|
||||
void cart2togeneric(vec3 R, ang& phi, ang& lam, bool alt) const;
|
||||
template<int n>
|
||||
void generictocart2(ang phi, ang lam, vec3& R, bool alt) const;
|
||||
template<int n> ang meridianplane(ang lam, bool alt) const;
|
||||
void cardinaldir(vec3 R, ang merid, vec3& N, vec3& E, bool alt) const;
|
||||
template<int n>
|
||||
void cart2togeneric(vec3 R, vec3 V, ang& phi, ang& lam, ang& zet, bool alt)
|
||||
const;
|
||||
template<int n>
|
||||
void generictocart2(ang phi, ang lam, ang zet, vec3& R, vec3&V, bool alt)
|
||||
const;
|
||||
real cubic(vec3 R2) const;
|
||||
|
||||
template<int n>
|
||||
class funp {
|
||||
private:
|
||||
// Evaluate
|
||||
// f(p) = sum( (R[0]/(p + l[0]))^n, k = 0..2) - 1
|
||||
// and it derivative.
|
||||
const real _d;
|
||||
const vec3 _r, _l;
|
||||
public:
|
||||
funp(const vec3& R, const vec3& l)
|
||||
: _d(std::numeric_limits<real>::epsilon()/2)
|
||||
, _r(R)
|
||||
, _l(l)
|
||||
{
|
||||
static_assert(n >= 1 && n <= 2, "Bad power in funp");
|
||||
}
|
||||
std::pair<real, real> operator()(real p) const;
|
||||
};
|
||||
|
||||
static real cartsolve(const std::function<std::pair<real, real>(real)>& f,
|
||||
real p0, real pscale);
|
||||
void carttoellip(vec3 R, Angle& bet, Angle& omg, real& H) const;
|
||||
void elliptocart(Angle bet, Angle omg, real H, vec3& R) const;
|
||||
|
||||
// real a() const { return t().a(); } // not needed
|
||||
real b() const { return t().b(); }
|
||||
real c() const { return t().c(); }
|
||||
public:
|
||||
/**
|
||||
* Enumerator for all the coordinates.
|
||||
**********************************************************************/
|
||||
enum coord {
|
||||
/**
|
||||
* Geodetic coordinates, \e phi, \e lam, \e zet \e h;
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
GEODETIC = 0,
|
||||
/**
|
||||
* Parametric coordinates, \e phi', \e lam', \e zet, \e h;
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
PARAMETRIC = 1,
|
||||
/**
|
||||
* %Geocentric coordinates, \e phi'', \e lam'', \e zet, \e h;
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
GEOCENTRIC = 2,
|
||||
/**
|
||||
* Ellipsoidal coordinates, \e beta, \e omg, \e alp, \e H;
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
ELLIPSOIDAL = 3,
|
||||
/**
|
||||
* Geodetic coordinates with pole aligned with the major axis.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
GEODETIC_X = 4 + GEODETIC,
|
||||
/**
|
||||
* Parametric coordinates with pole aligned with the major axis.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
PARAMETRIC_X = 4 + PARAMETRIC,
|
||||
/**
|
||||
* %Geocentric coordinates with pole aligned with the major axis.
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
GEOCENTRIC_X = 4 + GEOCENTRIC,
|
||||
/**
|
||||
* An alias for GEODETIC;
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
PLANETODETIC = GEODETIC,
|
||||
/**
|
||||
* Another alias for GEODETIC;
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
GEOGRAPHIC = GEODETIC,
|
||||
/**
|
||||
* An alias for GEOCENTRIC;
|
||||
* @hideinitializer
|
||||
**********************************************************************/
|
||||
PLANETOCENTRIC = GEOCENTRIC,
|
||||
};
|
||||
/** \name Transformations for points on the ellipsoid.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Constructor for a triaxial ellipsoid defined by Ellipsoid3 object.
|
||||
*
|
||||
* @param[in] t the Ellipsoid3 object.
|
||||
**********************************************************************/
|
||||
Cartesian3(const Ellipsoid3& t);
|
||||
/**
|
||||
* Constructor for a triaxial ellipsoid with semiaxes.
|
||||
*
|
||||
* @param[in] a the largest semiaxis.
|
||||
* @param[in] b the middle semiaxis.
|
||||
* @param[in] c the smallest semiaxis.
|
||||
* @exception GeographicErr if the required ordering is semiaxes is
|
||||
* violated.
|
||||
*
|
||||
* The semiaxes must satisfy \e a ≥ \e b ≥ \e c > 0.
|
||||
* If \e a = \e c (a sphere), then the oblate limit is taken.
|
||||
**********************************************************************/
|
||||
Cartesian3(real a, real b, real c);
|
||||
/**
|
||||
* Alternate constructor for a triaxial ellipsoid.
|
||||
*
|
||||
* @param[in] b the middle semiaxis.
|
||||
* @param[in] e2 the eccentricity squared \f$e^2 = (a^2 - c^2)/b^2\f$.
|
||||
* @param[in] k2 the oblateness parameter squared \f$k^2 = (b^2 - c^2) /
|
||||
* (a^2 - c^2)\f$.
|
||||
* @param[in] kp2 the prolateness parameter squared \f$k'^2= (a^2 - b^2) /
|
||||
* (a^2 - c^2)\f$.
|
||||
* @exception GeographicErr if the required ordering is semiaxes is
|
||||
* violated.
|
||||
*
|
||||
* \note The constructor normalizes \e k2 and \e kp2 to ensure then \e k2 +
|
||||
* \e kp2 = 1.
|
||||
**********************************************************************/
|
||||
Cartesian3(real b, real e2, real k2, real kp2);
|
||||
///@}
|
||||
|
||||
/** \name Transformations for points on the ellipsoid.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Convert latitude and longitude to a point on the surface.
|
||||
*
|
||||
* @param[in] coordin one of the coordinate types, Cartesian3::coord.
|
||||
* @param[in] lat the latitude of the point.
|
||||
* @param[in] lon the longitude of the point.
|
||||
* @param[out] R the cartesian position on the surface of the ellipsoid.
|
||||
* @exception GeographicErr if \e coordin is not recognized.
|
||||
**********************************************************************/
|
||||
void anytocart2(coord coordin, Angle lat, Angle lon, vec3& R) const;
|
||||
/**
|
||||
* Convert latitude and longitude in degrees to a point on the surface.
|
||||
*
|
||||
* @param[in] coordin one of the coordinate types, Cartesian3::coord.
|
||||
* @param[in] lat the latitude of the point (in degrees).
|
||||
* @param[in] lon the longitude of the point (in degrees).
|
||||
* @param[out] R the cartesian position on the surface of the ellipsoid.
|
||||
* @exception GeographicErr if \e coordin is not recognized.
|
||||
**********************************************************************/
|
||||
void anytocart2(coord coordin, real lat, real lon, vec3& R) const {
|
||||
anytocart2(coordin, Angle(lat), Angle(lon), R);
|
||||
}
|
||||
/**
|
||||
* Convert a point on the surface to latitude and longitude.
|
||||
*
|
||||
* @param[in] R the cartesian position on the surface of the ellipsoid.
|
||||
* @param[in] coordout one of the coordinate types, Cartesian3::coord.
|
||||
* @param[out] lat the latitude of the point.
|
||||
* @param[out] lon the longitude of the point.
|
||||
* @exception GeographicErr if \e coordout is not recognized.
|
||||
**********************************************************************/
|
||||
void cart2toany(vec3 R, coord coordout, Angle& lat, Angle& lon) const;
|
||||
/**
|
||||
* Convert a point on the surface to latitude and longitude in degrees.
|
||||
*
|
||||
* @param[in] R the cartesian position on the surface of the ellipsoid.
|
||||
* @param[in] coordout one of the coordinate types, Cartesian3::coord.
|
||||
* @param[out] lat the latitude of the point (in degrees).
|
||||
* @param[out] lon the longitude of the point (in degrees).
|
||||
* @exception GeographicErr if \e coordout is not recognized.
|
||||
**********************************************************************/
|
||||
void cart2toany(vec3 R, coord coordout, real& lat, real& lon) const {
|
||||
Angle lata, lona; cart2toany(R, coordout, lata, lona);
|
||||
lat = real(lata); lon = real(lona);
|
||||
}
|
||||
/**
|
||||
* Convert between latitudes and longitudes.
|
||||
*
|
||||
* @param[in] coordin one of the coordinate types, Cartesian3::coord.
|
||||
* @param[in] lat1 the \e coordin latitude of the point.
|
||||
* @param[in] lon1 the \e coordin longitude of the point.
|
||||
* @param[in] coordout one of the coordinate types, Cartesian3::coord.
|
||||
* @param[out] lat2 the \e coordout latitude of the point.
|
||||
* @param[out] lon2 the \e coordout longitude of the point.
|
||||
* @exception GeographicErr if \e coordin or \e coordout is not recognized.
|
||||
**********************************************************************/
|
||||
void anytoany(coord coordin, Angle lat1, Angle lon1,
|
||||
coord coordout, Angle& lat2, Angle& lon2) const;
|
||||
/**
|
||||
* Convert between latitudes and longitudes in degrees.
|
||||
*
|
||||
* @param[in] coordin one of the coordinate types, Cartesian3::coord.
|
||||
* @param[in] lat1 the \e coordin latitude of the point (in degrees).
|
||||
* @param[in] lon1 the \e coordin longitude of the point (in degrees).
|
||||
* @param[in] coordout one of the coordinate types, Cartesian3::coord.
|
||||
* @param[out] lat2 the \e coordout latitude of the point (in degrees).
|
||||
* @param[out] lon2 the \e coordout longitude of the point (in degrees).
|
||||
* @exception GeographicErr if \e coordin or \e coordout is not recognized.
|
||||
**********************************************************************/
|
||||
void anytoany(coord coordin, real lat1, real lon1,
|
||||
coord coordout, real& lat2, real& lon2) const {
|
||||
Angle lat2a, lon2a;
|
||||
anytoany(coordin, Angle(lat1), Angle(lon1), coordout, lat2a, lon2a);
|
||||
lat2 = real(lat2a); lon2 = real(lon2a);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name Transformations for points and directions on the ellipsoid.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Convert latitude, longitude, and azimuth to cartesian position and
|
||||
* direction.
|
||||
*
|
||||
* @param[in] coordin one of the coordinate types, Cartesian3::coord.
|
||||
* @param[in] lat the latitude of the point.
|
||||
* @param[in] lon the longitude of the point.
|
||||
* @param[in] azi the azimuth of the heading.
|
||||
* @param[out] R the cartesian position on the surface of the ellipsoid.
|
||||
* @param[out] V the cartesian direction tangent to the ellipsoid.
|
||||
* @exception GeographicErr if \e coordin is not recognized.
|
||||
**********************************************************************/
|
||||
void anytocart2(coord coordin, Angle lat, Angle lon, Angle azi,
|
||||
vec3& R, vec3& V) const;
|
||||
/**
|
||||
* Convert latitude, longitude, and azimuth in degrees to cartesian
|
||||
* position and direction.
|
||||
*
|
||||
* @param[in] coordin one of the coordinate types, Cartesian3::coord.
|
||||
* @param[in] lat the latitude of the point (in degrees).
|
||||
* @param[in] lon the longitude of the point (in degrees).
|
||||
* @param[in] azi the azimuth of the heading (in degrees).
|
||||
* @param[out] R the cartesian position on the surface of the ellipsoid.
|
||||
* @param[out] V the cartesian direction tangent to the ellipsoid.
|
||||
* @exception GeographicErr if \e coordin is not recognized.
|
||||
**********************************************************************/
|
||||
void anytocart2(coord coordin, real lat, real lon, real azi,
|
||||
vec3& R, vec3& V) const {
|
||||
anytocart2(coordin, Angle(lat), Angle(lon), Angle(azi), R, V);
|
||||
}
|
||||
/**
|
||||
* Convert position and direction on surface to latitude, longitude, and
|
||||
* azimuth.
|
||||
*
|
||||
* @param[in] R the cartesian position on the surface of the ellipsoid.
|
||||
* @param[in] V the cartesian direction tangent to the ellipsoid.
|
||||
* @param[in] coordout one of the coordinate types, Cartesian3::coord.
|
||||
* @param[out] lat the latitude of the point.
|
||||
* @param[out] lon the longitude of the point.
|
||||
* @param[out] azi the azimuth of the heading.
|
||||
* @exception GeographicErr if \e coordout is not recognized.
|
||||
**********************************************************************/
|
||||
void cart2toany(vec3 R, vec3 V,
|
||||
coord coordout, Angle& lat, Angle& lon, Angle& azi) const;
|
||||
/**
|
||||
* Convert position and direction on surface to latitude, longitude, and
|
||||
* azimuth in degrees.
|
||||
*
|
||||
* @param[in] R the cartesian position on the surface of the ellipsoid.
|
||||
* @param[in] V the cartesian direction tangent to the ellipsoid.
|
||||
* @param[in] coordout one of the coordinate types, Cartesian3::coord.
|
||||
* @param[out] lat the latitude of the point (in degrees).
|
||||
* @param[out] lon the longitude of the point (in degrees).
|
||||
* @param[out] azi the azimuth of the heading (in degrees).
|
||||
* @exception GeographicErr if \e coordout is not recognized.
|
||||
**********************************************************************/
|
||||
void cart2toany(vec3 R, vec3 V,
|
||||
coord coordout, real& lat, real& lon, real& azi) const {
|
||||
Angle lata, lona, azia; cart2toany(R, V, coordout, lata, lona, azia);
|
||||
lat = real(lata); lon = real(lona), azi = real(azia);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name Transformations for arbitrary points.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Convert latitude, longitude, and height to a cartesian position.
|
||||
*
|
||||
* @param[in] coordin one of the coordinate types, Cartesian3::coord.
|
||||
* @param[in] lat the latitude of the point.
|
||||
* @param[in] lon the longitude of the point.
|
||||
* @param[in] h the height (in meters).
|
||||
* @param[out] R the cartesian position of the point.
|
||||
* @exception GeographicErr if \e coordin is not recognized.
|
||||
**********************************************************************/
|
||||
void anytocart(coord coordin, Angle lat, Angle lon, real h, vec3& R) const;
|
||||
/**
|
||||
* Convert latitude, longitude in degrees, and height to a cartesian
|
||||
* position.
|
||||
*
|
||||
* @param[in] coordin one of the coordinate types, Cartesian3::coord.
|
||||
* @param[in] lat the latitude of the point (in degrees).
|
||||
* @param[in] lon the longitude of the point (in degrees).
|
||||
* @param[in] h the height (in meters).
|
||||
* @param[out] R the cartesian position of the point.
|
||||
* @exception GeographicErr if \e coordin is not recognized.
|
||||
**********************************************************************/
|
||||
void anytocart(coord coordin, real lat, real lon, real h, vec3& R) const {
|
||||
anytocart(coordin, Angle(lat), Angle(lon), h, R);
|
||||
}
|
||||
/**
|
||||
* Convert a cartesian position to latitude, longitude, and height.
|
||||
*
|
||||
* @param[in] R the cartesian position of the point.
|
||||
* @param[in] coordout one of the coordinate types, Cartesian3::coord.
|
||||
* @param[out] lat the latitude of the point.
|
||||
* @param[out] lon the longitude of the point.
|
||||
* @param[out] h the height (in meters).
|
||||
* @exception GeographicErr if \e coordin is not recognized.
|
||||
**********************************************************************/
|
||||
void carttoany(vec3 R,
|
||||
coord coordout, Angle& lat, Angle& lon, real& h) const;
|
||||
/**
|
||||
* Convert a cartesian position to latitude, longitude in degrees, and
|
||||
* height.
|
||||
*
|
||||
* @param[in] R the cartesian position of the point.
|
||||
* @param[in] coordout one of the coordinate types, Cartesian3::coord.
|
||||
* @param[out] lat the latitude of the point (in degrees).
|
||||
* @param[out] lon the longitude of the point (in degrees).
|
||||
* @param[out] h the height (in meters).
|
||||
* @exception GeographicErr if \e coordin is not recognized.
|
||||
**********************************************************************/
|
||||
void carttoany(vec3 R,
|
||||
coord coordout, real& lat, real& lon, real& h) const {
|
||||
Angle lata, lona; carttoany(R, coordout, lata, lona, h);
|
||||
lat = real(lata); lon = real(lona);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** \name Transferring an arbitrary point onto the ellipsoid.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Convert a point on the ellipsoid and a height to a cartesian position.
|
||||
*
|
||||
* @param[in] R2 the cartesian position of the point on the ellipsoid.
|
||||
* @param[in] h the height above the ellipsoid (in meters).
|
||||
* @param[out] R the cartesian position of the point.
|
||||
**********************************************************************/
|
||||
void cart2tocart(vec3 R2, real h, vec3& R) const;
|
||||
/**
|
||||
* Find the closest point on the ellipsoid
|
||||
*
|
||||
* @param[in] R the cartesian position of the point.
|
||||
* @param[out] R2 the cartesian position of the closest point on the
|
||||
* ellipsoid.
|
||||
* @param[out] h the height above the ellipsoid (in meters).
|
||||
**********************************************************************/
|
||||
void carttocart2(vec3 R, vec3& R2, real& h) const;
|
||||
///@}
|
||||
|
||||
/** \name Generating random points on the ellipsoid.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Generate a random point on the ellipsoid.
|
||||
*
|
||||
* @tparam G the type of the random generator.
|
||||
* @param[inout] g the random generator.
|
||||
* @param[out] R a cartesian position uniformly sampled on the surface of
|
||||
* the ellipsoid.
|
||||
*
|
||||
* See the example listed in the description of this class for an example
|
||||
* of using this function.
|
||||
*
|
||||
* The method of sampling is given by
|
||||
* <a href="https://doi.org/10.1007/s11075-023-01628-4"> Marples and
|
||||
* Williams (2023)</a> Algorithm 1, based on the general method of
|
||||
* <a href="https://doi.org/10.1088/0031-9155/32/10/009"> Williamson
|
||||
* (1987)</a>.
|
||||
**********************************************************************/
|
||||
template <class G> void cart2rand(G& g, vec3& R) const;
|
||||
/**
|
||||
* Generate a random point and direction on the ellipsoid.
|
||||
*
|
||||
* @tparam G the type of the random generator.
|
||||
* @param[inout] g the random generator.
|
||||
* @param[out] R a cartesian position uniformly sampled on the surface of
|
||||
* the ellipsoid.
|
||||
* @param[out] V a cartesian direction uniformly sampled tangent to the
|
||||
* ellipsoid.
|
||||
**********************************************************************/
|
||||
template <class G> void cart2rand(G& g, vec3& R, vec3& V) const;
|
||||
///@}
|
||||
|
||||
/** \name Inspector function
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return the Ellipsoid3 object for this projection.
|
||||
**********************************************************************/
|
||||
const Ellipsoid3& t() const { return _t; }
|
||||
///@}
|
||||
};
|
||||
|
||||
template<class G> inline void Cartesian3::cart2rand(G& g, vec3& R) const {
|
||||
// This uses the simple rejection technique given by Marples and Williams,
|
||||
// Num. Alg. (2023), Algorithm 1 based on the general method of Williamson,
|
||||
// Phys. Med. Biol. (1987).
|
||||
using std::isfinite;
|
||||
while (true) {
|
||||
while (true) {
|
||||
// guaranteed evaluated left to right
|
||||
R = {real(_norm(g)), real(_norm(g)), real(_norm(g))};
|
||||
Ellipsoid3::normvec(R); // But catch rare cases where |R| = 0
|
||||
if (isfinite(R[0])) break;
|
||||
}
|
||||
R[0] *= _axes[0]; R[1] *= _axes[1]; R[2] *= _axes[2];
|
||||
vec3 up{ R[0] / _axes2[0], R[1] / _axes2[1], R[2] / _axes2[2] };
|
||||
real q = c() * Math::hypot3(up[0], up[1], up[2]);
|
||||
if (real(_uni(g)) < q) break;
|
||||
}
|
||||
}
|
||||
template<class G> inline void Cartesian3::cart2rand(G& g, vec3& R, vec3& V)
|
||||
const {
|
||||
using std::isfinite;
|
||||
cart2rand<G>(g, R);
|
||||
while (true) {
|
||||
// guaranteed evaluated left to right
|
||||
V = {real(_norm(g)), real(_norm(g)), real(_norm(g))};
|
||||
vec3 up{ R[0] / _axes2[0], R[1] / _axes2[1], R[2] / _axes2[2] };
|
||||
real u2 = Math::sq(up[0]) + Math::sq(up[1]) + Math::sq(up[2]), // |up|^2
|
||||
// (up . V) / |up|^2
|
||||
uv = (V[0] * up[0] + V[1] * up[1] + V[2] * up[2])/u2;
|
||||
// V - up * (up . V) / |up|^2
|
||||
V[0] -= uv * up[0]; V[1] -= uv * up[1]; V[2] -= uv * up[2];
|
||||
Ellipsoid3::normvec(V); // But catch rare cases where |V| = 0
|
||||
if (isfinite(V[0])) break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Triaxial
|
||||
} // namespace GeographicLib
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#endif // GEOGRAPHICLIB_CARTESIAN3_HPP
|
||||
+451
@@ -0,0 +1,451 @@
|
||||
/**
|
||||
* \file Conformal3.hpp
|
||||
* \brief Header for GeographicLib::Triaxial::Conformal3 class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2014-2025) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_CONFORMAL3_HPP)
|
||||
#define GEOGRAPHICLIB_CONFORMAL3_HPP 1
|
||||
|
||||
#include <GeographicLib/Angle.hpp>
|
||||
#include <GeographicLib/EllipticFunction.hpp>
|
||||
#include <GeographicLib/Triaxial/Ellipsoid3.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
namespace Triaxial {
|
||||
|
||||
/**
|
||||
* \brief Jacobi's conformal projection of a triaxial ellipsoid
|
||||
*
|
||||
* This is a conformal projection of the ellipsoid to a plane in which the
|
||||
* grid lines are straight; see Jacobi,
|
||||
* <a href="https://books.google.com/books?id=ryEOAAAAQAAJ&pg=PA212">
|
||||
* Vorlesungen über Dynamik, §28</a>. The constructor takes the
|
||||
* semiaxes of the ellipsoid (which must be in order). Member functions map
|
||||
* the ellipsoidal coordinates ω and β separately to \e x and \e
|
||||
* y. Jacobi's coordinates have been multiplied by \f$eb/2\f$ so that the
|
||||
* customary results are returned in the cases of a sphere or an ellipsoid of
|
||||
* revolution. In particular the scale satisfies \f$m\ge 1\f$ with \f$m =
|
||||
* 1\f$ at \f$\cos\beta = \sin\omega = 1\f$. (Note: usually %GeographicLib
|
||||
* uses \f$k\f$ to denote the scale. However, in order to avoid confusion
|
||||
* with the oblateness parameter \f$k\f$ used to specify the triaxial
|
||||
* ellipsoid, the symbol \f$m\f$ is used for the scale in this class.)
|
||||
*
|
||||
* The ellipsoid is oriented so that the major principal ellipse, \f$Z=0\f$,
|
||||
* is the equator, \f$\beta=0\f$, while the minor principal ellipse,
|
||||
* \f$X=0\f$, is the meridian, \f$\omega\pm\pi/2\f$. The four umbilical
|
||||
* points, \f$\sin\omega = \cos\beta = 0\f$, lie on median principal ellipse
|
||||
* in the plane \f$Y=0\f$.
|
||||
*
|
||||
* In this projection the easting, \e x, depends only on the longitude
|
||||
* \f$\omega\f$ and the northing, \e y depends only on the latitude
|
||||
* \f$\beta\f$. Thus lines of constant latitude and longitude map to
|
||||
* straight lines. For a general ellipsoid, each octant of the ellipsoid
|
||||
* maps to a finite rectangle of dimensions x0() x y0(). For an oblate
|
||||
* (resp. prolate) ellipsoid, x0() (resp. y0()) diverges.
|
||||
*
|
||||
* For any ellipsoid, we define a "conformal sphere" which has exactly the
|
||||
* same extent for the mapping of an octant. This allows points on the
|
||||
* ellipsoid to be conformally mapped to a sphere. This in turn allows any
|
||||
* triaxial ellipsoid to be conformally mapped to any other triaxial
|
||||
* ellipsoid.
|
||||
*
|
||||
* The units for the easting and the northing are the same as the units uses
|
||||
* to specify the size of the ellipsoid.
|
||||
*
|
||||
* For more information on this projection, see \ref jacobi.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Conformal3.cpp
|
||||
*
|
||||
* <a href="Conformal3Proj.1.html">Conformal3Proj</a> is a command-line
|
||||
* utility providing access to the functionality of Conformal3.
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT Conformal3 {
|
||||
private:
|
||||
using real = Math::real;
|
||||
using ang = Angle;
|
||||
Ellipsoid3 _t, _s;
|
||||
EllipticFunction _ex, _ey, _exs, _eys;
|
||||
real _x, _y;
|
||||
|
||||
real a() const { return _t.a(); }
|
||||
real b() const { return _t.b(); }
|
||||
real c() const { return _t.c(); }
|
||||
real e2() const { return _t.e2(); }
|
||||
real k2() const { return _t.k2(); }
|
||||
real kp2() const { return _t.kp2(); }
|
||||
static real Pi(const EllipticFunction& ell, ang phi);
|
||||
static ang Piinv(const EllipticFunction& ell, real x);
|
||||
static real F(const EllipticFunction& ell, ang phi);
|
||||
static ang Finv(const EllipticFunction& ell, real x);
|
||||
static ang omegashift(ang omg, int dir) {
|
||||
return omg - ang::cardinal(dir);
|
||||
}
|
||||
// Jacobi has m = 2/sqrt(lambda2 - lambda3)
|
||||
// Setting lambda2 = Cayley's k = -(b^2 \sin^2\beta + c^2 \cos^2\beta)
|
||||
// = -(b^2 - (b^2 - c^2) \cos^2\beta)
|
||||
// Setting lambda3 = Cayley's h = -(b^2 \cos^2\omega + a^2 \sin^2\omega)
|
||||
// = -(b^2 + (a^2 - b^2) \sin^2\omega)
|
||||
// lambda2 - lambda3 =
|
||||
// - (b^2 - (b^2 - c^2) \cos^2\beta)
|
||||
// + (b^2 + (a^2 - b^2) \sin^2\omega)
|
||||
// (a^2 - c^2) * (k'^2 * \sin^2\omega + k^2 \cos^2\beta)
|
||||
// m = 2/sqrt(a^2 - c^2) * 1/sqrt(k'^2 * \sin^2\omega + k^2 \cos^2\beta)
|
||||
real invscale(ang bet, ang omg) const {
|
||||
using std::sqrt;
|
||||
omg = omegashift(omg, +1);
|
||||
return sqrt(k2() * Math::sq(bet.c()) + kp2() * Math::sq(omg.c()));
|
||||
}
|
||||
static Ellipsoid3 EquivSphere(real x, real y,
|
||||
EllipticFunction& ellx,
|
||||
EllipticFunction& elly);
|
||||
real sphericalscale(real ma, real mb) const;
|
||||
public:
|
||||
using vec3 = Ellipsoid3::vec3;
|
||||
/**
|
||||
* Constructor for a triaxial ellipsoid defined by Ellipsoid3 object.
|
||||
*
|
||||
* @param[in] t the Ellipsoid3 object.
|
||||
**********************************************************************/
|
||||
Conformal3(const Ellipsoid3& t = Ellipsoid3{});
|
||||
/**
|
||||
* Constructor for a triaxial ellipsoid with semiaxes.
|
||||
*
|
||||
* @param[in] a the largest semiaxis.
|
||||
* @param[in] b the middle semiaxis.
|
||||
* @param[in] c the smallest semiaxis.
|
||||
* @exception GeographicErr if the required ordering is semiaxes is
|
||||
* violated.
|
||||
*
|
||||
* The semiaxes must satisfy \e a ≥ \e b ≥ \e c > 0.
|
||||
* If \e a = \e c (a sphere), then the oblate limit is taken.
|
||||
**********************************************************************/
|
||||
Conformal3(real a, real b, real c);
|
||||
/**
|
||||
* Alternate constructor for a triaxial ellipsoid.
|
||||
*
|
||||
* @param[in] b the middle semiaxis.
|
||||
* @param[in] e2 the eccentricity squared \f$e^2 = (a^2 - c^2)/b^2\f$.
|
||||
* @param[in] k2 the oblateness parameter squared \f$k^2 = (b^2 - c^2) /
|
||||
* (a^2 - c^2)\f$.
|
||||
* @param[in] kp2 the prolateness parameter squared \f$k'^2= (a^2 - b^2) /
|
||||
* (a^2 - c^2)\f$.
|
||||
* @exception GeographicErr if the required ordering is semiaxes is
|
||||
* violated.
|
||||
*
|
||||
* \note The constructor normalizes \e k2 and \e kp2 to ensure then \e k2 +
|
||||
* \e kp2 = 1.
|
||||
**********************************************************************/
|
||||
Conformal3(real b, real e2, real k2, real kp2);
|
||||
/**
|
||||
* @return the quadrant length in the \e x direction.
|
||||
**********************************************************************/
|
||||
Math::real x0() const { return _x; }
|
||||
/**
|
||||
* The \e x projection.
|
||||
*
|
||||
* @param[in] omg the longitude ω as an Angle.
|
||||
* @return the easting.
|
||||
**********************************************************************/
|
||||
Math::real x(Angle omg) const;
|
||||
/**
|
||||
* The \e x projection.
|
||||
*
|
||||
* @param[in] omg the longitude ω (in degrees).
|
||||
* @return \e x.
|
||||
**********************************************************************/
|
||||
Math::real x(real omg) const {
|
||||
return x(ang(omg));
|
||||
}
|
||||
/**
|
||||
* The inverse \e x projection.
|
||||
*
|
||||
* @param[in] x the easting.
|
||||
* @return the longitude ω as an Angle.
|
||||
**********************************************************************/
|
||||
Angle omega(real x) const;
|
||||
/**
|
||||
* The inverse \e x projection.
|
||||
*
|
||||
* @param[in] x the easting.
|
||||
* @return the longitude ω in degrees.
|
||||
**********************************************************************/
|
||||
Math::real omegad(real x) const {
|
||||
return real(omega(x));
|
||||
}
|
||||
/**
|
||||
* @return the quadrant length in the \e y direction.
|
||||
**********************************************************************/
|
||||
Math::real y0() const { return _y; }
|
||||
/**
|
||||
* The \e y projection.
|
||||
*
|
||||
* @param[in] bet the latitude β as an Angle
|
||||
* @return the northing.
|
||||
**********************************************************************/
|
||||
Math::real y(Angle bet) const;
|
||||
/**
|
||||
* The \e y projection.
|
||||
*
|
||||
* @param[in] bet the latitude β (in degrees).
|
||||
* @return the northing.
|
||||
**********************************************************************/
|
||||
Math::real y(real bet) const {
|
||||
return y(ang(bet));
|
||||
}
|
||||
/**
|
||||
* The inverse \e y projection.
|
||||
*
|
||||
* @param[in] y the northing.
|
||||
* @return the latitude β as an Angle.
|
||||
**********************************************************************/
|
||||
Angle beta(real y) const;
|
||||
/**
|
||||
* The inverse \e y projection.
|
||||
*
|
||||
* @param[in] y the northing.
|
||||
* @return the latitude β in degrees.
|
||||
**********************************************************************/
|
||||
Math::real betad(real y) const {
|
||||
return real(beta(y));
|
||||
}
|
||||
/**
|
||||
* The forward projection.
|
||||
*
|
||||
* @param[in] bet the ellipsoidal latitude.
|
||||
* @param[in] omg the ellipsoidal longitude.
|
||||
* @param[out] x the easting.
|
||||
* @param[out] y the easting.
|
||||
**********************************************************************/
|
||||
void Forward(Angle bet, Angle omg, real& x, real& y) const;
|
||||
/**
|
||||
* The forward projection in degrees
|
||||
*
|
||||
* @param[in] bet the ellipsoidal latitude (in degrees).
|
||||
* @param[in] omg the ellipsoidal longitude (in degrees).
|
||||
* @param[out] x the easting.
|
||||
* @param[out] y the easting.
|
||||
**********************************************************************/
|
||||
void Forward(real bet, real omg, real& x, real& y) const {
|
||||
Forward(ang(bet), ang(omg), x, y);
|
||||
}
|
||||
/**
|
||||
* The forward projection with the scale.
|
||||
*
|
||||
* @param[in] bet the ellipsoidal latitude.
|
||||
* @param[in] omg the ellipsoidal longitude.
|
||||
* @param[out] x the easting.
|
||||
* @param[out] y the easting.
|
||||
* @param[out] m the scale.
|
||||
*
|
||||
* The meridian convergence for this projection is 0.
|
||||
**********************************************************************/
|
||||
void Forward(Angle bet, Angle omg, real& x, real& y, real& m) const;
|
||||
/**
|
||||
* The forward projection in degrees with the scale.
|
||||
*
|
||||
* @param[in] bet the ellipsoidal latitude (in degrees).
|
||||
* @param[in] omg the ellipsoidal longitude (in degrees).
|
||||
* @param[out] x the easting.
|
||||
* @param[out] y the easting.
|
||||
* @param[out] m the scale.
|
||||
*
|
||||
* The meridian convergence for this projection is 0.
|
||||
**********************************************************************/
|
||||
void Forward(real bet, real omg, real& x, real& y, real& m) const {
|
||||
Forward(ang(bet), ang(omg), x, y, m);
|
||||
}
|
||||
/**
|
||||
* The reverse projection.
|
||||
*
|
||||
* @param[in] x the easting.
|
||||
* @param[in] y the easting.
|
||||
* @param[out] bet the ellipsoidal latitude.
|
||||
* @param[out] omg the ellipsoidal longitude.
|
||||
**********************************************************************/
|
||||
void Reverse(real x, real y, Angle& bet, Angle& omg) const;
|
||||
/**
|
||||
* The reverse projection in degrees.
|
||||
*
|
||||
* @param[in] x the easting.
|
||||
* @param[in] y the easting.
|
||||
* @param[out] bet the ellipsoidal latitude (in degrees).
|
||||
* @param[out] omg the ellipsoidal longitude (in degrees).
|
||||
**********************************************************************/
|
||||
void Reverse(real x, real y, real& bet, real& omg) const {
|
||||
ang beta, omga;
|
||||
Reverse(x, y, beta, omga);
|
||||
bet = real(beta); omg = real(omga);
|
||||
}
|
||||
/**
|
||||
* The reverse projection with the scale.
|
||||
*
|
||||
* @param[in] x the easting.
|
||||
* @param[in] y the easting.
|
||||
* @param[out] bet the ellipsoidal latitude.
|
||||
* @param[out] omg the ellipsoidal longitude.
|
||||
* @param[out] m the scale.
|
||||
*
|
||||
* The meridian convergence for this projection is 0.
|
||||
**********************************************************************/
|
||||
void Reverse(real x, real y, Angle& bet, Angle& omg, real& m) const;
|
||||
/**
|
||||
* The reverse projection in degrees with the scale.
|
||||
*
|
||||
* @param[in] x the easting.
|
||||
* @param[in] y the easting.
|
||||
* @param[out] bet the ellipsoidal latitude (in degrees).
|
||||
* @param[out] omg the ellipsoidal longitude (in degrees).
|
||||
* @param[out] m the scale.
|
||||
*
|
||||
* The meridian convergence for this projection is 0.
|
||||
**********************************************************************/
|
||||
void Reverse(real x, real y, real& bet, real& omg, real& m) const {
|
||||
ang beta, omga;
|
||||
Reverse(x, y, beta, omga, m);
|
||||
bet = real(beta); omg = real(omga);
|
||||
}
|
||||
/**
|
||||
* The forward projection onto the conformal sphere.
|
||||
*
|
||||
* @param[in] bet the ellipsoidal latitude.
|
||||
* @param[in] omg the ellipsoidal longitude.
|
||||
* @param[out] r the projected position on the sphere
|
||||
* @param[out] v the projected direction for due north on the triaxial
|
||||
* ellipsoid.
|
||||
* @param[out] m the scale.
|
||||
**********************************************************************/
|
||||
void ForwardSphere(Angle bet, Angle omg, vec3& r, vec3& v, real& m) const;
|
||||
/**
|
||||
* The forward projection in degrees onto the conformal sphere.
|
||||
*
|
||||
* @param[in] bet the ellipsoidal latitude (in degrees).
|
||||
* @param[in] omg the ellipsoidal longitude (in degrees).
|
||||
* @param[out] r the projected position on the sphere
|
||||
* @param[out] v the projected direction for due north on the triaxial
|
||||
* ellipsoid.
|
||||
* @param[out] m the scale.
|
||||
**********************************************************************/
|
||||
void ForwardSphere(real bet, real omg, vec3& r, vec3& v, real& m) const {
|
||||
ForwardSphere(ang(bet), ang(omg), r, v, m);
|
||||
}
|
||||
/**
|
||||
* The reverse projection from the conformal sphere.
|
||||
*
|
||||
* @param[in] r the position on the sphere.
|
||||
* @param[in] v a reference direction.
|
||||
* @param[out] bet the ellipsoidal latitude.
|
||||
* @param[out] omg the ellipsoidal longitude.
|
||||
* @param[out] gam the meridian convergence.
|
||||
* @param[out] m the scale.
|
||||
*
|
||||
* Here \e v is the direction of grid north in some projection and \e gam
|
||||
* is the resulting meridian convergence.
|
||||
**********************************************************************/
|
||||
void ReverseSphere(vec3 r, vec3 v, Angle& bet, Angle& omg,
|
||||
Angle& gam, real& m) const;
|
||||
/**
|
||||
* The reverse projection in degrees from the conformal sphere.
|
||||
*
|
||||
* @param[in] r the position on the sphere.
|
||||
* @param[in] v a reference direction.
|
||||
* @param[out] bet the ellipsoidal latitude (in degrees).
|
||||
* @param[out] omg the ellipsoidal longitude (in degrees).
|
||||
* @param[out] gam the meridian convergence (in degrees).
|
||||
* @param[out] m the scale.
|
||||
*
|
||||
* Here \e v is the direction of grid north in some projection and \e gam
|
||||
* is the resulting meridian convergence.
|
||||
**********************************************************************/
|
||||
void ReverseSphere(vec3 r, vec3 v, real& bet, real& omg,
|
||||
real& gam, real& m) const {
|
||||
ang beta, omga, gama;
|
||||
ReverseSphere(r, v, beta, omga, gama, m);
|
||||
bet = real(beta); omg = real(omga); gam = real(gama);
|
||||
}
|
||||
/**
|
||||
* The forward conformal projection to some other ellipsoid.
|
||||
*
|
||||
* @param[in] alt the Conformal3 object for the other ellipsoid.
|
||||
* @param[in] bet the ellipsoidal latitude.
|
||||
* @param[in] omg the ellipsoidal longitude.
|
||||
* @param[out] betalt the ellipsoidal latitude on the other ellipsoid.
|
||||
* @param[out] omgalt the ellipsoidal longitude on the other ellipsoid.
|
||||
* @param[out] gam the meridian convergence.
|
||||
* @param[out] m the scale.
|
||||
**********************************************************************/
|
||||
void ForwardOther(const Conformal3& alt, Angle bet, Angle omg,
|
||||
Angle& betalt, Angle& omgalt, Angle& gam, real& m)
|
||||
const;
|
||||
/**
|
||||
* The forward conformal projection in degrees to some other ellipsoid.
|
||||
*
|
||||
* @param[in] alt the Conformal3 object for the other ellipsoid.
|
||||
* @param[in] bet the ellipsoidal latitude (in degrees).
|
||||
* @param[in] omg the ellipsoidal longitude (in degrees).
|
||||
* @param[out] betalt the ellipsoidal latitude on the other ellipsoid (in
|
||||
* degrees).
|
||||
* @param[out] omgalt the ellipsoidal longitude on the other ellipsoid (in
|
||||
* degrees).
|
||||
* @param[out] gam the meridian convergence (in degrees).
|
||||
* @param[out] m the scale.
|
||||
**********************************************************************/
|
||||
void ForwardOther(const Conformal3& alt, real bet, real omg,
|
||||
real& betalt, real& omgalt, real& gam, real& m)
|
||||
const {
|
||||
ang betalta, omgalta, gama;
|
||||
ForwardOther(alt, ang(bet), ang(omg), betalta, omgalta, gama, m);
|
||||
betalt = real(betalta); omgalt = real(omgalta); gam = real(gama);
|
||||
}
|
||||
/**
|
||||
* The reverse conformal projection from some other ellipsoid.
|
||||
*
|
||||
* @param[in] alt the Conformal3 object for the other ellipsoid.
|
||||
* @param[in] betalt the ellipsoidal latitude on the other ellipsoid.
|
||||
* @param[in] omgalt the ellipsoidal longitude on the other ellipsoid.
|
||||
* @param[out] bet the ellipsoidal latitude.
|
||||
* @param[out] omg the ellipsoidal longitude.
|
||||
* @param[out] gam the meridian convergence.
|
||||
* @param[out] m the scale.
|
||||
**********************************************************************/
|
||||
void ReverseOther(const Conformal3& alt, Angle betalt, Angle omgalt,
|
||||
Angle& bet, Angle& omg, Angle& gam, real& m)
|
||||
const;
|
||||
/**
|
||||
* The reverse conformal projection in degrees from some other ellipsoid.
|
||||
*
|
||||
* @param[in] alt the Conformal3 object for the other ellipsoid.
|
||||
* @param[in] betalt the ellipsoidal latitude on the other ellipsoid (in
|
||||
* degrees).
|
||||
* @param[in] omgalt the ellipsoidal longitude on the other ellipsoid (in
|
||||
* degrees).
|
||||
* @param[out] bet the ellipsoidal latitude (in degrees).
|
||||
* @param[out] omg the ellipsoidal longitude (in degrees).
|
||||
* @param[out] gam the meridian convergence (in degrees).
|
||||
* @param[out] m the scale.
|
||||
**********************************************************************/
|
||||
void ReverseOther(const Conformal3& alt, real betalt, real omgalt,
|
||||
real& bet, real& omg, real& gam, real& m)
|
||||
const {
|
||||
ang beta, omga, gama;
|
||||
ReverseOther(alt, ang(betalt), ang(omgalt), beta, omga, gama, m);
|
||||
bet = real(beta); omg = real(omga); gam = real(gama);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the Ellipsoid3 object for this projection.
|
||||
**********************************************************************/
|
||||
const Ellipsoid3& t() { return _t; }
|
||||
/**
|
||||
* @return the Ellipsoid3 object for the conformal sphere.
|
||||
**********************************************************************/
|
||||
const Ellipsoid3& s() { return _s; }
|
||||
};
|
||||
|
||||
} // namespace Triaxial
|
||||
} // namespace GeographicLib
|
||||
#endif // GEOGRAPHICLIB_CONFORMAL3_HPP
|
||||
+364
@@ -0,0 +1,364 @@
|
||||
/**
|
||||
* \file Ellipsoid3.hpp
|
||||
* \brief Header for GeographicLib::Triaxial::Ellipsoid3 class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2024-2025) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_ELLIPSOID3_HPP)
|
||||
#define GEOGRAPHICLIB_ELLIPSOID3_HPP 1
|
||||
|
||||
#include <iostream>
|
||||
#include <array>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/Angle.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
/**
|
||||
* \brief Namespace for operations on triaxial ellipsoids
|
||||
*
|
||||
* The algorithms on triaxial ellipsoids are, for the most part, distinct
|
||||
* from those in the rest of %GeographicLib, which deal with biaxial
|
||||
* ellipsoids; it is therefore convenient to put them in a distinct
|
||||
* namespace. In order to minimize the change of name clashes, the classes
|
||||
* in the namespace include "3" in their names. The header files are
|
||||
* included via GeographicLib/Triaxial/Class.hpp.
|
||||
**********************************************************************/
|
||||
namespace Triaxial {
|
||||
/**
|
||||
* \brief A triaxial ellipsoid.
|
||||
*
|
||||
* The class holds the basic information about a triaxial ellipsoid
|
||||
* given by
|
||||
* \f[ S(\mathbf R) =
|
||||
* \frac{X^2}{a^2} + \frac{Y^2}{b^2} + \frac{Z^2}{c^2} - 1 = 0, \f]
|
||||
* where the semiaxes satisfy \f$ a \ge b \ge c > 0\f$.
|
||||
* It is useful to characterize the shape of the ellipsoid by
|
||||
* \f[
|
||||
* \begin{align}
|
||||
* e &= \frac{\sqrt{a^2-c^2}}b,\\
|
||||
* k &= \frac{\sqrt{b^2-c^2}}{\sqrt{a^2-c^2}},\\
|
||||
* k' &= \frac{\sqrt{a^2-b^2}}{\sqrt{a^2-c^2}};
|
||||
* \end{align}
|
||||
* \f]
|
||||
* note that \f$k^2 + k'^2 = 1\f$. The spherical limit \f$ e\rightarrow 0
|
||||
* \f$ is nonuniform since the values of \f$k\f$ and \f$k'\f$ depend on how
|
||||
* the limit is taken. In this cases, it's convenient to specify the
|
||||
* ellipsoid in terms of these parameters. The semiaxes are related to these
|
||||
* parameters by
|
||||
* \f[
|
||||
* [a,b,c] = b \bigl[ \sqrt{1 + e^2k'^2}, 1, \sqrt{1 - e^2k^2} \bigr].
|
||||
* \f]
|
||||
*
|
||||
* Positions on the ellipsoid are given in term so the ellipsoidal
|
||||
* coordinates given in Section 2 of
|
||||
* - C. F. F. Karney,<br>
|
||||
* <a href="https://arxiv.org/abs/2511.01621">
|
||||
* Jacobi's solution for geodesics on a triaxial ellipsoid</a>,<br>
|
||||
* Technical Report, SRI International, Nov. 2025.<br>
|
||||
* <a href="https://arxiv.org/abs/2511.01621">arxiv:2511.01621</a>
|
||||
* .
|
||||
* Ellipsoidal latitude * \f$\beta\f$ and the ellipsoidal longitude
|
||||
* \f$\omega\f$ which are defined * by
|
||||
* \f[
|
||||
* \mathbf R = \begin{bmatrix}
|
||||
* a \cos\omega \sqrt{k^2\cos^2\beta + k'^2} \\
|
||||
* b \cos\beta \sin\omega \\
|
||||
* c \sin\beta \sqrt{k^2 + k'^2\sin^2\omega}
|
||||
* \end{bmatrix}.
|
||||
* \f]
|
||||
* Headings are given by the direction \f$ \alpha \f$ measured clockwise from
|
||||
* a line of constant \f$ \omega \f$. Conversions between cartesian and
|
||||
* ellipsoidal coordinates is provided by cart2toellip() and elliptocart2().
|
||||
*
|
||||
* The ellipsoid coordinates "cover" the ellipsoid twice; the replacement
|
||||
* \f[
|
||||
* \begin{align}
|
||||
* \omega & \rightarrow -\omega,\\
|
||||
* \beta & \rightarrow \pi-\beta,\\
|
||||
* \alpha & \rightarrow \pi+\alpha,
|
||||
* \end{align}
|
||||
* \f]
|
||||
* leaves the position and direction unchanged; see AngNorm(),
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Ellipsoid3.cpp
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT Ellipsoid3 {
|
||||
public:
|
||||
/**
|
||||
* A type to hold three-dimensional positions and directions in cartesian
|
||||
* coordinates.
|
||||
**********************************************************************/
|
||||
using vec3 = std::array<Math::real, 3>;
|
||||
private:
|
||||
/// \cond SKIP
|
||||
friend class Cartesian3; // For access to cart2toellipint normvec
|
||||
friend class Geodesic3; // For Flip
|
||||
/// \endcond
|
||||
using real = Math::real;
|
||||
using ang = Angle;
|
||||
static void normvec(vec3& R) {
|
||||
real h = Math::hypot3(R[0], R[1], R[2]);
|
||||
// No checking for h = 0. Result will be NaNs (we rely on this in
|
||||
// Cartesian3::cart2rand).
|
||||
R[0] /= h; R[1] /= h; R[2] /= h;
|
||||
}
|
||||
static void Flip(ang& bet, ang& omg, ang& alp) {
|
||||
bet.reflect(false, true);
|
||||
omg.reflect(true);
|
||||
alp.reflect(true, true);
|
||||
}
|
||||
real _a, _b, _c; // semiaxes
|
||||
real _e2, _k2, _kp2, _k, _kp;
|
||||
bool _oblate, _prolate, _biaxial;
|
||||
void cart2toellipint(vec3 R, ang& bet, ang& omg, vec3 axes) const;
|
||||
/**
|
||||
* @return \e k the oblateness parameter.
|
||||
**********************************************************************/
|
||||
real k() const { return _k; }
|
||||
/**
|
||||
* @return \e kp the prolateness parameter.
|
||||
**********************************************************************/
|
||||
real kp() const { return _kp; }
|
||||
/**
|
||||
* @return whether the ellipsoid is oblate.
|
||||
*
|
||||
* This is determined by the condition kp2() == 0.
|
||||
**********************************************************************/
|
||||
bool oblate() const { return _oblate; }
|
||||
/**
|
||||
* @return whether the ellipsoid is prolate.
|
||||
*
|
||||
* This is determined by the condition k2() == 0.
|
||||
**********************************************************************/
|
||||
bool prolate() const { return _prolate; }
|
||||
/**
|
||||
* @return whether the ellipsoid is oblate or prolate.
|
||||
**********************************************************************/
|
||||
bool biaxial() const { return _biaxial; }
|
||||
public:
|
||||
/**
|
||||
* The default constructor for a unit sphere in the oblate limit.
|
||||
**********************************************************************/
|
||||
Ellipsoid3();
|
||||
/**
|
||||
* An ellipsoid specified by its semiaxes.
|
||||
*
|
||||
* @param[in] a the major semiaxis.
|
||||
* @param[in] b the median semiaxis.
|
||||
* @param[in] c the minor semiaxis.
|
||||
* @exception GeographicErr if the required ordering is semiaxes is
|
||||
* violated.
|
||||
*
|
||||
* The semiaxes must satisfy \e a ≥ \e b ≥ \e c > 0.
|
||||
* If \e a = \e c (a sphere), then the oblate limit is taken.
|
||||
**********************************************************************/
|
||||
Ellipsoid3(real a, real b, real c);
|
||||
/**
|
||||
* An ellipsoid specified by its median semiaxis and shape.
|
||||
*
|
||||
* @param[in] b the middle semiaxis.
|
||||
* @param[in] e2 the eccentricity squared \f$e^2 = (a^2 - c^2)/b^2\f$.
|
||||
* @param[in] k2 the oblateness parameter squared \f$k^2 = (b^2 - c^2) /
|
||||
* (a^2 - c^2)\f$.
|
||||
* @param[in] kp2 the prolateness parameter squared \f$k'^2= (a^2 - b^2) /
|
||||
* (a^2 - c^2)\f$.
|
||||
* @exception GeographicErr if the required ordering is semiaxes is
|
||||
* violated.
|
||||
*
|
||||
* This form of the constructor is important when the eccentricity is small
|
||||
* and giving \e e2 allows for more precision.
|
||||
*
|
||||
* In the case of a sphere with \e e2 = 0, this constructor distinguishes
|
||||
* between and "oblate sphere" (\e k2 = 1), a "prolate sphere" (\e k2 = 0),
|
||||
* and a "triaxial sphere" (\e k2 &isin (0,1)). These distinctions matter
|
||||
* when ellipsoidal coordinates are used.
|
||||
*
|
||||
* \note The constructor normalizes \e k2 and \e kp2 so that \e k2 + \e kp2
|
||||
* = 1.
|
||||
**********************************************************************/
|
||||
Ellipsoid3(real b, real e2, real k2, real kp2);
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return \e a the major semiaxis.
|
||||
**********************************************************************/
|
||||
real a() const { return _a; }
|
||||
/**
|
||||
* @return \e b the median semiaxis.
|
||||
**********************************************************************/
|
||||
real b() const { return _b; }
|
||||
/**
|
||||
* @return \e c the minor semiaxis.
|
||||
**********************************************************************/
|
||||
real c() const { return _c; }
|
||||
/**
|
||||
* @return \e e2 the eccentricity squared.
|
||||
**********************************************************************/
|
||||
real e2() const { return _e2; }
|
||||
/**
|
||||
* @return \e k2 the oblateness parameter squared.
|
||||
**********************************************************************/
|
||||
real k2() const { return _k2; }
|
||||
/**
|
||||
* @return \e kp2 the prolateness parameter squared.
|
||||
**********************************************************************/
|
||||
real kp2() const { return _kp2; }
|
||||
///@}
|
||||
/** \name Normalizing functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Scale a position to ensure it lies on the ellipsoid
|
||||
*
|
||||
* @param[inout] R the position.
|
||||
*
|
||||
* The components of \e R are scaled so that it lies on the ellipsoid. The
|
||||
* resulting position is not in general the closest point on the ellipsoid.
|
||||
* Use Cartesian3::carttocart2() for that.
|
||||
**********************************************************************/
|
||||
void Norm(vec3& R) const;
|
||||
/**
|
||||
* Scale a position and direction to the ellipsoid
|
||||
*
|
||||
* @param[inout] R the position.
|
||||
* @param[inout] V the position.
|
||||
*
|
||||
* The components of \e R are scaled so that it lies on the ellipsoid. Then
|
||||
* \e V is projected to be tangent to the surface and is normalized to a
|
||||
* unit vector.
|
||||
**********************************************************************/
|
||||
void Norm(vec3& R, vec3& V) const;
|
||||
/**
|
||||
* Set the sheet for coordinates.
|
||||
*
|
||||
* @param[inout] bet the ellipsoidal latitude.
|
||||
* @param[inout] omg the ellipsoidal longitude.
|
||||
* @param[inout] alp the heading.
|
||||
* @param[in] alt if true switch to the alternate sheet.
|
||||
* @return whether the coordinates were changed.
|
||||
*
|
||||
* If alt = false (the default), the conventional sheet is used switching
|
||||
* the values of \e bet, \e omg, and \e alp, so that \e bet ∈
|
||||
* [-π/2, &pi/2].
|
||||
*
|
||||
* If alt = true, the alternate sheet is used switching the values of \e
|
||||
* bet, \e omg, and \e alp, so that \e omg ∈ [0, π].
|
||||
*
|
||||
* This routine does not change \e n (the number of turns) for the
|
||||
* coordinates.
|
||||
**********************************************************************/
|
||||
static bool AngNorm(Angle& bet, Angle& omg, Angle& alp, bool alt = false) {
|
||||
using std::signbit;
|
||||
// If !alt, put bet in [-pi/2,pi/2]
|
||||
// If alt, put omg in [0, pi]
|
||||
bool flip = alt ? signbit(omg.s()) : signbit(bet.c());
|
||||
if (flip)
|
||||
Flip(bet, omg, alp);
|
||||
return flip;
|
||||
}
|
||||
/**
|
||||
* Set the sheet for coordinates.
|
||||
*
|
||||
* @param[inout] bet the ellipsoidal latitude.
|
||||
* @param[inout] omg the ellipsoidal longitude.
|
||||
* @param[in] alt if true switch to the alternate sheet.
|
||||
* @return whether the coordinated were changed.
|
||||
*
|
||||
* This acts precisely the same and AngNorm(Angle&, Angle&, Angle&, bool)
|
||||
* except that \e alp is omitted.
|
||||
**********************************************************************/
|
||||
static bool AngNorm(Angle& bet, Angle& omg, bool alt = false) {
|
||||
using std::signbit;
|
||||
// If !alt, put bet in [-pi/2,pi/2]
|
||||
// If alt, put omg in [0, pi]
|
||||
bool flip = alt ? signbit(omg.s()) : signbit(bet.c());
|
||||
if (flip) {
|
||||
ang alp;
|
||||
Flip(bet, omg, alp);
|
||||
}
|
||||
return flip;
|
||||
}
|
||||
///@}
|
||||
/** \name Coordinate conversions.
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* Convert a cartesian position to ellipsoidal coordinates.
|
||||
*
|
||||
* @param[in] R the cartesian position.
|
||||
* @param[out] bet the ellipsoidal latitude.
|
||||
* @param[out] omg the ellipsoidal longitude.
|
||||
*
|
||||
* \note \e R must lie on the surface of the ellipsoid. The "2" in "cart2"
|
||||
* is used to emphasize this.
|
||||
**********************************************************************/
|
||||
void cart2toellip(vec3 R, Angle& bet, Angle& omg) const;
|
||||
/**
|
||||
* Convert a cartesian position and direction to ellipsoidal coordinates.
|
||||
*
|
||||
* @param[in] R the cartesian position.
|
||||
* @param[in] V the cartesian direction.
|
||||
* @param[out] bet the ellipsoidal latitude.
|
||||
* @param[out] omg the ellipsoidal longitude.
|
||||
* @param[out] alp the azimuth.
|
||||
*
|
||||
* \note \e R must lie on the surface of the ellipsoid and \e V must be
|
||||
* tangent to the surface at that point. The "2" in "cart2" is used to
|
||||
* emphasize this.
|
||||
**********************************************************************/
|
||||
void cart2toellip(vec3 R, vec3 V,
|
||||
Angle& bet, Angle& omg, Angle& alp) const;
|
||||
/**
|
||||
* Convert an ellipsoid position and cartesian direction to a heading.
|
||||
*
|
||||
* @param[in] bet the ellipsoidal latitude.
|
||||
* @param[in] omg the ellipsoidal longitude.
|
||||
* @param[in] V the cartesian direction.
|
||||
* @param[out] alp the azimuth.
|
||||
*
|
||||
* This is a variant of cart2toellip(vec3, vec3, Angle&, Angle&, Angle&)
|
||||
* where \e bet and \e omg are used to ensure that the correct sheet is
|
||||
* used in determining \e alp.
|
||||
*
|
||||
* \note \e V must be tangent to the surface of the ellipsoid. The "2" in
|
||||
* "cart2" is used to emphasize this.
|
||||
**********************************************************************/
|
||||
void cart2toellip(Angle bet, Angle omg,
|
||||
vec3 V, Angle& alp) const;
|
||||
/**
|
||||
* Convert ellipsoidal coordinates to a cartesian position.
|
||||
*
|
||||
* @param[in] bet the ellipsoidal latitude.
|
||||
* @param[in] omg the ellipsoidal longitude.
|
||||
* @param[out] R the cartesian position.
|
||||
**********************************************************************/
|
||||
void elliptocart2(Angle bet, Angle omg, vec3& R) const;
|
||||
/**
|
||||
* Convert coordinates and heading to a cartesian position and direction.
|
||||
*
|
||||
* @param[in] bet the ellipsoidal latitude.
|
||||
* @param[in] omg the ellipsoidal longitude.
|
||||
* @param[in] alp the azimuth.
|
||||
* @param[out] R the cartesian position.
|
||||
* @param[out] V the cartesian direction.
|
||||
**********************************************************************/
|
||||
void elliptocart2(Angle bet, Angle omg, Angle alp,
|
||||
vec3& R, vec3& V) const;
|
||||
///@}
|
||||
/**
|
||||
* A global instantiation of Ellipsoid3 with the parameters for the
|
||||
* Earth.
|
||||
**********************************************************************/
|
||||
static const Ellipsoid3& Earth();
|
||||
|
||||
};
|
||||
|
||||
} // namespace Triaxial
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_TRIAXIAL_HPP
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
/**
|
||||
* \file Geodesic3.hpp
|
||||
* \brief Header for GeographicLib::Triaxial::Geodesic3 class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2024-2025) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GEODESIC3_HPP)
|
||||
#define GEOGRAPHICLIB_GEODESIC3_HPP 1
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <GeographicLib/Triaxial/Ellipsoid3.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Squelch warnings about dll vs shared_ptr
|
||||
# pragma warning (push)
|
||||
# pragma warning (disable: 4251)
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
namespace Triaxial {
|
||||
class GeodesicLine3;
|
||||
|
||||
/**
|
||||
* \brief The solution of the geodesic problem for a triaxial ellipsoid
|
||||
*
|
||||
* This is an implementation of
|
||||
* <a href="https://books.google.com/books?id=RbwGAAAAYAAJ&pg=PA309">
|
||||
* Jacobi's method for finding geodesics on a triaxial ellipsoid</a>
|
||||
* (1839). This class offers an interface to the solutions to the direct
|
||||
* geodesic problem via the GeodesicLine3 class which contains the meat of
|
||||
* Jacobi's direct solution. In addition it provides a solution to the
|
||||
* inverse problem which closely parallels the solution for the biaxial
|
||||
* problem given by GeodesicExact. For more details see \ref triaxial
|
||||
* and
|
||||
* - C. F. F. Karney,<br>
|
||||
* <a href="https://arxiv.org/abs/2511.01621">
|
||||
* Jacobi's solution for geodesics on a triaxial ellipsoid</a>,<br>
|
||||
* Technical Report, SRI International, Nov. 2025.<br>
|
||||
* <a href="https://arxiv.org/abs/2511.01621">arxiv:2511.01621</a>
|
||||
*
|
||||
* Data for testing the geodesic routines is available at
|
||||
* <a href="https://doi.org/10.5281/zenodo.12510796"> Test set of geodesics
|
||||
* on a triaxial ellipsoid (2024)</a>.
|
||||
*
|
||||
* \note There's a lot of new code here and testing is two orders of
|
||||
* magnitude more difficult compared with the biaxial case (an extra
|
||||
* parameter to fix the shape of the ellipsoid and geodesics now depend on
|
||||
* the longitude of the two end points separately). I've limited by testing
|
||||
* to ellipsoids with \e a/\e c ≤ 2. I don't expect any problems if \e
|
||||
* a/\e c ≤ 10; but you might run into problems with more eccentric
|
||||
* ellipsoids. The code treats oblate and prolate (biaxial) ellipsoids
|
||||
* correctly; but, again, there may be problems with triaxial ellipsoids
|
||||
* which are \e extremely close to oblate or prolate i.e., with either \e k
|
||||
* or \e k' very small. (However the triaxial model of the Earth where the
|
||||
* difference in the equatorial semiaxes is 70 m, \f$k' = 0.057\f$, is
|
||||
* treated just fine.) While I have made every effort to ensure that the
|
||||
* code is error free, it's likely that some bugs remain. Please use caution
|
||||
* with the results and report any problems (via email or with a Github
|
||||
* issue).
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Geodesic3.cpp
|
||||
*
|
||||
* <a href="Geod3Solve.1.html">Geod3Solve</a> is a command-line utility
|
||||
* providing access to the functionality of Geodesic3 and GeodesicLine3.
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT Geodesic3 {
|
||||
private:
|
||||
/// \cond SKIP
|
||||
// For access to BigValue, _ellipthresh, _biaxp
|
||||
friend class GeodesicLine3;
|
||||
/// \endcond
|
||||
using real = Math::real;
|
||||
using ang = Angle;
|
||||
static constexpr bool debug_ = false; // print out diagnostics
|
||||
static constexpr bool throw_ = true; // exception on convergence failure
|
||||
// special treatment for biaxial non-meridional
|
||||
static constexpr bool biaxp_ = true;
|
||||
// favor hybrid solution in terms of omg
|
||||
static constexpr bool hybridalt_ = true;
|
||||
// allow swapping of omega{1,2}
|
||||
static constexpr bool swapomg_ = false;
|
||||
static constexpr int maxit_ = 200;
|
||||
Ellipsoid3 _t;
|
||||
|
||||
// Run geodesic from bet1, omg1, alp1, find its first intersection with bet
|
||||
// = bet2a and return omg2a - omg2b
|
||||
real HybridA(ang bet1, ang omg1, ang alp1,
|
||||
ang bet2a, ang omg2b, bool betp) const;
|
||||
static ang findroot(const std::function<real(const ang&)>& f,
|
||||
ang xa, ang xb,
|
||||
real fa, real fb,
|
||||
int* countn = nullptr, int* countb = nullptr);
|
||||
bool _umbalt; // how coordinates wrap with umbilical lines
|
||||
// If k'^2 < ellipthresh transform phi -> F(phi, k^2)
|
||||
real _ellipthresh;
|
||||
mutable std::shared_ptr<GeodesicLine3> _umbline;
|
||||
static real BigValue() {
|
||||
using std::log;
|
||||
static real bigval = -3*log(std::numeric_limits<real>::epsilon());
|
||||
return bigval;
|
||||
}
|
||||
class gamblk {
|
||||
public:
|
||||
bool transpolar;
|
||||
// gamma = (k * cbet * salp)^2 - (kp * somg * calp)^2
|
||||
// = k2*cb2*sa2 - kp2*so2*ca2
|
||||
// Need accurate expressions for
|
||||
// k2 - gamma = k2*(sb2+ca2*cb2) + kp2*so2*ca2
|
||||
// kp2 + gamma = k2*cb2*sa2 + kp2*(co2+sa2*so2)
|
||||
// If gamma is given, eval new alp given new bet and new omg
|
||||
// gamma < 0
|
||||
// ca2 = (k2*cb2-gamma) / (k2*cb2+kp2*so2)
|
||||
// sa2 = (kp2+gamma - kp2*co2) / (k2*cb2+kp2*so2)
|
||||
// gamma > 0
|
||||
// ca2 = (k2-gamma - k2*sb2) / (k2*cb2+kp2*so2)
|
||||
// sa2 = (kp2*so2+gamma) / (k2*cb2+kp2*so2)
|
||||
// gamma > 0
|
||||
// k2*sb2 = spsi2 * (k2-gamma)
|
||||
// (k2-gamma - k2*sb2) = (k2-gamma)*(1-spsi2) = (k2-gamma)*cpsi2
|
||||
// spsi2 = k2*sb2/(k2-gamma)
|
||||
// cpsi2 = (k2*cb2-gamma)/(k2-gamma)
|
||||
real gamma,
|
||||
// [nu, nup]
|
||||
// = [sqrt(gam)/k, sqrt(1 - gam/k2)] for !signbit(gam)
|
||||
// = [sqrt(-gam)/kp, sqrt(1 + gam/kp2)] for signbit(gam)
|
||||
// unused for umbilics
|
||||
nu, nup,
|
||||
gammax, kx2, kxp2, kx, kxp;
|
||||
// Default values for gamma = +/-0
|
||||
gamblk() {}
|
||||
gamblk(const Geodesic3& tg, bool neg = false);
|
||||
gamblk(const Geodesic3& tg, ang bet, ang omg, ang alp);
|
||||
// gamblk(real gammax, real nux, real nupx)
|
||||
// : gamma(gammax), nu(nux), nup(nupx) {}
|
||||
};
|
||||
gamblk gamma(ang bet, ang omg, ang alp)
|
||||
const;
|
||||
// real a() const { return t().a(); } // not needed
|
||||
real b() const { return t().b(); }
|
||||
// real c() const { return t().c(); } // not needed
|
||||
real e2() const { return t().e2(); }
|
||||
real k2() const { return t().k2(); }
|
||||
real kp2() const { return t().kp2(); }
|
||||
real k() const { return t().k(); }
|
||||
real kp() const { return t().kp(); }
|
||||
bool oblate() const { return t().oblate(); }
|
||||
bool prolate() const { return t().prolate(); }
|
||||
bool biaxial() const { return t().biaxial(); }
|
||||
public:
|
||||
/**
|
||||
* Constructor for a triaxial ellipsoid defined by Ellipsoid3 object.
|
||||
*
|
||||
* @param[in] t the Ellipsoid3 object.
|
||||
**********************************************************************/
|
||||
Geodesic3(const Ellipsoid3& t = Ellipsoid3{});
|
||||
/**
|
||||
* Constructor for a triaxial ellipsoid with semiaxes.
|
||||
*
|
||||
* @param[in] a the largest semiaxis.
|
||||
* @param[in] b the middle semiaxis.
|
||||
* @param[in] c the smallest semiaxis.
|
||||
* @exception GeographicErr if the required ordering is semiaxes is
|
||||
* violated.
|
||||
*
|
||||
* The semiaxes must satisfy \e a ≥ \e b ≥ \e c > 0.
|
||||
* If \e a = \e c (a sphere), then the oblate limit is taken.
|
||||
**********************************************************************/
|
||||
Geodesic3(real a, real b, real c);
|
||||
/**
|
||||
* Alternate constructor for a triaxial ellipsoid.
|
||||
*
|
||||
* @param[in] b the middle semiaxis.
|
||||
* @param[in] e2 the eccentricity squared \f$e^2 = (a^2 - c^2)/b^2\f$.
|
||||
* @param[in] k2 the oblateness parameter squared \f$k^2 = (b^2 - c^2) /
|
||||
* (a^2 - c^2)\f$.
|
||||
* @param[in] kp2 the prolateness parameter squared \f$k'^2= (a^2 - b^2) /
|
||||
* (a^2 - c^2)\f$.
|
||||
* @exception GeographicErr if the required ordering is semiaxes is
|
||||
* violated.
|
||||
*
|
||||
* \note The constructor normalizes \e k2 and \e kp2 to ensure then \e k2 +
|
||||
* \e kp2 = 1.
|
||||
**********************************************************************/
|
||||
Geodesic3(real b, real e2, real k2, real kp2);
|
||||
/**
|
||||
* @return the Ellipsoid3 object for this projection.
|
||||
**********************************************************************/
|
||||
const Ellipsoid3& t() const { return _t; }
|
||||
/**
|
||||
* Solve the inverse problem
|
||||
*
|
||||
* @param[in] bet1 the ellipsoidal latitude of point 1.
|
||||
* @param[in] omg1 the ellipsoidal longitude of point 1.
|
||||
* @param[in] bet2 the ellipsoidal latitude of point 2.
|
||||
* @param[in] omg2 the ellipsoidal longitude of point 2.
|
||||
* @param[out] s12 the shortest distance between the points.
|
||||
* @param[out] alp1 the forward azimuth of the geodesic at point 1.
|
||||
* @param[out] alp2 the forward azimuth of the geodesic at point 2.
|
||||
* @return a GeodesicLine3 object defining the geodesic.
|
||||
**********************************************************************/
|
||||
GeodesicLine3 Inverse(Angle bet1, Angle omg1, Angle bet2, Angle omg2,
|
||||
real& s12, Angle& alp1, Angle& alp2) const;
|
||||
/**
|
||||
* Solve the inverse problem in degrees
|
||||
*
|
||||
* @param[in] bet1 the ellipsoidal latitude of point 1 (in degrees).
|
||||
* @param[in] omg1 the ellipsoidal longitude of point 1 (in degrees).
|
||||
* @param[in] bet2 the ellipsoidal latitude of point 2 (in degrees).
|
||||
* @param[in] omg2 the ellipsoidal longitude of point 2 (in degrees).
|
||||
* @param[out] s12 the shortest distance between the points.
|
||||
* @param[out] alp1 the forward azimuth of the geodesic at point 1 (in
|
||||
* degrees).
|
||||
* @param[out] alp2 the forward azimuth of the geodesic at point 2 (in
|
||||
* degrees).
|
||||
* @return a GeodesicLine3 object defining the geodesic.
|
||||
**********************************************************************/
|
||||
GeodesicLine3 Inverse(real bet1, real omg1, real bet2, real omg2,
|
||||
real& s12, real& alp1, real& alp2) const;
|
||||
/**
|
||||
* Solve the direct problem
|
||||
*
|
||||
* @param[in] bet1 the ellipsoidal latitude of point 1.
|
||||
* @param[in] omg1 the ellipsoidal longitude of point 1.
|
||||
* @param[in] alp1 the forward azimuth of the geodesic at point 1.
|
||||
* @param[in] s12 the distance from point 1 to point 2.
|
||||
* @param[out] bet2 the ellipsoidal latitude of point 2.
|
||||
* @param[out] omg2 the ellipsoidal longitude of point 2.
|
||||
* @param[out] alp2 the forward azimuth of the geodesic at point 2.
|
||||
* @return a GeodesicLine3 object defining the geodesic.
|
||||
**********************************************************************/
|
||||
GeodesicLine3 Direct(Angle bet1, Angle omg1, Angle alp1, real s12,
|
||||
Angle& bet2, Angle& omg2, Angle& alp2) const;
|
||||
/**
|
||||
* Solve the direct problem in degrees
|
||||
*
|
||||
* @param[in] bet1 the ellipsoidal latitude of point 1 (in degrees).
|
||||
* @param[in] omg1 the ellipsoidal longitude of point 1 (in degrees).
|
||||
* @param[in] alp1 the forward azimuth of the geodesic at point 1 (in
|
||||
* degrees).
|
||||
* @param[in] s12 the distance from point 1 to point 2.
|
||||
* @param[out] bet2 the ellipsoidal latitude of point 2 (in degrees).
|
||||
* @param[out] omg2 the ellipsoidal longitude of point 2 (in degrees).
|
||||
* @param[out] alp2 the forward azimuth of the geodesic at point 2 (in
|
||||
* degrees).
|
||||
* @return a GeodesicLine3 object defining the geodesic.
|
||||
**********************************************************************/
|
||||
GeodesicLine3 Direct(real bet1, real omg1, real alp1, real s12,
|
||||
real& bet2, real& omg2, real& alp2) const;
|
||||
/**
|
||||
* A geodesic line defined at a starting point
|
||||
*
|
||||
* @param[in] bet1 the ellipsoidal latitude of point 1.
|
||||
* @param[in] omg1 the ellipsoidal longitude of point 1.
|
||||
* @param[in] alp1 the forward azimuth of the geodesic at point 1.
|
||||
* @return a GeodesicLine3 object defining the geodesic.
|
||||
**********************************************************************/
|
||||
GeodesicLine3 Line(Angle bet1, Angle omg1, Angle alp1) const;
|
||||
/**
|
||||
* A geodesic line defined at a starting point specified in degrees
|
||||
*
|
||||
* @param[in] bet1 the ellipsoidal latitude of point 1 (in degrees).
|
||||
* @param[in] omg1 the ellipsoidal longitude of point 1 (in degrees).
|
||||
* @param[in] alp1 the forward azimuth of the geodesic at point 1 (in
|
||||
* degrees).
|
||||
* @return a GeodesicLine3 object defining the geodesic.
|
||||
**********************************************************************/
|
||||
GeodesicLine3 Line(real bet1, real omg1, real alp1) const;
|
||||
/**
|
||||
* Specify the behavior of umbilical geodesics
|
||||
*
|
||||
* @param[in] numbalt the new value of the \e umbalt flag.
|
||||
*
|
||||
* If \e umbalt is true (resp. false) then the latitude (resp. longitude)
|
||||
* of an umbilical geodesic is the librating coordinate and the longitude
|
||||
* (resp. latitude) is the rotating coordinate. This has no effect for
|
||||
* biaxial ellipsoids. In this case the latitude (resp. longitude) is
|
||||
* always the librating coordinate for oblate (resp. prolate) ellipsoids.
|
||||
**********************************************************************/
|
||||
void umbalt(bool numbalt) {
|
||||
if (_t.k2() > 0 && _t.kp2() > 0) _umbalt = numbalt;
|
||||
}
|
||||
/**
|
||||
* @return the value of the \e umbalt flag; see umbalt(bool)).
|
||||
**********************************************************************/
|
||||
bool umbalt() const { return _umbalt; }
|
||||
};
|
||||
|
||||
} // namespace Triaxial
|
||||
} // namespace GeographicLib
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning (pop)
|
||||
#endif
|
||||
|
||||
// Include this because all the Geodesic3 methods return a GeodesicLine3.
|
||||
#include <GeographicLib/Triaxial/GeodesicLine3.hpp>
|
||||
|
||||
#endif // GEOGRAPHICLIB_GEODESIC3_HPP
|
||||
+571
@@ -0,0 +1,571 @@
|
||||
/**
|
||||
* \file GeodesicLine3.hpp
|
||||
* \brief Header for GeographicLib::Triaxial::GeodesicLine3 class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2024-2025) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_GEODESICLINE3_HPP)
|
||||
#define GEOGRAPHICLIB_GEODESICLINE3_HPP 1
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <GeographicLib/EllipticFunction.hpp>
|
||||
#include <GeographicLib/Angle.hpp>
|
||||
#include <GeographicLib/Trigfun.hpp>
|
||||
#include <GeographicLib/Triaxial/Geodesic3.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Squelch warnings about dll vs fline + fline::fics + gline + gline::gics
|
||||
# pragma warning (push)
|
||||
# pragma warning (disable: 4251)
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
namespace Triaxial {
|
||||
|
||||
/**
|
||||
* \brief The direct geodesic problem for a triaxial ellipsoid
|
||||
*
|
||||
* This is an implementation of
|
||||
* <a href="https://books.google.com/books?id=RbwGAAAAYAAJ&pg=PA309">
|
||||
* Jacobi's method for finding geodesics on a triaxial ellipsoid</a> (1839).
|
||||
|
||||
* This class performs the quadrature necessary to evaluate to the integrals
|
||||
* for the course and distance equations and it solves the 2 coupled
|
||||
* equations to determine the latitude and longitude of the destination.
|
||||
* This functionality is used by the Geodesic3 class to solve the inverse
|
||||
* geodesic problem.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-GeodesicLine3.cpp
|
||||
*
|
||||
* <a href="Geod3Solve.1.html">Geod3Solve</a> is a command-line utility
|
||||
* providing access to the functionality of Geodesic3 and GeodesicLine3.
|
||||
*
|
||||
* The class experimental::TriaxialGeodesicODE solves the direct geodesic
|
||||
* problem by integrating the corresponding ordinary differential equations.
|
||||
* This is \e not part of %GeographicLib itself because it used Boost for
|
||||
* solving the ODEs and Boost is not a requirement for using %GeographicLib.
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT GeodesicLine3 {
|
||||
private:
|
||||
/// \cond SKIP
|
||||
friend class Geodesic3; // For access to fline, gline, etc.
|
||||
/// \endcond
|
||||
using real = Math::real;
|
||||
using ang = Angle;
|
||||
static constexpr int maxit_ = 300;
|
||||
|
||||
class hfun {
|
||||
// This combines ffun abd gfun in order to minimize the duplication of
|
||||
// code.
|
||||
// Establish consistent notation for coordinates:
|
||||
// theta = rotating coord, omega-90 or beta for circum- or transpolar
|
||||
// phi = librating coord, beta or omega-90 for circum- or transpolar
|
||||
// psi = rotating equivalent of phi
|
||||
// zeta = a generic version of theta or psi
|
||||
// u = possible change of vars for theta
|
||||
// v = possible change of vars for psi
|
||||
// w = a generic version of u or v
|
||||
private:
|
||||
real _kap, _kapp, _eps, _mu, _sqrtmu, _sqrtkap, _sqrtkapp;
|
||||
bool _distp, _tx;
|
||||
EllipticFunction _ell;
|
||||
TrigfunExt _fun;
|
||||
real _max;
|
||||
bool _umb, _meridr, _meridl, _biaxr, _biaxl;
|
||||
// The f functions
|
||||
// mu > 0
|
||||
static real fthtp(real c, real kap, real kapp, real eps, real mu);
|
||||
static real fup(real cn, real kap, real kapp, real eps, real mu);
|
||||
// mu == 0
|
||||
static real dfp(real c, real kap, real kapp, real eps);
|
||||
static real dfvp(real cn, real dn, real kap, real kapp, real eps);
|
||||
// mu < 0
|
||||
static real fpsip(real s, real c, real kap, real kapp,
|
||||
real eps, real mu);
|
||||
static real fvp(real dn, real kap, real kapp, real eps, real mu);
|
||||
// biaxial variant for kap = 0, kapp = 1
|
||||
static real fthtbiax(real tht, real eps, real mu);
|
||||
// biaxial variant for kap = 1, kapp = 0, mu <= 0, !_tx
|
||||
static real dfpsibiax(real s, real c, real eps, real mu);
|
||||
// NOT USED
|
||||
// biaxial variant for kap = 1, kapp = 0, mu <= 0, _tx
|
||||
// static real dfvbiax(real dn, real eps, real mu);
|
||||
|
||||
// The g functions
|
||||
// _mu > 0
|
||||
static real gthtp(real c, real kap, real kapp, real eps, real mu);
|
||||
static real gup(real cn, real dn, real kap, real kapp,
|
||||
real eps, real mu);
|
||||
// _mu == 0
|
||||
static real g0p(real c, real kap, real kapp, real eps);
|
||||
static real g0vp(real cn, real kap, real kapp, real eps);
|
||||
// _mu < 0
|
||||
static real gpsip(real s, real c, real kap, real kapp,
|
||||
real eps, real mu);
|
||||
static real gvp(real cn, real dn, real kap, real kapp,
|
||||
real eps, real mu);
|
||||
// biaxial variants for kap = 0, kapp = 1, mu >= 0
|
||||
static real gthtbiax(real tht, real eps, real mu);
|
||||
// biaxial variants for kap = 1, kapp = 0, mu <= 0
|
||||
static real gpsibiax(real s, real c, real eps, real mu);
|
||||
// NOT USED
|
||||
// static real gvbiax(real cn, real dn, real eps, real mu);
|
||||
|
||||
// Return atan(m * tan(x)) keeping result continuous. Only defined for
|
||||
// !signbit(m).
|
||||
static real modang(real x, real m) {
|
||||
return ang::radians(x).modang(m).radians();
|
||||
}
|
||||
real root(real z, real u0, int* countn, int* countb, real tol = 0) const;
|
||||
public:
|
||||
// Summary of f and g functions
|
||||
// _meridr = kap == 0 && mu == 0 (biaxial meridian rotating coordinate)
|
||||
// f = fthtbiax / sqrt(mu), fthtbiax = 1 inversion trivial
|
||||
// g = gthtbiax, gthtbiax = 0
|
||||
// _meridl = kapp == 0 && mu -= 0 (biaxial meridian librating coordinate)
|
||||
// f = (atan(sqrt(-mu)*tan(psi)) - dfpsiobl) / sqrt(-mu)
|
||||
// mu == 0 **
|
||||
// atan(sqrt(-mu)*tan(psi)) -> round(psi/pi)*pi
|
||||
// g = gpsibiax
|
||||
// mu > 0 (rotating coordinate)
|
||||
// f = ftht or fu
|
||||
// g = gtht or gu **
|
||||
// mu < 0 (librating coordinate)
|
||||
// f = fpsi or fv
|
||||
// g = gpsi or gv **
|
||||
// _umb = !biaxial && mu == 0 (triaxial umbilic)
|
||||
// f = (u + df)/sqrt(kap*kapp) or (u + dfv)/sqrt(kap*kapp)
|
||||
// g = g0 or g0v
|
||||
//
|
||||
// Handing of f funtions needs to be handled specially for
|
||||
// _merid[lr]
|
||||
// leading order behavior is separated out
|
||||
// N.B. inverse of f is discontinuous for mu == 0
|
||||
// functions which need special treatment
|
||||
// operator(), deriv, inv, Slope, Max
|
||||
// _umb
|
||||
// leading order behavior is separated out
|
||||
// otherwise everything is handled by generic routines
|
||||
//
|
||||
// Handling of g function is always generic
|
||||
// _umb
|
||||
// functions which need special treatment
|
||||
// Slope, fwd, rev, Max
|
||||
//
|
||||
hfun() {}
|
||||
hfun(bool distp, real kap, real kapp, real eps, real mu,
|
||||
const Geodesic3& tg);
|
||||
real operator()(real u) const;
|
||||
// THIS ISN"T USED
|
||||
// ang operator()(const ang& ang) const;
|
||||
real deriv(real u) const;
|
||||
real df(real u) const { return _fun(u); }
|
||||
real dfp(real u) const { return _fun.deriv(u); }
|
||||
|
||||
// Accurate (to tol) inverse by direct Newton (not using _finv)
|
||||
real inv(real z, int* countn = nullptr, int* countb = nullptr) const;
|
||||
|
||||
real fwd(real zeta) const {
|
||||
return _umb ? lam(zeta, _sqrtkapp) :
|
||||
(_tx ? _ell.F(zeta) : zeta);
|
||||
}
|
||||
real rev(real w) const {
|
||||
return _umb ? gd(w, _sqrtkapp) :
|
||||
(_tx ? _ell.am(w) : w);
|
||||
}
|
||||
int NCoeffs() const { return _fun.NCoeffs(); }
|
||||
bool txp() const { return _tx; }
|
||||
real HalfPeriod() const {
|
||||
return _umb ? Math::infinity() : (_tx ? _ell.K() : Math::pi()/2);
|
||||
}
|
||||
real Slope() const {
|
||||
using std::sqrt;
|
||||
return _umb ? 1 :
|
||||
!_distp && _meridl ? 0 :
|
||||
!_distp && _biaxl ? 1 - sqrt(-_mu) * _fun.Slope() :
|
||||
_fun.Slope();
|
||||
}
|
||||
real Max() const { return _max; }
|
||||
real MaxPlus() const {
|
||||
using std::fmax;
|
||||
return fmax(_max, HalfPeriod() * (Slope() == 0 ? 1 : Slope()) / 1000);
|
||||
}
|
||||
};
|
||||
|
||||
class fline {
|
||||
private:
|
||||
Geodesic3 _tg;
|
||||
Geodesic3::gamblk _gm;
|
||||
real _deltashift;
|
||||
hfun _fpsi, _ftht;
|
||||
public:
|
||||
class fics {
|
||||
// bundle of data setting the initial conditions for a geodesic
|
||||
public:
|
||||
// alp1 is angle measured from line of const rotating coording
|
||||
ang tht1, phi1, alp1, // rotating, librating starting point
|
||||
psi1, // phi1 transformed to rotating angle psi
|
||||
// Angles about which quantities oscillate
|
||||
// circumpolar:
|
||||
// omg0 not used, bet0 = cardinal(even), alp0 = cardinal(odd)
|
||||
// transpolar:
|
||||
// bet0 not used, omg0 = cardinal(even), alp0 = cardinal(even)
|
||||
// umbilical
|
||||
// bet0, omg0 = cardinal(even) = middle of starting segment
|
||||
// !umbalt: alp0 = cardinal(odd)
|
||||
// umbalt: alp0 = cardinal(even)
|
||||
tht0, phi0, alp0;
|
||||
real u0, v0, delta; // starting point geodesic
|
||||
int Nx, Ex; // Northgoing / eastgoing relative to tht
|
||||
fics() {}
|
||||
fics(const fline& f,
|
||||
ang bet1, ang omg1, ang alp1);
|
||||
void setquadrant(const fline& f, unsigned q);
|
||||
void pos1(bool transpolar,
|
||||
ang& bet10, ang& omg10, ang& alp10) const;
|
||||
};
|
||||
class disttx {
|
||||
// bundle of data to pass along for distance
|
||||
public:
|
||||
real phiw2, thtw2;
|
||||
int ind2;
|
||||
};
|
||||
fline() {}
|
||||
fline(const Geodesic3& tg, bool neg = false);
|
||||
fline(const Geodesic3& tg, Geodesic3::gamblk gm);
|
||||
const hfun& fpsi() const { return _fpsi; }
|
||||
const hfun& ftht() const { return _ftht; }
|
||||
const hfun& fbet() const {
|
||||
return !transpolar() ? fpsi() : ftht();
|
||||
}
|
||||
const hfun& fomg() const {
|
||||
return transpolar() ? fpsi() : ftht();
|
||||
}
|
||||
const Geodesic3& tg() const { return _tg; }
|
||||
const Ellipsoid3& t() const { return _tg.t(); }
|
||||
real gamma() const { return _gm.gamma; }
|
||||
real gammax() const { return _gm.gammax; }
|
||||
real kx2() const { return _gm.kx2; }
|
||||
real kxp2() const { return _gm.kxp2; }
|
||||
real kx() const { return _gm.kx; }
|
||||
real kxp() const { return _gm.kxp; }
|
||||
real nu() const { return _gm.nu; }
|
||||
real nup() const { return _gm.nup; }
|
||||
real deltashift() const { return _deltashift; }
|
||||
bool transpolar() const { return _gm.transpolar; }
|
||||
const Geodesic3::gamblk& gm() const { return _gm; }
|
||||
// Run fline to its first intersection with
|
||||
// (for betp) bet2 and return omg2
|
||||
// (for !betp) omg2 and return bet2
|
||||
real Hybrid0(const fics& fic, ang bet2, ang omg2,
|
||||
bool betp = true) const;
|
||||
// Run fline to its first intersection with bet and return resulting
|
||||
// bet2a, omg2a, alp2a (without angle normalization) and distance
|
||||
// calculation object
|
||||
disttx Hybrid(const fics& fic, ang betomg2,
|
||||
ang& bet2a, ang& omg2a, ang& alp2a,
|
||||
bool betp = true) const;
|
||||
disttx ArcPos0(const fics& fic, ang tau12,
|
||||
ang& bet2a, ang& omg2a, ang& alp2a,
|
||||
bool betp = true) const;
|
||||
};
|
||||
|
||||
class gline {
|
||||
private:
|
||||
Geodesic3 _tg;
|
||||
Geodesic3::gamblk _gm;
|
||||
hfun _gpsi, _gtht;
|
||||
public:
|
||||
real s0;
|
||||
class gics {
|
||||
// bundle of data setting the initial conditions for a distance calc
|
||||
public:
|
||||
real sig1, s13; // starting point
|
||||
gics() {}
|
||||
gics(const gline& g, const fline::fics& fic);
|
||||
};
|
||||
gline() {}
|
||||
gline(const Geodesic3& tg, bool neg = false);
|
||||
gline(const Geodesic3& tg, const Geodesic3::gamblk& gm);
|
||||
real gamma() const { return _gm.gamma; }
|
||||
real gammax() const { return _gm.gammax; }
|
||||
real kx2() const { return _gm.kx2; }
|
||||
real kxp2() const { return _gm.kxp2; }
|
||||
real kx() const { return _gm.kx; }
|
||||
real kxp() const { return _gm.kxp; }
|
||||
bool transpolar() const { return _gm.transpolar; }
|
||||
const hfun& gpsi() const { return _gpsi; }
|
||||
const hfun& gtht() const { return _gtht; }
|
||||
const hfun& gbet() const {
|
||||
return !transpolar() ? gpsi() : gtht();
|
||||
}
|
||||
const hfun& gomg() const {
|
||||
return transpolar() ? gpsi() : gtht();
|
||||
}
|
||||
const Geodesic3& tg() const { return _tg; }
|
||||
const Ellipsoid3& t() const { return _tg.t(); }
|
||||
const Geodesic3::gamblk& gm() const { return _gm; }
|
||||
real dist(gics ic, fline::disttx d) const;
|
||||
};
|
||||
|
||||
class zvals {
|
||||
public:
|
||||
real z, fz, gz;
|
||||
zvals(real z0 = 0, real fz0 = 0, real gz0 = 0)
|
||||
: z(z0), fz(fz0), gz(gz0) {}
|
||||
bool operator<(const zvals& t) const { return z < t.z; }
|
||||
bool operator==(const zvals& t) const { return z == t.z; }
|
||||
};
|
||||
|
||||
class zset {
|
||||
private:
|
||||
std::vector<zvals> _s;
|
||||
public:
|
||||
zset(const zvals& a, const zvals& b)
|
||||
: _s({a, b})
|
||||
{
|
||||
if (a == b)
|
||||
// Allow coincident start and end values
|
||||
_s.resize(1);
|
||||
else if (!(a < b && a.fz <= b.fz && a.gz <= b.gz))
|
||||
throw GeographicLib::GeographicErr("bad zset initializer");
|
||||
}
|
||||
int num() const { return int(_s.size()); }
|
||||
const zvals& val(int i) const { return _s[i]; }
|
||||
const zvals& min() const { return _s[0]; }
|
||||
const zvals& max() const { return _s.back(); }
|
||||
int insert(zvals& t, int flag = 0);
|
||||
real bisect() const {
|
||||
// return (min().z + max().z) / 2;
|
||||
// return z in the middle of biggest gap
|
||||
if (num() == 1)
|
||||
return min().z;
|
||||
real maxgap = -1; int maxind = 0;
|
||||
for (int i = 0; i < num() - 1; ++i) {
|
||||
real gap = _s[i+1].z - _s[i].z;
|
||||
if (gap > maxgap) {
|
||||
maxgap = gap;
|
||||
maxind = i;
|
||||
}
|
||||
}
|
||||
return (_s[maxind].z + _s[maxind+1].z) / 2;
|
||||
}
|
||||
};
|
||||
|
||||
Geodesic3 _tg;
|
||||
fline _f;
|
||||
fline::fics _fic;
|
||||
gline _g;
|
||||
gline::gics _gic;
|
||||
|
||||
static void solve2(real f0, real g0,
|
||||
const hfun& fx, const hfun& fy,
|
||||
const hfun& gx, const hfun& gy,
|
||||
real& x, real& y,
|
||||
int* countn = nullptr, int* countb = nullptr);
|
||||
static void solve2u(real f0, real g0,
|
||||
const hfun& fx, const hfun& fy,
|
||||
const hfun& gx, const hfun& gy,
|
||||
real& x, real& y,
|
||||
int* countn = nullptr, int* countb = nullptr);
|
||||
static void newt2(real f0, real g0,
|
||||
const hfun& fx, const hfun& fy,
|
||||
const hfun& gx, const hfun& gy,
|
||||
real xa, real xb, real xscale,
|
||||
real ya, real yb, real yscale,
|
||||
real fscale, real gscale,
|
||||
real& x, real& y,
|
||||
int* countn = nullptr, int* countb = nullptr);
|
||||
static void zsetsinsert(zset& xset, zset& yset,
|
||||
zvals& xfg, zvals& yfg,
|
||||
real f0, real g0);
|
||||
static void zsetsdiag(const zset& xset, const zset& yset,
|
||||
real f0, real g0);
|
||||
static std::pair<real, real> zsetsbisect(const zset& xset, const zset& yset,
|
||||
real f0, real g0, bool secant);
|
||||
static real bigclamp(real x, real mult = 1) {
|
||||
using std::fmax, std::fmin;
|
||||
real z = mult * Geodesic3::BigValue();
|
||||
return Math::clamp(x, -z, z);
|
||||
}
|
||||
static real lamang0(ang x, real mult = 1) {
|
||||
// lam(x) when x is an ang -- no clamping
|
||||
using std::asinh, std::fabs;
|
||||
return asinh(mult * x.t());
|
||||
}
|
||||
static real lamang(ang x, real mult = 1) {
|
||||
// lam(x) when x is an ang -- with clamping
|
||||
// A consistent large value for x near pi/2.
|
||||
return bigclamp(lamang0(x, mult));
|
||||
}
|
||||
static real lam(real x, real mult = 1) {
|
||||
using std::tan, std::asinh, std::fabs, std::copysign;
|
||||
// A consistent large value for x near pi/2. Also deals with the issue
|
||||
// that tan(pi/2) may be negative, e.g., for long doubles.
|
||||
return fabs(x) >= Math::pi()/2 ? copysign(Geodesic3::BigValue(), x) :
|
||||
asinh(mult * tan(x));
|
||||
}
|
||||
static real gd(real x, real mult = 1) {
|
||||
using std::atan, std::sinh;
|
||||
return atan(sinh(x) / mult);
|
||||
}
|
||||
static ang anglam(real u, real mult = 1) {
|
||||
using std::sinh;
|
||||
return ang(sinh(u), mult, 0);
|
||||
}
|
||||
static real mcosh(real u, real mult = 1) {
|
||||
using std::cosh, std::sinh, std::hypot;
|
||||
return mult == 1 ? cosh(u) : hypot(sinh(u), mult) / mult;
|
||||
}
|
||||
|
||||
const hfun& fbet() const { return _f.fbet(); }
|
||||
const hfun& fomg() const { return _f.fomg(); }
|
||||
const hfun& fpsi() const { return _f.fpsi(); }
|
||||
const hfun& ftht() const { return _f.ftht(); }
|
||||
const hfun& gbet() const { return _g.gbet(); }
|
||||
const hfun& gomg() const { return _g.gomg(); }
|
||||
const hfun& gpsi() const { return _g.gpsi(); }
|
||||
const hfun& gtht() const { return _g.gtht(); }
|
||||
|
||||
// remainder with result in
|
||||
// [-y/2, y/2) if alt = false (default)
|
||||
// (-y/2, y/2] if alt = true
|
||||
static std::pair<real, real> remx(real x, real y, bool alt = false) {
|
||||
using std::remainder, std::rint;
|
||||
real z = remainder(x, y);
|
||||
if (alt) {
|
||||
if (z == -y/2) z = y/2;
|
||||
} else {
|
||||
if (z == y/2) z = -y/2;
|
||||
}
|
||||
return std::pair<real, real>(z, rint((x - z) / y));
|
||||
}
|
||||
// remainder with x in
|
||||
// [-pi/2, pi/2) if alt = false (default)
|
||||
// (-pi/2, pi/2] if alt = true
|
||||
// equivalent to remx(x.radians(), Math::pi(), alt)
|
||||
static std::pair<real, real> remx(ang x, bool alt = false) {
|
||||
using std::signbit;
|
||||
real m = 0;
|
||||
if (signbit(x.c())) {
|
||||
x -= ang::cardinal(2);
|
||||
++m;
|
||||
}
|
||||
if (x.c() == 0) {
|
||||
if (alt && signbit(x.s())) {
|
||||
x += ang::cardinal(2);
|
||||
--m;
|
||||
} else if (!alt && !signbit(x.s())) {
|
||||
x -= ang::cardinal(2);
|
||||
++m;
|
||||
}
|
||||
}
|
||||
return std::pair<real, real>(x.radians0(), m + 2*x.n());
|
||||
}
|
||||
static bool biaxspecial(const Geodesic3& tg, real gamma) {
|
||||
using std::fabs;
|
||||
return Geodesic3::biaxp_ && (tg.t().k2() == 0 || tg.t().kp2() == 0) &&
|
||||
gamma != 0 && fabs(gamma) < tg._ellipthresh;
|
||||
}
|
||||
// Private constructor to assemble the pieces of the class on exiting
|
||||
// Geodesic3::Inverse.
|
||||
GeodesicLine3(fline f, fline::fics fic, gline g, gline::gics gic);
|
||||
// Private constructor to provide the umbilical fline object.
|
||||
GeodesicLine3(const Geodesic3& tg);
|
||||
// Find first crossing of bet = bet2. NaN returned if
|
||||
// no crossing. Assume bet1 in [-90,0] (or maybe (-90,0]) and bet2 in
|
||||
// [bet2, -bet2]. Special cases, bet2 = bet1...
|
||||
// bet1 < 0, alp1 in [-90,90], omg2 = 0
|
||||
// bet1 == 0, alp1 in (-90,90), omg2 = 0,
|
||||
// alp1 = +/-90 omg2 = conj pt
|
||||
void Hybrid(Angle betomg2,
|
||||
Angle& bet2a, Angle& omg2a, Angle& alp2a,
|
||||
real& s12, bool betp = true) const;
|
||||
public:
|
||||
/**
|
||||
* Constructor for a geodesic line specified at point 1
|
||||
*
|
||||
* @param[in] tg the underlying Geodesic3 object.
|
||||
* @param[in] bet1 the ellipsoidal latitude of point 1.
|
||||
* @param[in] omg1 the ellipsoidal longitude of point 1.
|
||||
* @param[in] alp1 the forward azimuth of the geodesic at point 1.
|
||||
**********************************************************************/
|
||||
GeodesicLine3(const Geodesic3& tg, Angle bet1, Angle omg1, Angle alp1);
|
||||
/**
|
||||
* Constructor for a geodesic line specified at point 1 in degrees
|
||||
*
|
||||
* @param[in] tg the underlying Geodesic3 object.
|
||||
* @param[in] bet1 the ellipsoidal latitude of point 1.
|
||||
* @param[in] omg1 the ellipsoidal longitude of point 1 (in degrees).
|
||||
* @param[in] alp1 the forward azimuth of the geodesic at point 1 (in
|
||||
* degrees).
|
||||
**********************************************************************/
|
||||
GeodesicLine3(const Geodesic3& tg, real bet1, real omg1, real alp1);
|
||||
/**
|
||||
* Find point 2 a given distance from point 1
|
||||
*
|
||||
* @param[in] s12 the distance from point 1 to point 2.
|
||||
* @param[out] bet2 the ellipsoidal latitude of point 2.
|
||||
* @param[out] omg2 the ellipsoidal longitude of point 2.
|
||||
* @param[out] alp2 the forward azimuth of the geodesic at point 2.
|
||||
**********************************************************************/
|
||||
void Position(real s12, Angle& bet2, Angle& omg2, Angle& alp2) const;
|
||||
/**
|
||||
* Find point 2 a given distance from point 1 in degrees
|
||||
*
|
||||
* @param[in] s12 the distance from point 1 to point 2.
|
||||
* @param[out] bet2 the ellipsoidal latitude of point 2 (in degrees).
|
||||
* @param[out] omg2 the ellipsoidal longitude of point 2 (in degrees).
|
||||
* @param[out] alp2 the forward azimuth of the geodesic at point 2 (in
|
||||
* degrees).
|
||||
* @param[in] unroll if true (the default) "unroll" the coordinates;
|
||||
* otherwise reduce them to their conventional ranges.
|
||||
**********************************************************************/
|
||||
void Position(real s12, real& bet2, real& omg2, real& alp2,
|
||||
bool unroll = true) const;
|
||||
/**
|
||||
* Define a reference point 3 on the geodesic line
|
||||
*
|
||||
* @param[in] s13 distance from point 1 to point 3.
|
||||
**********************************************************************/
|
||||
void SetDistance(real s13) { _gic.s13 = s13; }
|
||||
/**
|
||||
* @return \e s13 the distance from point 1 to reference point 3.
|
||||
**********************************************************************/
|
||||
real Distance() const { return _gic.s13; }
|
||||
/**
|
||||
* Return the coordinates of point 1
|
||||
*
|
||||
* @param[out] bet1 the ellipsoidal latitude of point 1.
|
||||
* @param[out] omg1 the ellipsoidal longitude of point 1.
|
||||
* @param[out] alp1 the forward azimuth of the geodesic at point 1.
|
||||
**********************************************************************/
|
||||
void pos1(Angle& bet1, Angle& omg1, Angle& alp1) const;
|
||||
/**
|
||||
* Return the coordinates of point 1 in degrees
|
||||
*
|
||||
* @param[out] bet1 the ellipsoidal latitude of point 1 (in degrees).
|
||||
* @param[out] omg1 the ellipsoidal longitude of point 1 (in degrees).
|
||||
* @param[out] alp1 the forward azimuth of the geodesic at point 1 (in
|
||||
* degrees).
|
||||
* @param[in] unroll if true (the default), return the coordinates used to
|
||||
* specify this line; otherwise reduce the coordinates for point 1 to
|
||||
* their conventional ranges.
|
||||
**********************************************************************/
|
||||
void pos1(real& bet1, real& omg1, real& alp1, bool unroll = true) const;
|
||||
};
|
||||
|
||||
} // namespace Triaxial
|
||||
} // namespace GeographicLib
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#endif // GEOGRAPHICLIB_GEODESICLINE3_HPP
|
||||
+638
@@ -0,0 +1,638 @@
|
||||
/**
|
||||
* \file Trigfun.hpp
|
||||
* \brief Header for GeographicLib::Trigfun class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2024-2025) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_TRIGFUN_HPP)
|
||||
#define GEOGRAPHICLIB_TRIGFUN_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Squelch warnings about dll vs vector
|
||||
# pragma warning (push)
|
||||
# pragma warning (disable: 4251)
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
namespace Triaxial {
|
||||
class GeodesicLine3;
|
||||
class Conformal3;
|
||||
}
|
||||
/**
|
||||
* \brief Representing a function by a Fourier series
|
||||
*
|
||||
* This class mimic the functionality of Chebfun's 'trig' representation of
|
||||
* periodic functions. Key differences are:
|
||||
*
|
||||
* - Only odd or even functions are allowed (i.e., only sine of only cosine
|
||||
* terms in the Fourier series).
|
||||
* - Can specify that the function has symmetry about the quarter period
|
||||
* point so that the Fourier series only includes odd harmonics.
|
||||
* - The integral of a Trigfun is counted as a Trigfun even if it includes a
|
||||
* secular term.
|
||||
* - The inverse function of the integral is also a Trigfun (only makes sense
|
||||
* if the original function is either strictly positive or strictly
|
||||
* negative).
|
||||
*
|
||||
* Assuming the half period = \e h, the function f(x) is represented as
|
||||
* follows, here \f$ = (\pi/h) x\f$:
|
||||
* - \e sym = false,
|
||||
* - \e odd = true,
|
||||
* \f$ f(x) = C_0 y + \sum_{m = 1}^{M-1} C_m \sin my \f$;
|
||||
* - \e odd = false,
|
||||
* \f$ f(x) = \sum_{m = 0}^{M-1} C_m \cos my \f$;
|
||||
* - \e sym = true,
|
||||
* - \e odd = true,
|
||||
* \f$ f(x) = \sum_{m = 0}^{M-1} C_m \sin 2(m+\frac12) y \f$;
|
||||
* - \e odd = false,
|
||||
* \f$ f(x) = \sum_{m = 0}^{M-1} C_m \cos 2(m+\frac12) y \f$.
|
||||
* .
|
||||
*
|
||||
* Here we compute FFTs using the kissfft package
|
||||
* https://github.com/mborgerding/kissfft by Mark Borgerding.
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Trigfun.cpp
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT Trigfun {
|
||||
private:
|
||||
/// \cond SKIP
|
||||
friend class TrigfunExt; // For access to root sig 2
|
||||
friend class Triaxial::GeodesicLine3; // For access to root sig 2
|
||||
friend class Triaxial::Conformal3; // For access to root sig 2
|
||||
/// \endcond
|
||||
using real = Math::real;
|
||||
static constexpr bool debug_ = false;
|
||||
static constexpr bool throw_ = true; // exception on convergence failure
|
||||
static constexpr int maxit_ = 300;
|
||||
int _m, // Number of coefficients in series
|
||||
_n; // Number of samples in half/quarter period
|
||||
bool _odd, _sym;
|
||||
std::vector<real> _coeff;
|
||||
real _h, _q; // half, quarter, whole period
|
||||
mutable real _max;
|
||||
static int chop(const std::vector<real>& c, real tol, real scale = -1);
|
||||
static real tolerance(real tol) {
|
||||
static const real eps = std::numeric_limits<real>::epsilon();
|
||||
return tol <= 0 ? eps : tol;
|
||||
}
|
||||
// Function samples over half/quarter period of !sym/sym
|
||||
// odd sym cent samples nF nC
|
||||
// f f f |-|-|-|-|-|-|-|-| n+1 n+1 (4)
|
||||
// t f f --|-|-|-|-|-|-|-| n n+1 (1), (2), (3), (7)
|
||||
// f t f |-|-|-|-|-|-|-|-- n n (1)
|
||||
// t t f --|-|-|-|-|-|-|-| n n (1)
|
||||
// f f t -|-|-|-|-|-|-|-|- n n+1 (4), (6)
|
||||
// t f t -|-|-|-|-|-|-|-|- n n+1 (5)
|
||||
// f t t -|-|-|-|-|-|-|-|- n n
|
||||
// t t t -|-|-|-|-|-|-|-|- n n
|
||||
//
|
||||
// (1) missing end terms presumed zero
|
||||
// (2) included last term is usually zero, if non zero, gives secular term
|
||||
// (3) zeroth coeff used for secular term
|
||||
// (4) zeroth coeff gives constant.
|
||||
// (5) secular term should have been removed from samples
|
||||
// (6) last coeff is zero (but not for centerp)
|
||||
// (7) last coeff is zero (but not for !centerp)
|
||||
// Function is represented by (y = pi/h * x)
|
||||
// sym = false, sample in f_i = f(h * i/n)
|
||||
// odd = true (n samples, n+1 coeffs)
|
||||
// f_0 = 0, need f_i for i in (0, n], f_n defines linear contrib
|
||||
// f(x) = c[0] * y + sum(c[k] * sin(k * y), k, 1, n)
|
||||
// F(x) = 0 + (-h/pi) * sum( c[k]/k * cos(k * y), k, 1, n)
|
||||
// odd = false (n+1 samples, n+1 coeffs)
|
||||
// need f_i for i in [0, n]
|
||||
// f(x) = c[0] + sum(c[k] * cos(k * y), k, 1, n)
|
||||
// F(x) = (h/pi) * (c[0] * y + sum( c[k]/k * sin(k * y), k, 1, n))
|
||||
// sym = true, sample in f_i = f(q * i/n)
|
||||
// odd = true (n samples, n coeffs)
|
||||
// f_0 = 0, need f_i for i in (0, n] (n samples)
|
||||
// f(x) = sum(c[k] * sin(2*(k+1/2) * y), k, 0, n - 1)
|
||||
// F(x) = -(q/pi) * sum(c[k]/(k+1/2) * cos(2*(k+1/2) * y), k, 0, n - 1)
|
||||
// odd = false (n samples, n coeffs)
|
||||
// f_n = 0, need f_i for i in [0, n) (n samples)
|
||||
// f(x) = sum(c[k] * cos(2*(k+1/2) * y), k, 0, n - 1)
|
||||
// F(x) = (q/pi) * sum(c[k]/(k+1/2) * sin(2*(k+1/2) * y), k, 0, n - 1)
|
||||
|
||||
Trigfun(const std::vector<real>& c, bool odd, bool sym, real h)
|
||||
: _m(int(c.size()))
|
||||
, _n(sym ? _m : _m - 1)
|
||||
, _odd(odd)
|
||||
, _sym(sym)
|
||||
, _coeff(c)
|
||||
, _h(h)
|
||||
, _q(_h/2)
|
||||
, _max(-1)
|
||||
{}
|
||||
void refine(const Trigfun& tb);
|
||||
real check(const std::vector<real>& F, bool centerp, real tol) const;
|
||||
// Given z, return dx = finv(z) - nslope * z
|
||||
// dx0 is an estimate of dx (NaN means no information)
|
||||
// the "p" in the function name mean periodic (vs secular)
|
||||
real inversep(real z, const std::function<real(real)>& fp,
|
||||
real dx0, int* countn, int* countb, real tol) const;
|
||||
static Trigfun initbysamples(const std::vector<real>& F,
|
||||
bool odd, bool sym, real halfp, bool centerp);
|
||||
/**
|
||||
* Tags to indicate which routine is invoking root(). Because the root
|
||||
* functions may be called recursively, each invocation is tagged by an
|
||||
* indicator value \e ind. This is merely an aid to debugging.
|
||||
**********************************************************************/
|
||||
enum ind {
|
||||
NONE = 0,
|
||||
INV1,
|
||||
INV2,
|
||||
ARCPOS0,
|
||||
FFUNROOT,
|
||||
GFUNROOT,
|
||||
INVERSEP,
|
||||
PIINV,
|
||||
FINV,
|
||||
KINV,
|
||||
OTHER,
|
||||
};
|
||||
|
||||
/**
|
||||
* Given \e z, find \e x, such that \e z = \e f(\e x).
|
||||
*
|
||||
* @param[in] indicator a numeric indicator to track this call (can be
|
||||
* safely set to Trigfun::OTHER).
|
||||
* @param[in] z the value of \e f(\e x).
|
||||
* @param[in] fp the derivative of \e f(\e x).
|
||||
* @param[in] x0 an estimate of the solution, i.e., \e z ≈ \e f(\e
|
||||
* x0). Use Math::NaN() to indicate that no estimate is known.
|
||||
* @param[in] countn if not nullptr, a pointer to an integer that gets
|
||||
* incremented by the number of iterations.
|
||||
* @param[in] countb if not nullptr, a pointer to an integer that gets
|
||||
* incremented by the number of bisection steps (which indicates how well
|
||||
* Newton's method is working).
|
||||
* @param[in] tol the tolerance using in terminating the root finding. \e
|
||||
* tol = 0 (the default) mean to use the machine epsilon.
|
||||
* @return the root \e x = \e f <sup>−1</sup>(\e z).
|
||||
*
|
||||
* Newton's method is used to find the root. At each step the bounds are
|
||||
* adjusted. If any Newton step gives a result which lies outside the
|
||||
* bounds, a bisection step is taken instead.
|
||||
*
|
||||
* \warning The routine assumes that there's a unique root. This, in turn,
|
||||
* requires that \e f include a secular term.
|
||||
**********************************************************************/
|
||||
// root sig 2
|
||||
real root(ind indicator, real z, const std::function<real(real)>& fp,
|
||||
real x0, int* countn, int* countb, real tol) const;
|
||||
/**
|
||||
* A general purpose Newton solver for \e z = \e f(\e x).
|
||||
*
|
||||
* @param[in] indicator a numeric indicator to track this call (can be
|
||||
* safely set to Trigfun::OTHER).
|
||||
* @param[in] ffp a function returning \e f(\e x) and \e f'(\e x) as a
|
||||
* pair.
|
||||
* @param[in] z the value of \e f(\e x).
|
||||
* @param[in] x0 an estimate of the solution, i.e., \e z ≅ \e f(\e
|
||||
* x0).
|
||||
* @param[in] xa a lower estimate of the solution.
|
||||
* @param[in] xb an upper estimate of the solution.
|
||||
* @param[in] xscale a representative scale for \e x.
|
||||
* @param[in] zscale a representative scale for \e z.
|
||||
* @param[in] s ±1 depending on whether \e f is an increasing or
|
||||
* decreasing function.
|
||||
* @param[in] countn if not nullptr, a pointer to an integer that gets
|
||||
* incremented by the number of iterations.
|
||||
* @param[in] countb if not nullptr, a pointer to an integer that gets
|
||||
* incremented by the number of bisection steps (which indicates how well
|
||||
* Newton's method is working).
|
||||
* @param[in] tol the tolerance using in terminating the root finding. \e
|
||||
* tol = 0 (the default) mean to use the machine epsilon.
|
||||
* @return the root \e x = \e f <sup>−1</sup>(\e z).
|
||||
*
|
||||
* This is a static function, so \e f(\e x) need not be a Trigfun. \e ffp
|
||||
* provides both the function an its derivative in one function call to
|
||||
* accommodate the (common) situation where the two values can be
|
||||
* efficiently computed together.
|
||||
*
|
||||
* Newton's method is used to find the inverse function. At each step the
|
||||
* bounds are adjusted. If any Newton step gives a result which lies
|
||||
* outside the bounds, a bisection step is taken instead.
|
||||
*
|
||||
* \warning The routine assumes that there's a unique root lying in the
|
||||
* interval [\e xa, \e xb] and that \e x0 lies in the same interval.
|
||||
**********************************************************************/
|
||||
// root sig 4
|
||||
static real root(ind indicator,
|
||||
const std::function<std::pair<real, real>(real)>& ffp,
|
||||
real z,
|
||||
real x0, real xa, real xb,
|
||||
real xscale = 1, real zscale = 1, int s = 1,
|
||||
int* countn = nullptr, int* countb = nullptr,
|
||||
real tol = 0);
|
||||
/**
|
||||
* Produce a Trigfun for the inverse of \e f.
|
||||
*
|
||||
* @param[in] fp the derivative of \e f(\e x).
|
||||
* @param[in] countn if not nullptr, a pointer to an integer that gets
|
||||
* incremented by the number of iterations need to create the inverse.
|
||||
* @param[in] countb if not nullptr, a pointer to an integer that gets
|
||||
* incremented by the number of bisection steps (which indicates how well
|
||||
* Newton's method is working) needed to create the inverse.
|
||||
* @param[in] nmax the maximum number of points in a quarter period
|
||||
* (default 2^16 = 65536).
|
||||
* @param[in] tol the tolerance using in terminating the root finding. \e
|
||||
* tol = 0 (the default) mean to use the machine epsilon.
|
||||
* @param[in] scale; if \e scale is negative (the default), \e tol sets the
|
||||
* error relative to the largest Fourier coefficient. Otherwise, the error
|
||||
* is relative to the maximum of the largest Fourier coefficient and \e
|
||||
* scale.
|
||||
* @return the Trigfun representation of \e f <sup>−1</sup>(\e z).
|
||||
*
|
||||
* As with the normal constructor this routine successively doubles the
|
||||
* number of sample points, which are computed using Newton's method. A
|
||||
* good starting guess for Newton's method is provided by the previous
|
||||
* Fourier approximation. As a result the average number of Newton
|
||||
* iterations per sample point is about 1 or 2.
|
||||
*
|
||||
* \note Computing the inverse is only possible with a Trigfun with a
|
||||
* secular term.
|
||||
**********************************************************************/
|
||||
Trigfun inverse(const std::function<real(real)>& fp,
|
||||
int* countn, int* countb,
|
||||
int nmax, real tol, real scale) const;
|
||||
public:
|
||||
/**
|
||||
* Default constructor specifying with the function \e f(\e x) = 0.
|
||||
**********************************************************************/
|
||||
Trigfun()
|
||||
: _m(1)
|
||||
, _n(0)
|
||||
, _odd(false)
|
||||
, _sym(false)
|
||||
, _coeff(1, 0)
|
||||
, _h(Math::pi())
|
||||
, _q(_h/2)
|
||||
, _max(-1)
|
||||
{}
|
||||
/**
|
||||
* Construct a Trigfun with a given number of samples a function.
|
||||
*
|
||||
* @param[in] n the number of samples.
|
||||
* @param[in] f the function.
|
||||
* @param[in] odd is the function odd? If it's not odd, then it is even.
|
||||
* @param[in] sym is the function symmetric about the quarter period
|
||||
* point (so it contains only odd Fourier harmonics)?
|
||||
* @param[in] halfp the half period.
|
||||
* @param[in] centerp whether to sample on a centered grid (default false).
|
||||
*
|
||||
* For \e sym = false, \e n is the number of samples in a half period, and
|
||||
* spacing between the samples is \e halfp/\e n. (If \e centerp = false
|
||||
* and \e oddp = false, the function is, in fact sampled \e n + 1 times.)
|
||||
* The number of points given to the FFT routine is 2\e n.
|
||||
*
|
||||
* For \e sym = true, \e n is the number of samples in a quarter period, and
|
||||
* spacing between the samples is \e halfp/(2\e n). The number of points
|
||||
* given to the FFT routine is 4\e n.
|
||||
*
|
||||
* \note In order for the FFT method to operate efficiently, \e n should be
|
||||
* the product of a small factors (typically a power of 2).
|
||||
*
|
||||
* \warning \e f must be a periodic function and it must be either even or
|
||||
* odd. With \e odd = true and \e sym = false, the secular term can be
|
||||
* set with setsecular().
|
||||
**********************************************************************/
|
||||
Trigfun(int n, const std::function<real(real)>& f,
|
||||
bool odd, bool sym, real halfp, bool centerp = false);
|
||||
/**
|
||||
* Construct a Trigfun from a function of one argument.
|
||||
*
|
||||
* @param[in] f the function.
|
||||
* @param[in] odd is the function odd? If it's not odd, then it is even.
|
||||
* @param[in] sym is the function symmetric about the quarter period
|
||||
* point (so it contains only odd Fourier harmonics)?
|
||||
* @param[in] halfp the half period.
|
||||
* @param[in] nmax the maximum number of points in a half/quarter period
|
||||
* (default 2^16 = 65536).
|
||||
* @param[in] tol the tolerance, the default value 0 means use the machine
|
||||
* epsilon.
|
||||
* @param[in] scale; if \e scale is negative (the default), \e tol sets the
|
||||
* error relative to the largest Fourier coefficient. Otherwise, the error
|
||||
* is relative to the maximum of the largest Fourier coefficient and \e
|
||||
* scale.
|
||||
*
|
||||
* The constructor successively doubles the number of sample points and
|
||||
* updating the Fourier coefficients accordingly until the high order
|
||||
* coefficients become sufficiently small. At that point Fourier series is
|
||||
* truncated discarding some of the trailing coefficients. This mimics the
|
||||
* method used by Chebfun. In particular, the method used to truncate the
|
||||
* series is taken from Aurentz and L. N. Trefethen,
|
||||
* <a href="https://doi.org/10.1145/2998442"> Chopping a Chebyshev
|
||||
* series</a> (2017); <a href="https://arxiv.org/abs/1512.01803">
|
||||
* preprint</a>.
|
||||
*
|
||||
* \warning \e f must be a periodic function and it must be either even or
|
||||
* odd. With \e odd = true and \e sym = false, the secular term can be
|
||||
* set with setsecular().
|
||||
**********************************************************************/
|
||||
Trigfun(const std::function<real(real)>& f, bool odd, bool sym,
|
||||
real halfp, int nmax = 1 << 16,
|
||||
real tol = 0,
|
||||
real scale = -1);
|
||||
/**
|
||||
* Construct a Trigfun from a function of two arguments.
|
||||
*
|
||||
* @param[in] f the function.
|
||||
* @param[in] odd is the function odd? If it's not odd, then it is even.
|
||||
* @param[in] sym is the function symmetric about the quarter period
|
||||
* point (so it contains only odd Fourier harmonics)?
|
||||
* @param[in] halfp the half period.
|
||||
* @param[in] nmax the maximum number of points in a half/quarter period
|
||||
* (default 2^16 = 65536).
|
||||
* @param[in] tol the tolerance, the default value 0 means use the machine
|
||||
* epsilon.
|
||||
* @param[in] scale; if \e scale is negative (the default), \e tol sets the
|
||||
* error relative to the largest Fourier coefficient. Otherwise, the error
|
||||
* is relative to the maximum of the largest Fourier coefficient and \e
|
||||
* scale.
|
||||
*
|
||||
* This accommodates the situation where the inverse of a Trigfun \e g is
|
||||
* being computed using inverse(). In this case \e f(\e x, \e y0) returns
|
||||
* the value \e y such that \e g(\e y) = \e x. This is typically found
|
||||
* using Newton's method which requires a starting guess \e y0. In the
|
||||
* implementation of inverse(), the Fourier representation is successively
|
||||
* refined by doubling the number samples. At each stage, a good estimate
|
||||
* of the function values at the new points is found by using the current
|
||||
* Fourier representation.
|
||||
*
|
||||
* \warning \e f must be a periodic function and it must be either even or
|
||||
* odd. With \e odd = true and \e sym = false, the secular term can be
|
||||
* set with setsecular().
|
||||
**********************************************************************/
|
||||
Trigfun(const std::function<real(real, real)>& f, bool odd, bool sym,
|
||||
real halfp, int nmax = 1 << 16,
|
||||
real tol = 0,
|
||||
real scale = -1);
|
||||
/**
|
||||
* Set the coefficient of the secular term
|
||||
*
|
||||
* @param[in] f0 the value of \e f(\e halfp).
|
||||
*
|
||||
* \warning This throws an error unless \e odd = true and \e sym = false.
|
||||
**********************************************************************/
|
||||
void setsecular(real f0);
|
||||
/**
|
||||
* Evaluate the Trigfun.
|
||||
*
|
||||
* @param[in] x the function argument.
|
||||
* @return the function value \e f(\e x).
|
||||
**********************************************************************/
|
||||
real operator()(real x) const;
|
||||
// For support of Angle
|
||||
// real eval(Angle phi) const;
|
||||
|
||||
/**
|
||||
* The integral of a Trigfun.
|
||||
*
|
||||
* @return the integral.
|
||||
*
|
||||
* \warning The secular term (only present with \e odd = true and \e sym =
|
||||
* false) is ignored when taking the integral.
|
||||
**********************************************************************/
|
||||
Trigfun integral() const;
|
||||
/**
|
||||
* Produce a Trigfun for the inverse of \e f.
|
||||
*
|
||||
* @param[in] fp the derivative of \e f(\e x).
|
||||
* @param[in] nmax the maximum number of points in a quarter period
|
||||
* (default 2^16 = 65536).
|
||||
* @param[in] tol the tolerance using in terminating the root finding. \e
|
||||
* tol = 0 (the default) mean to use the machine epsilon.
|
||||
* @param[in] scale; if \e scale is negative (the default), \e tol sets the
|
||||
* error relative to the largest Fourier coefficient. Otherwise, the error
|
||||
* is relative to the maximum of the largest Fourier coefficient and \e
|
||||
* scale.
|
||||
* @return the Trigfun representation of \e f <sup>−1</sup>(\e z).
|
||||
*
|
||||
* As with the normal constructor this routine successively doubles the
|
||||
* number of sample points, which are computed using Newton's method. A
|
||||
* good starting guess for Newton's method is provided by the previous
|
||||
* Fourier approximation. As a result the average number of Newton
|
||||
* iterations per sample point is about 1 or 2.
|
||||
*
|
||||
* \e scale is used when \e f(\e x) is a correction term added to a larger
|
||||
* contribution; and it would then be the magnitude of the larger
|
||||
* contribution.
|
||||
|
||||
* \note Computing the inverse is only possible with a Trigfun with a
|
||||
* secular term.
|
||||
**********************************************************************/
|
||||
Trigfun inverse(const std::function<real(real)>& fp,
|
||||
int nmax = 1 << 16, real tol = 0, real scale = -1) const {
|
||||
return inverse(fp, nullptr, nullptr, nmax, tol, scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the function is odd or not. If it's not odd, then it is
|
||||
* even.
|
||||
**********************************************************************/
|
||||
bool Odd() const { return _odd; }
|
||||
/**
|
||||
* @return whether the function is symmetric about the quarter period
|
||||
* point. If it is it, then the Fourier series has only odd terms.
|
||||
**********************************************************************/
|
||||
bool Symmetric() const { return _sym; }
|
||||
/**
|
||||
* @return the half period of the function.
|
||||
**********************************************************************/
|
||||
real HalfPeriod() const { return _h; }
|
||||
/**
|
||||
* @return the number of terms in the Fourier series.
|
||||
**********************************************************************/
|
||||
int NCoeffs() const { return _m; }
|
||||
/**
|
||||
* @return an estimate of the amplitude of the oscillating component of \e
|
||||
* f.
|
||||
*
|
||||
* \note This estimate excludes any constant or secular terms in the
|
||||
* series. The estimate is found by summing the absolute values of the
|
||||
* remaining coefficients (and is thus an overestimate).
|
||||
**********************************************************************/
|
||||
real Max() const;
|
||||
/**
|
||||
* @return the (approximate) half range of the function.
|
||||
*
|
||||
* For a Trigfun containing a secular contribution this is the value of the
|
||||
* function at the half perioid. Otherwise Max() is returned.
|
||||
**********************************************************************/
|
||||
real HalfRange() const {
|
||||
return _odd && !_sym ? _coeff[0] * Math::pi() : Max();
|
||||
}
|
||||
/**
|
||||
* @return the average slope of the function.
|
||||
*
|
||||
* For a Trigfun containing a secular contribution this is the slope of the
|
||||
* secular component. Otherwise 0 is returned..
|
||||
**********************************************************************/
|
||||
real Slope() const {
|
||||
return _odd && !_sym ? HalfRange() / HalfPeriod() : 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A function defined by its derivative and its inverse.
|
||||
*
|
||||
* This is an extension of Trigfun which allows a function to be defined by
|
||||
* its derivative. In this case the derivative must be even, so that its
|
||||
* integral is odd (and taken to be zero at the origin).
|
||||
*
|
||||
* In addition, this class offers a flexible interface to computing the
|
||||
* inverse of the function. If the inverse is only required at a few points
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-TrigfunExt.cpp
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT TrigfunExt {
|
||||
private:
|
||||
/// \cond SKIP
|
||||
friend class Triaxial::GeodesicLine3; // For access internal inv, inv1
|
||||
/// \endcond
|
||||
using real = Math::real;
|
||||
std::function<real(real)> _fp;
|
||||
bool _sym;
|
||||
Trigfun _f, _finv;
|
||||
real _tol;
|
||||
int _nmax;
|
||||
bool _invp;
|
||||
int _countn, _countb;
|
||||
|
||||
// Approximate inverse using _finv
|
||||
real inv0(real z) const {
|
||||
if (!_invp) return Math::NaN();
|
||||
return _sym ? Math::NaN() : _finv(z);
|
||||
}
|
||||
// Accurate inverse by direct Newton (not using _finv)
|
||||
real inv1(real z, int* countn, int* countb) const {
|
||||
return _sym ? Math::NaN() : _f.root(Trigfun::INV1, z, _fp, Math::NaN(),
|
||||
countn, countb, 0);
|
||||
}
|
||||
// Accurate inverse correcting result from _finv
|
||||
real inv2(real z, int* countn, int* countb) const {
|
||||
if (!_invp) return Math::NaN();
|
||||
return _sym ? Math::NaN() :
|
||||
_f.root(Trigfun::INV2, z, _fp, _finv(z), countn, countb, 0);
|
||||
}
|
||||
real inv(real z, int* countn, int* countb) const {
|
||||
return _invp ? inv2(z, countn, countb) : inv1(z, countn, countb);
|
||||
}
|
||||
public:
|
||||
TrigfunExt() {}
|
||||
/**
|
||||
* Constructor specifying the derivative, an even periodic function
|
||||
*
|
||||
* @param[in] fp the derivative function, \e fp(\e x) = \e f'(\e x).
|
||||
* @param[in] halfp the half period.
|
||||
* @param[in] sym is \e fp symmetric about the quarter period point (so it
|
||||
* contains only odd Fourier harmonics)?
|
||||
* @param[in] scale; this is passed to the Trigfun constructor when finding
|
||||
* the Fourier series for \e fp.
|
||||
*
|
||||
* \warning \e fp must be an even periodic function. In addition \e fp
|
||||
* must be nonnegative for the inverse of \e f to be computed (in this
|
||||
* case, \e f is a monotonically increasing function). The inverse is
|
||||
* undefined for \e sym = true.
|
||||
**********************************************************************/
|
||||
TrigfunExt(const std::function<real(real)>& fp, real halfp,
|
||||
bool sym = false, real scale = -1);
|
||||
/**
|
||||
* Evaluate the TrigfunExt.
|
||||
*
|
||||
* @param[in] x the function argument.
|
||||
* @return the function value \e f(\e x).
|
||||
**********************************************************************/
|
||||
real operator()(real x) const { return _f(x); }
|
||||
/**
|
||||
* Evaluate the derivative for TrigfunExt.
|
||||
*
|
||||
* @param[in] x the function argument.
|
||||
* @return the value of the derivative \e fp(\e x). This uses the function
|
||||
* object passed to the constructor.
|
||||
**********************************************************************/
|
||||
real deriv(real x) const { return _fp(x); }
|
||||
/**
|
||||
* Evaluate the inverse of \e f
|
||||
*
|
||||
* @param[in] z the value of \e f(\e x)
|
||||
* @return the value of \e x = \e f <sup>−1</sup>(\e z).
|
||||
*
|
||||
* This compute the inverse using Newton's method with the derivative
|
||||
* function \e fp supplied on construction. Initially, the starting guess
|
||||
* is based on just the secular component of \e f(\e x). However, if
|
||||
* ComputeInverse() is called, a rough Trigfun approximation to the inverse
|
||||
* is found and this is used as the starting point for Newton's method.
|
||||
**********************************************************************/
|
||||
real inv(real z) const {
|
||||
return _invp ? inv2(z, nullptr, nullptr) : inv1(z, nullptr, nullptr);
|
||||
}
|
||||
/**
|
||||
* Compute a coarse Fourier series approximation of the inverse.
|
||||
*
|
||||
* This is used to provide a better starting guess for Newton's method in
|
||||
* inv(). Because ComputeInverse() is fairly expensive, this only makes
|
||||
* sense if inv() will be called many times. In order to limit the expense
|
||||
* in computing this approximate inverse, the number of Fourier components
|
||||
* in the Trigfun for the inverse is limited to 3/2 of the number of
|
||||
* components for \e f and the tolerance is set to the square root of the
|
||||
* machine epsilon.
|
||||
**********************************************************************/
|
||||
void ComputeInverse() {
|
||||
if (!_invp && !_sym) {
|
||||
_countn = _countb = 0;
|
||||
_finv = _f.inverse(_fp, &_countn, &_countb, _nmax, _tol, -1);
|
||||
_invp = true;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return whether the function is symmetric about the quarter period
|
||||
* point. If it is it, then the Fourier series has only odd harmonics.
|
||||
**********************************************************************/
|
||||
bool Symmetric() const { return _sym; }
|
||||
/**
|
||||
* @return the number of terms in the Fourier series for \e f.
|
||||
**********************************************************************/
|
||||
int NCoeffs() const { return _f.NCoeffs(); }
|
||||
/**
|
||||
* @return the number of terms in the Fourier series for
|
||||
* \e f<sup>−1</sup>.
|
||||
**********************************************************************/
|
||||
int NCoeffsInv() const {
|
||||
if (!_invp) return -1;
|
||||
return _finv.NCoeffs(); }
|
||||
/**
|
||||
* @return Max() for the underlying Trigfun.
|
||||
**********************************************************************/
|
||||
real Max() const { return _f.Max(); }
|
||||
/**
|
||||
* @return HalfPeriod() for the underlying Trigfun.
|
||||
**********************************************************************/
|
||||
real HalfPeriod() const { return _f.HalfPeriod(); }
|
||||
/**
|
||||
* @return HalfRange() for the underlying Trigfun.
|
||||
**********************************************************************/
|
||||
real HalfRange() const { return _f.HalfRange(); }
|
||||
/**
|
||||
* @return Slope() for the underlying Trigfun.
|
||||
**********************************************************************/
|
||||
real Slope() const { return _f.Slope(); }
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#endif // GEOGRAPHICLIB_TRIGFUN_HPP
|
||||
+422
@@ -0,0 +1,422 @@
|
||||
/**
|
||||
* \file UTMUPS.hpp
|
||||
* \brief Header for GeographicLib::UTMUPS class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2008-2022) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_UTMUPS_HPP)
|
||||
#define GEOGRAPHICLIB_UTMUPS_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Convert between geographic coordinates and UTM/UPS
|
||||
*
|
||||
* UTM and UPS are defined
|
||||
* - J. W. Hager, J. F. Behensky, and B. W. Drew,
|
||||
* <a href="https://web.archive.org/web/20161214054445/http://earth-info.nga.mil/GandG/publications/tm8358.2/TM8358_2.pdf">
|
||||
* The Universal Grids: Universal Transverse Mercator (UTM) and Universal
|
||||
* Polar Stereographic (UPS)</a>, Defense Mapping Agency, Technical Manual
|
||||
* TM8358.2 (1989).
|
||||
* .
|
||||
* Section 2-3 defines UTM and section 3-2.4 defines UPS. This document also
|
||||
* includes approximate algorithms for the computation of the underlying
|
||||
* transverse Mercator and polar stereographic projections. Here we
|
||||
* substitute much more accurate algorithms given by
|
||||
* GeographicLib:TransverseMercator and GeographicLib:PolarStereographic.
|
||||
* These are the algorithms recommended by the NGA document
|
||||
* - <a href="https://earth-info.nga.mil/php/download.php?file=coord-utmups">
|
||||
* The Universal Grids and the Transverse Mercator and Polar Stereographic
|
||||
* Map Projections</a>, NGA.SIG.0012 (2014).
|
||||
*
|
||||
* In this implementation, the conversions are closed, i.e., output from
|
||||
* Forward is legal input for Reverse and vice versa. The error is about 5nm
|
||||
* in each direction. However, the conversion from legal UTM/UPS coordinates
|
||||
* to geographic coordinates and back might throw an error if the initial
|
||||
* point is within 5nm of the edge of the allowed range for the UTM/UPS
|
||||
* coordinates.
|
||||
*
|
||||
* The simplest way to guarantee the closed property is to define allowed
|
||||
* ranges for the eastings and northings for UTM and UPS coordinates. The
|
||||
* UTM boundaries are the same for all zones. (The only place the
|
||||
* exceptional nature of the zone boundaries is evident is when converting to
|
||||
* UTM/UPS coordinates requesting the standard zone.) The MGRS lettering
|
||||
* scheme imposes natural limits on UTM/UPS coordinates which may be
|
||||
* converted into MGRS coordinates. For the conversion to/from geographic
|
||||
* coordinates these ranges have been extended by 100km in order to provide a
|
||||
* generous overlap between UTM and UPS and between UTM zones.
|
||||
*
|
||||
* The <a href="http://www.nga.mil">NGA</a> software package
|
||||
* <a href="https://earth-info.nga.mil/index.php?dir=wgs84&action=wgs84#tab_geotrans">geotrans</a>
|
||||
* also provides conversions to and from UTM and UPS. Version 2.4.2 (and
|
||||
* earlier) suffers from some drawbacks:
|
||||
* - Inconsistent rules are used to determine the whether a particular UTM or
|
||||
* UPS coordinate is legal. A more systematic approach is taken here.
|
||||
* - The underlying projections are not very accurately implemented.
|
||||
*
|
||||
* The GeographicLib::UTMUPS::EncodeZone encodes the UTM zone and hemisphere
|
||||
* to allow UTM/UPS coordinated to be displayed as, for example, "38N 444500
|
||||
* 3688500". According to NGA.SIG.0012_2.0.0_UTMUPS the use of "N" to denote
|
||||
* "north" in the context is not allowed (since a upper case letter in this
|
||||
* context denotes the MGRS latitude band). Consequently, as of version
|
||||
* 1.36, EncodeZone uses the lower case letters "n" and "s" to denote the
|
||||
* hemisphere. In addition EncodeZone accepts an optional final argument \e
|
||||
* abbrev, which, if false, results in the hemisphere being spelled out as in
|
||||
* "38north".
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-UTMUPS.cpp
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT UTMUPS {
|
||||
private:
|
||||
typedef Math::real real;
|
||||
static const int falseeasting_[4];
|
||||
static const int falsenorthing_[4];
|
||||
static const int mineasting_[4];
|
||||
static const int maxeasting_[4];
|
||||
static const int minnorthing_[4];
|
||||
static const int maxnorthing_[4];
|
||||
static const int epsg01N = 32601; // EPSG code for UTM 01N
|
||||
static const int epsg60N = 32660; // EPSG code for UTM 60N
|
||||
static const int epsgN = 32661; // EPSG code for UPS N
|
||||
static const int epsg01S = 32701; // EPSG code for UTM 01S
|
||||
static const int epsg60S = 32760; // EPSG code for UTM 60S
|
||||
static const int epsgS = 32761; // EPSG code for UPS S
|
||||
static real CentralMeridian(int zone)
|
||||
{ return real(6 * zone - 183); }
|
||||
// Throw an error if easting or northing are outside standard ranges. If
|
||||
// throwp = false, return bool instead.
|
||||
static bool CheckCoords(bool utmp, bool northp, real x, real y,
|
||||
bool msgrlimits = false, bool throwp = true);
|
||||
UTMUPS() = delete; // Disable constructor
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* In this class we bring together the UTM and UPS coordinates systems.
|
||||
* The UTM divides the earth between latitudes −80° and 84°
|
||||
* into 60 zones numbered 1 thru 60. Zone assign zone number 0 to the UPS
|
||||
* regions, covering the two poles. Within UTMUPS, nonnegative zone
|
||||
* numbers refer to one of the "physical" zones, 0 for UPS and [1, 60] for
|
||||
* UTM. Negative "pseudo-zone" numbers are used to select one of the
|
||||
* physical zones.
|
||||
**********************************************************************/
|
||||
enum zonespec {
|
||||
/**
|
||||
* The smallest pseudo-zone number.
|
||||
**********************************************************************/
|
||||
MINPSEUDOZONE = -4,
|
||||
/**
|
||||
* A marker for an undefined or invalid zone. Equivalent to NaN.
|
||||
**********************************************************************/
|
||||
INVALID = -4,
|
||||
/**
|
||||
* If a coordinate already include zone information (e.g., it is an MGRS
|
||||
* coordinate), use that, otherwise apply the UTMUPS::STANDARD rules.
|
||||
**********************************************************************/
|
||||
MATCH = -3,
|
||||
/**
|
||||
* Apply the standard rules for UTM zone assigment extending the UTM zone
|
||||
* to each pole to give a zone number in [1, 60]. For example, use UTM
|
||||
* zone 38 for longitude in [42°, 48°). The rules include the
|
||||
* Norway and Svalbard exceptions.
|
||||
**********************************************************************/
|
||||
UTM = -2,
|
||||
/**
|
||||
* Apply the standard rules for zone assignment to give a zone number in
|
||||
* [0, 60]. If the latitude is not in [−80°, 84°), then
|
||||
* use UTMUPS::UPS = 0, otherwise apply the rules for UTMUPS::UTM. The
|
||||
* tests on latitudes and longitudes are all closed on the lower end open
|
||||
* on the upper. Thus for UTM zone 38, latitude is in [−80°,
|
||||
* 84°) and longitude is in [42°, 48°).
|
||||
**********************************************************************/
|
||||
STANDARD = -1,
|
||||
/**
|
||||
* The largest pseudo-zone number.
|
||||
**********************************************************************/
|
||||
MAXPSEUDOZONE = -1,
|
||||
/**
|
||||
* The smallest physical zone number.
|
||||
**********************************************************************/
|
||||
MINZONE = 0,
|
||||
/**
|
||||
* The zone number used for UPS
|
||||
**********************************************************************/
|
||||
UPS = 0,
|
||||
/**
|
||||
* The smallest UTM zone number.
|
||||
**********************************************************************/
|
||||
MINUTMZONE = 1,
|
||||
/**
|
||||
* The largest UTM zone number.
|
||||
**********************************************************************/
|
||||
MAXUTMZONE = 60,
|
||||
/**
|
||||
* The largest physical zone number.
|
||||
**********************************************************************/
|
||||
MAXZONE = 60,
|
||||
};
|
||||
|
||||
/**
|
||||
* The standard zone.
|
||||
*
|
||||
* @param[in] lat latitude (degrees).
|
||||
* @param[in] lon longitude (degrees).
|
||||
* @param[in] setzone zone override (optional). If omitted, use the
|
||||
* standard rules for picking the zone. If \e setzone is given then use
|
||||
* that zone if it is nonnegative, otherwise apply the rules given in
|
||||
* UTMUPS::zonespec.
|
||||
* @exception GeographicErr if \e setzone is outside the range
|
||||
* [UTMUPS::MINPSEUDOZONE, UTMUPS::MAXZONE] = [−4, 60].
|
||||
*
|
||||
* This is exact.
|
||||
**********************************************************************/
|
||||
static int StandardZone(real lat, real lon, int setzone = STANDARD);
|
||||
|
||||
/**
|
||||
* Forward projection, from geographic to UTM/UPS.
|
||||
*
|
||||
* @param[in] lat latitude of point (degrees).
|
||||
* @param[in] lon longitude of point (degrees).
|
||||
* @param[out] zone the UTM zone (zero means UPS).
|
||||
* @param[out] northp hemisphere (true means north, false means south).
|
||||
* @param[out] x easting of point (meters).
|
||||
* @param[out] y northing of point (meters).
|
||||
* @param[out] gamma meridian convergence at point (degrees).
|
||||
* @param[out] k scale of projection at point.
|
||||
* @param[in] setzone zone override (optional).
|
||||
* @param[in] mgrslimits if true enforce the stricter MGRS limits on the
|
||||
* coordinates (default = false).
|
||||
* @exception GeographicErr if \e lat is not in [−90°,
|
||||
* 90°].
|
||||
* @exception GeographicErr if the resulting \e x or \e y is out of allowed
|
||||
* range (see Reverse); in this case, these arguments are unchanged.
|
||||
*
|
||||
* If \e setzone is omitted, use the standard rules for picking the zone.
|
||||
* If \e setzone is given then use that zone if it is nonnegative,
|
||||
* otherwise apply the rules given in UTMUPS::zonespec. The accuracy of
|
||||
* the conversion is about 5nm.
|
||||
*
|
||||
* The northing \e y jumps by UTMUPS::UTMShift() when crossing the equator
|
||||
* in the southerly direction. Sometimes it is useful to remove this
|
||||
* discontinuity in \e y by extending the "northern" hemisphere using
|
||||
* UTMUPS::Transfer:
|
||||
* \code
|
||||
double lat = -1, lon = 123;
|
||||
int zone;
|
||||
bool northp;
|
||||
double x, y, gamma, k;
|
||||
GeographicLib::UTMUPS::Forward(lat, lon, zone, northp, x, y, gamma, k);
|
||||
GeographicLib::UTMUPS::Transfer(zone, northp, x, y,
|
||||
zone, true, x, y, zone);
|
||||
northp = true;
|
||||
\endcode
|
||||
**********************************************************************/
|
||||
static void Forward(real lat, real lon,
|
||||
int& zone, bool& northp, real& x, real& y,
|
||||
real& gamma, real& k,
|
||||
int setzone = STANDARD, bool mgrslimits = false);
|
||||
|
||||
/**
|
||||
* Reverse projection, from UTM/UPS to geographic.
|
||||
*
|
||||
* @param[in] zone the UTM zone (zero means UPS).
|
||||
* @param[in] northp hemisphere (true means north, false means south).
|
||||
* @param[in] x easting of point (meters).
|
||||
* @param[in] y northing of point (meters).
|
||||
* @param[out] lat latitude of point (degrees).
|
||||
* @param[out] lon longitude of point (degrees).
|
||||
* @param[out] gamma meridian convergence at point (degrees).
|
||||
* @param[out] k scale of projection at point.
|
||||
* @param[in] mgrslimits if true enforce the stricter MGRS limits on the
|
||||
* coordinates (default = false).
|
||||
* @exception GeographicErr if \e zone, \e x, or \e y is out of allowed
|
||||
* range; this this case the arguments are unchanged.
|
||||
*
|
||||
* The accuracy of the conversion is about 5nm.
|
||||
*
|
||||
* UTM eastings are allowed to be in the range [0km, 1000km], northings are
|
||||
* allowed to be in in [0km, 9600km] for the northern hemisphere and in
|
||||
* [900km, 10000km] for the southern hemisphere. However UTM northings
|
||||
* can be continued across the equator. So the actual limits on the
|
||||
* northings are [-9100km, 9600km] for the "northern" hemisphere and
|
||||
* [900km, 19600km] for the "southern" hemisphere.
|
||||
*
|
||||
* UPS eastings and northings are allowed to be in the range [1200km,
|
||||
* 2800km] in the northern hemisphere and in [700km, 3300km] in the
|
||||
* southern hemisphere.
|
||||
*
|
||||
* These ranges are 100km larger than allowed for the conversions to MGRS.
|
||||
* (100km is the maximum extra padding consistent with eastings remaining
|
||||
* nonnegative.) This allows generous overlaps between zones and UTM and
|
||||
* UPS. If \e mgrslimits = true, then all the ranges are shrunk by 100km
|
||||
* so that they agree with the stricter MGRS ranges. No checks are
|
||||
* performed besides these (e.g., to limit the distance outside the
|
||||
* standard zone boundaries).
|
||||
**********************************************************************/
|
||||
static void Reverse(int zone, bool northp, real x, real y,
|
||||
real& lat, real& lon, real& gamma, real& k,
|
||||
bool mgrslimits = false);
|
||||
|
||||
/**
|
||||
* UTMUPS::Forward without returning convergence and scale.
|
||||
**********************************************************************/
|
||||
static void Forward(real lat, real lon,
|
||||
int& zone, bool& northp, real& x, real& y,
|
||||
int setzone = STANDARD, bool mgrslimits = false) {
|
||||
real gamma, k;
|
||||
Forward(lat, lon, zone, northp, x, y, gamma, k, setzone, mgrslimits);
|
||||
}
|
||||
|
||||
/**
|
||||
* UTMUPS::Reverse without returning convergence and scale.
|
||||
**********************************************************************/
|
||||
static void Reverse(int zone, bool northp, real x, real y,
|
||||
real& lat, real& lon, bool mgrslimits = false) {
|
||||
real gamma, k;
|
||||
Reverse(zone, northp, x, y, lat, lon, gamma, k, mgrslimits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer UTM/UPS coordinated from one zone to another.
|
||||
*
|
||||
* @param[in] zonein the UTM zone for \e xin and \e yin (or zero for UPS).
|
||||
* @param[in] northpin hemisphere for \e xin and \e yin (true means north,
|
||||
* false means south).
|
||||
* @param[in] xin easting of point (meters) in \e zonein.
|
||||
* @param[in] yin northing of point (meters) in \e zonein.
|
||||
* @param[in] zoneout the requested UTM zone for \e xout and \e yout (or
|
||||
* zero for UPS).
|
||||
* @param[in] northpout hemisphere for \e xout output and \e yout.
|
||||
* @param[out] xout easting of point (meters) in \e zoneout.
|
||||
* @param[out] yout northing of point (meters) in \e zoneout.
|
||||
* @param[out] zone the actual UTM zone for \e xout and \e yout (or zero
|
||||
* for UPS); this equals \e zoneout if \e zoneout ≥ 0.
|
||||
* @exception GeographicErr if \e zonein is out of range (see below).
|
||||
* @exception GeographicErr if \e zoneout is out of range (see below).
|
||||
* @exception GeographicErr if \e xin or \e yin fall outside their allowed
|
||||
* ranges (see UTMUPS::Reverse).
|
||||
* @exception GeographicErr if \e xout or \e yout fall outside their
|
||||
* allowed ranges (see UTMUPS::Reverse).
|
||||
*
|
||||
* \e zonein must be in the range [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0,
|
||||
* 60] with \e zonein = UTMUPS::UPS, 0, indicating UPS. \e zonein may
|
||||
* also be UTMUPS::INVALID.
|
||||
*
|
||||
* \e zoneout must be in the range [UTMUPS::MINPSEUDOZONE, UTMUPS::MAXZONE]
|
||||
* = [-4, 60]. If \e zoneout < UTMUPS::MINZONE then the rules give in
|
||||
* the documentation of UTMUPS::zonespec are applied, and \e zone is set to
|
||||
* the actual zone used for output.
|
||||
*
|
||||
* (\e xout, \e yout) can overlap with (\e xin, \e yin).
|
||||
**********************************************************************/
|
||||
static void Transfer(int zonein, bool northpin, real xin, real yin,
|
||||
int zoneout, bool northpout, real& xout, real& yout,
|
||||
int& zone);
|
||||
|
||||
/**
|
||||
* Decode a UTM/UPS zone string.
|
||||
*
|
||||
* @param[in] zonestr string representation of zone and hemisphere.
|
||||
* @param[out] zone the UTM zone (zero means UPS).
|
||||
* @param[out] northp hemisphere (true means north, false means south).
|
||||
* @exception GeographicErr if \e zonestr is malformed.
|
||||
*
|
||||
* For UTM, \e zonestr has the form of a zone number in the range
|
||||
* [UTMUPS::MINUTMZONE, UTMUPS::MAXUTMZONE] = [1, 60] followed by a
|
||||
* hemisphere letter, n or s (or "north" or "south" spelled out). For UPS,
|
||||
* it consists just of the hemisphere letter (or the spelled out
|
||||
* hemisphere). The returned value of \e zone is UTMUPS::UPS = 0 for UPS.
|
||||
* Note well that "38s" indicates the southern hemisphere of zone 38 and
|
||||
* not latitude band S, 32° ≤ \e lat < 40°. n, 01s, 2n, 38s,
|
||||
* south, 3north are legal. 0n, 001s, +3n, 61n, 38P are illegal. INV is a
|
||||
* special value for which the returned value of \e is UTMUPS::INVALID.
|
||||
**********************************************************************/
|
||||
static void DecodeZone(const std::string& zonestr,
|
||||
int& zone, bool& northp);
|
||||
|
||||
/**
|
||||
* Encode a UTM/UPS zone string.
|
||||
*
|
||||
* @param[in] zone the UTM zone (zero means UPS).
|
||||
* @param[in] northp hemisphere (true means north, false means south).
|
||||
* @param[in] abbrev if true (the default) use abbreviated (n/s) notation
|
||||
* for hemisphere; otherwise spell out the hemisphere (north/south)
|
||||
* @exception GeographicErr if \e zone is out of range (see below).
|
||||
* @exception std::bad_alloc if memoy for the string can't be allocated.
|
||||
* @return string representation of zone and hemisphere.
|
||||
*
|
||||
* \e zone must be in the range [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0,
|
||||
* 60] with \e zone = UTMUPS::UPS, 0, indicating UPS (but the resulting
|
||||
* string does not contain "0"). \e zone may also be UTMUPS::INVALID, in
|
||||
* which case the returned string is "inv". This reverses
|
||||
* UTMUPS::DecodeZone.
|
||||
**********************************************************************/
|
||||
static std::string EncodeZone(int zone, bool northp, bool abbrev = true);
|
||||
|
||||
/**
|
||||
* Decode EPSG.
|
||||
*
|
||||
* @param[in] epsg the EPSG code.
|
||||
* @param[out] zone the UTM zone (zero means UPS).
|
||||
* @param[out] northp hemisphere (true means north, false means south).
|
||||
*
|
||||
* EPSG (European Petroleum Survery Group) codes are a way to refer to many
|
||||
* different projections. DecodeEPSG decodes those referring to UTM or UPS
|
||||
* projections for the WGS84 ellipsoid. If the code does not refer to one
|
||||
* of these projections, \e zone is set to UTMUPS::INVALID. See
|
||||
* https://www.spatialreference.org/ref/epsg/
|
||||
**********************************************************************/
|
||||
static void DecodeEPSG(int epsg, int& zone, bool& northp);
|
||||
|
||||
/**
|
||||
* Encode zone as EPSG.
|
||||
*
|
||||
* @param[in] zone the UTM zone (zero means UPS).
|
||||
* @param[in] northp hemisphere (true means north, false means south).
|
||||
* @return EPSG code (or -1 if \e zone is not in the range
|
||||
* [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0, 60])
|
||||
*
|
||||
* Convert \e zone and \e northp to the corresponding EPSG (European
|
||||
* Petroleum Survery Group) codes
|
||||
**********************************************************************/
|
||||
static int EncodeEPSG(int zone, bool northp);
|
||||
|
||||
/**
|
||||
* @return shift (meters) necessary to align north and south halves of a
|
||||
* UTM zone (10<sup>7</sup>).
|
||||
**********************************************************************/
|
||||
static Math::real UTMShift();
|
||||
|
||||
/** \name Inspector functions
|
||||
**********************************************************************/
|
||||
///@{
|
||||
/**
|
||||
* @return \e a the equatorial radius of the WGS84 ellipsoid (meters).
|
||||
*
|
||||
* (The WGS84 value is returned because the UTM and UPS projections are
|
||||
* based on this ellipsoid.)
|
||||
**********************************************************************/
|
||||
static Math::real EquatorialRadius()
|
||||
{ return Constants::WGS84_a(); }
|
||||
|
||||
/**
|
||||
* @return \e f the flattening of the WGS84 ellipsoid.
|
||||
*
|
||||
* (The WGS84 value is returned because the UTM and UPS projections are
|
||||
* based on this ellipsoid.)
|
||||
**********************************************************************/
|
||||
static Math::real Flattening()
|
||||
{ return Constants::WGS84_f(); }
|
||||
///@}
|
||||
|
||||
};
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#endif // GEOGRAPHICLIB_UTMUPS_HPP
|
||||
+661
@@ -0,0 +1,661 @@
|
||||
/**
|
||||
* \file Utility.hpp
|
||||
* \brief Header for GeographicLib::Utility class
|
||||
*
|
||||
* Copyright (c) Charles Karney (2011-2024) <karney@alum.mit.edu> and licensed
|
||||
* under the MIT/X11 License. For more information, see
|
||||
* https://geographiclib.sourceforge.io/
|
||||
**********************************************************************/
|
||||
|
||||
#if !defined(GEOGRAPHICLIB_UTILITY_HPP)
|
||||
#define GEOGRAPHICLIB_UTILITY_HPP 1
|
||||
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <cctype>
|
||||
#include <ctime>
|
||||
#include <cstring>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Squelch warnings about constant conditional expressions
|
||||
# pragma warning (push)
|
||||
# pragma warning (disable: 4127)
|
||||
#endif
|
||||
|
||||
namespace GeographicLib {
|
||||
|
||||
/**
|
||||
* \brief Some utility routines for %GeographicLib
|
||||
*
|
||||
* Example of use:
|
||||
* \include example-Utility.cpp
|
||||
**********************************************************************/
|
||||
class GEOGRAPHICLIB_EXPORT Utility {
|
||||
private:
|
||||
static bool gregorian(int y, int m, int d) {
|
||||
// The original cut over to the Gregorian calendar in Pope Gregory XIII's
|
||||
// time had 1582-10-04 followed by 1582-10-15. Here we implement the
|
||||
// switch over used by the English-speaking world where 1752-09-02 was
|
||||
// followed by 1752-09-14. We also assume that the year always begins
|
||||
// with January 1, whereas in reality it often was reckoned to begin in
|
||||
// March.
|
||||
return 100 * (100 * y + m) + d >= 17520914; // or 15821015
|
||||
}
|
||||
static bool gregorian(int s) {
|
||||
return s >= 639799; // 1752-09-14
|
||||
}
|
||||
public:
|
||||
|
||||
/**
|
||||
* Convert a date to the day numbering sequentially starting with
|
||||
* 0001-01-01 as day 1.
|
||||
*
|
||||
* @param[in] y the year (must be positive).
|
||||
* @param[in] m the month, Jan = 1, etc. (must be positive). Default = 1.
|
||||
* @param[in] d the day of the month (must be positive). Default = 1.
|
||||
* @return the sequential day number.
|
||||
**********************************************************************/
|
||||
static int day(int y, int m = 1, int d = 1);
|
||||
|
||||
/**
|
||||
* Convert a date to the day numbering sequentially starting with
|
||||
* 0001-01-01 as day 1.
|
||||
*
|
||||
* @param[in] y the year (must be positive).
|
||||
* @param[in] m the month, Jan = 1, etc. (must be positive). Default = 1.
|
||||
* @param[in] d the day of the month (must be positive). Default = 1.
|
||||
* @param[in] check whether to check the date.
|
||||
* @exception GeographicErr if the date is invalid and \e check is true.
|
||||
* @return the sequential day number.
|
||||
**********************************************************************/
|
||||
static int day(int y, int m, int d, bool check);
|
||||
|
||||
/**
|
||||
* Given a day (counting from 0001-01-01 as day 1), return the date.
|
||||
*
|
||||
* @param[in] s the sequential day number (must be positive)
|
||||
* @param[out] y the year.
|
||||
* @param[out] m the month, Jan = 1, etc.
|
||||
* @param[out] d the day of the month.
|
||||
**********************************************************************/
|
||||
static void date(int s, int& y, int& m, int& d);
|
||||
|
||||
/**
|
||||
* Given a date as a string in the format yyyy, yyyy-mm, or yyyy-mm-dd,
|
||||
* return the numeric values for the year, month, and day. No checking is
|
||||
* done on these values. The string "now" is interpreted as the present
|
||||
* date (in UTC).
|
||||
*
|
||||
* @param[in] s the date in string format.
|
||||
* @param[out] y the year.
|
||||
* @param[out] m the month, Jan = 1, etc.
|
||||
* @param[out] d the day of the month.
|
||||
* @exception GeographicErr is \e s is malformed.
|
||||
**********************************************************************/
|
||||
static void date(const std::string& s, int& y, int& m, int& d);
|
||||
|
||||
/**
|
||||
* Given the date, return the day of the week.
|
||||
*
|
||||
* @param[in] y the year (must be positive).
|
||||
* @param[in] m the month, Jan = 1, etc. (must be positive).
|
||||
* @param[in] d the day of the month (must be positive).
|
||||
* @return the day of the week with Sunday, Monday--Saturday = 0,
|
||||
* 1--6.
|
||||
**********************************************************************/
|
||||
static int dow(int y, int m, int d) { return dow(day(y, m, d)); }
|
||||
|
||||
/**
|
||||
* Given the sequential day, return the day of the week.
|
||||
*
|
||||
* @param[in] s the sequential day (must be positive).
|
||||
* @return the day of the week with Sunday, Monday--Saturday = 0,
|
||||
* 1--6.
|
||||
**********************************************************************/
|
||||
static int dow(int s) {
|
||||
return (s + 5) % 7; // The 5 offset makes day 1 (0001-01-01) a Saturday.
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string representing a date to a fractional year.
|
||||
*
|
||||
* @tparam T the type of the argument.
|
||||
* @param[in] s the string to be converted.
|
||||
* @exception GeographicErr if \e s can't be interpreted as a date.
|
||||
* @return the fractional year.
|
||||
*
|
||||
* The string is first read as an ordinary number (e.g., 2010 or 2012.5);
|
||||
* if this is successful, the value is returned. Otherwise the string
|
||||
* should be of the form yyyy-mm or yyyy-mm-dd and this is converted to a
|
||||
* number with 2010-01-01 giving 2010.0 and 2012-07-03 giving 2012.5. The
|
||||
* string "now" is interpreted as the present date.
|
||||
**********************************************************************/
|
||||
template<typename T> static T fractionalyear(const std::string& s) {
|
||||
try {
|
||||
return val<T>(s);
|
||||
}
|
||||
catch (const std::exception&) {}
|
||||
int y, m, d;
|
||||
date(s, y, m, d);
|
||||
int t = day(y, m, d, true);
|
||||
return T(y) + T(t - day(y)) / T(day(y + 1) - day(y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a object of type T to a string.
|
||||
*
|
||||
* @tparam T the type of the argument.
|
||||
* @param[in] x the value to be converted.
|
||||
* @param[in] p the precision used (default −1).
|
||||
* @exception std::bad_alloc if memory for the string can't be allocated.
|
||||
* @return the string representation.
|
||||
*
|
||||
* If \e p ≥ 0, then the number fixed format is used with \e p bits of
|
||||
* precision. With \e p < 0, there is no manipulation of the format,
|
||||
* except that <code>boolalpha</code> is used to represent bools as "true"
|
||||
* and "false". There is an overload of this function if T is Math::real;
|
||||
* this deals with inf and nan.
|
||||
**********************************************************************/
|
||||
template<typename T> static std::string str(T x, int p = -1) {
|
||||
std::ostringstream s;
|
||||
if (p >= 0) s << std::fixed << std::setprecision(p);
|
||||
s << std::boolalpha << x; return s.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim the white space from the beginning and end of a string.
|
||||
*
|
||||
* @param[in] s the string to be trimmed
|
||||
* @return the trimmed string
|
||||
**********************************************************************/
|
||||
static std::string trim(const std::string& s);
|
||||
|
||||
/**
|
||||
* Lookup up a character in a string.
|
||||
*
|
||||
* @param[in] s the string to be searched.
|
||||
* @param[in] c the character to look for.
|
||||
* @return the index of the first occurrence character in the string or
|
||||
* −1 is the character is not present.
|
||||
*
|
||||
* \e c is converted to upper case before search \e s. Therefore, it is
|
||||
* intended that \e s should not contain any lower case letters.
|
||||
**********************************************************************/
|
||||
static int lookup(const std::string& s, char c);
|
||||
|
||||
/**
|
||||
* Lookup up a character in a char*.
|
||||
*
|
||||
* @param[in] s the char* string to be searched.
|
||||
* @param[in] c the character to look for.
|
||||
* @return the index of the first occurrence character in the string or
|
||||
* −1 is the character is not present.
|
||||
*
|
||||
* \e c is converted to upper case before search \e s. Therefore, it is
|
||||
* intended that \e s should not contain any lower case letters.
|
||||
**********************************************************************/
|
||||
static int lookup(const char* s, char c);
|
||||
|
||||
/**
|
||||
* Convert a string to type T.
|
||||
*
|
||||
* @tparam T the type of the return value.
|
||||
* @param[in] s the string to be converted.
|
||||
* @exception GeographicErr is \e s is not readable as a T.
|
||||
* @return object of type T.
|
||||
*
|
||||
* White space at the beginning and end of \e s is ignored.
|
||||
*
|
||||
* Special handling is provided for some types.
|
||||
*
|
||||
* If T is a floating point type, then inf and nan are recognized.
|
||||
*
|
||||
* If T is bool, then \e s should either be string a representing 0 (false)
|
||||
* or 1 (true) or one of the strings
|
||||
* - "false", "f", "nil", "no", "n", "off", or "" meaning false,
|
||||
* - "true", "t", "yes", "y", or "on" meaning true;
|
||||
* .
|
||||
* case is ignored.
|
||||
*
|
||||
* If T is std::string, then \e s is returned (with the white space at the
|
||||
* beginning and end removed).
|
||||
**********************************************************************/
|
||||
template<typename T> static T val(const std::string& s) {
|
||||
// If T is bool, then the specialization val<bool>() defined below is
|
||||
// used.
|
||||
T x;
|
||||
std::string errmsg, t(trim(s));
|
||||
do { // Executed once (provides the ability to break)
|
||||
std::istringstream is(t);
|
||||
if (!(is >> x)) {
|
||||
errmsg = "Cannot decode " + t;
|
||||
break;
|
||||
}
|
||||
int pos = int(is.tellg()); // Returns -1 at end of string?
|
||||
if (!(pos < 0 || pos == int(t.size()))) {
|
||||
errmsg = "Extra text " + t.substr(pos) + " at end of " + t;
|
||||
break;
|
||||
}
|
||||
return x;
|
||||
} while (false);
|
||||
x = std::numeric_limits<T>::is_integer ? 0 : nummatch<T>(t);
|
||||
if (x == 0)
|
||||
throw GeographicErr(errmsg);
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match "nan" and "inf" (and variants thereof) in a string.
|
||||
*
|
||||
* @tparam T the type of the return value (this should be a floating point
|
||||
* type).
|
||||
* @param[in] s the string to be matched.
|
||||
* @return appropriate special value (±∞, nan) or 0 if none is
|
||||
* found.
|
||||
*
|
||||
* White space is not allowed at the beginning or end of \e s.
|
||||
**********************************************************************/
|
||||
template<typename T> static T nummatch(const std::string& s) {
|
||||
if (s.length() < 3)
|
||||
return 0;
|
||||
std::string t(s);
|
||||
for (std::string::iterator p = t.begin(); p != t.end(); ++p)
|
||||
*p = char(std::toupper(*p));
|
||||
for (size_t i = s.length(); i--;)
|
||||
t[i] = char(std::toupper(s[i]));
|
||||
int sign = t[0] == '-' ? -1 : 1;
|
||||
std::string::size_type p0 = t[0] == '-' || t[0] == '+' ? 1 : 0;
|
||||
std::string::size_type p1 = t.find_last_not_of('0');
|
||||
if (p1 == std::string::npos || p1 + 1 < p0 + 3)
|
||||
return 0;
|
||||
// Strip off sign and trailing 0s
|
||||
t = t.substr(p0, p1 + 1 - p0); // Length at least 3
|
||||
if (t == "NAN" || t == "1.#QNAN" || t == "1.#SNAN" || t == "1.#IND" ||
|
||||
t == "1.#R")
|
||||
return Math::NaN<T>();
|
||||
else if (t == "INF" || t == "1.#INF" || t == "INFINITY")
|
||||
return sign * Math::infinity<T>();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a simple fraction, e.g., 3/4, from a string to an object of type T.
|
||||
*
|
||||
* @tparam T the type of the return value.
|
||||
* @param[in] s the string to be converted.
|
||||
* @exception GeographicErr is \e s is not readable as a fraction of type
|
||||
* T.
|
||||
* @return object of type T
|
||||
*
|
||||
* \note The msys shell under Windows converts arguments which look like
|
||||
* pathnames into their Windows equivalents. As a result the argument
|
||||
* "-1/300" gets mangled into something unrecognizable. A workaround is to
|
||||
* use a floating point number in the numerator, i.e., "-1.0/300". (Recent
|
||||
* versions of the msys shell appear \e not to have this problem.)
|
||||
**********************************************************************/
|
||||
template<typename T> static T fract(const std::string& s) {
|
||||
std::string::size_type delim = s.find('/');
|
||||
return
|
||||
!(delim != std::string::npos && delim >= 1 && delim + 2 <= s.size()) ?
|
||||
val<T>(s) :
|
||||
// delim in [1, size() - 2]
|
||||
val<T>(s.substr(0, delim)) / val<T>(s.substr(delim + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data of type ExtT from a binary stream to an array of type IntT.
|
||||
* The data in the file is in (bigendp ? big : little)-endian format.
|
||||
*
|
||||
* @tparam ExtT the type of the objects in the binary stream (external).
|
||||
* @tparam IntT the type of the objects in the array (internal).
|
||||
* @tparam bigendp true if the external storage format is big-endian.
|
||||
* @param[in] str the input stream containing the data of type ExtT
|
||||
* (external).
|
||||
* @param[out] array the output array of type IntT (internal).
|
||||
* @param[in] num the size of the array.
|
||||
* @exception GeographicErr if the data cannot be read.
|
||||
*
|
||||
* This routine is used to read binary data files for the Geoid,
|
||||
* GravityModel, and MagneticModel classes. In the case of GravityModel
|
||||
* and MagneticMode, the data is published by a government agency as text
|
||||
* files, and the coefficient to realize the models are converted to a
|
||||
* double precision binary format to minimize storage and to simplify
|
||||
* reading the data.
|
||||
*
|
||||
* For GEOGRAPHIC_PRECISION == 2, the data is read faithfully. For
|
||||
* GEOGRAPHICLIB_PRECISION > 2, external data of type double is interpreted
|
||||
* as an approximation of an exact decimal value; this exact number is
|
||||
* convered to a real number at the higher precision.
|
||||
**********************************************************************/
|
||||
template<typename ExtT, typename IntT, bool bigendp>
|
||||
static void readarray(std::istream& str, IntT array[], size_t num) {
|
||||
#if GEOGRAPHICLIB_PRECISION < 4
|
||||
if constexpr (sizeof(IntT) == sizeof(ExtT) &&
|
||||
std::numeric_limits<IntT>::is_integer ==
|
||||
std::numeric_limits<ExtT>::is_integer)
|
||||
{
|
||||
// Data is compatible (aside from the issue of endian-ness).
|
||||
str.read(reinterpret_cast<char*>(array), num * sizeof(ExtT));
|
||||
if (!str.good())
|
||||
throw GeographicErr("Failure reading data");
|
||||
if constexpr (bigendp != Math::bigendian) {
|
||||
// endian mismatch -> swap bytes
|
||||
for (size_t i = num; i--;)
|
||||
array[i] = Math::swab<IntT>(array[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
const int bufsize = 1024; // read this many values at a time
|
||||
ExtT buffer[bufsize]; // temporary buffer
|
||||
int k = int(num); // data values left to read
|
||||
int i = 0; // index into output array
|
||||
while (k) {
|
||||
int n = (std::min)(k, bufsize);
|
||||
str.read(reinterpret_cast<char*>(buffer), n * sizeof(ExtT));
|
||||
if (!str.good())
|
||||
throw GeographicErr("Failure reading data");
|
||||
for (int j = 0; j < n; ++j) {
|
||||
// fix endian-ness
|
||||
ExtT x = bigendp == Math::bigendian ? buffer[j] :
|
||||
Math::swab<ExtT>(buffer[j]);
|
||||
#if GEOGRAPHICLIB_PRECISION > 2
|
||||
// typeid doesn't allow if constexpr here
|
||||
if (typeid(ExtT) == typeid(double) &&
|
||||
typeid(IntT) == typeid(Math::real)) {
|
||||
// readarray is used to read in coefficient data rapidly. Thus
|
||||
// 8.3n is stored in its IEEE double representation. This is
|
||||
// fine is the working precision is double. However, when
|
||||
// working at higher precision, how should be interpret the
|
||||
// constant 8.3 appearing in a published table? Possibilities
|
||||
// are
|
||||
//
|
||||
// (a) treat this as an exact decimal number 83/10;
|
||||
//
|
||||
// (b) treat this as the approximate decimal representation of
|
||||
// an exact double precision number 2336242306698445/2^48 =
|
||||
// 8.300000000000000710542735760100185871124267578125
|
||||
//
|
||||
// Here use (a) if the number of significant digits in the
|
||||
// number is 15 or less. Otherwise, we use (b).
|
||||
//
|
||||
// We implement this as follows. Any double which can be
|
||||
// represented as a decimal number with precision 14 = digis10
|
||||
// - 1 (= 15 sig figs) is treated as an approximation to that
|
||||
// decimal number. The high precision number is then obtained
|
||||
// by reading the decimal number at that precision. Otherwise
|
||||
// the double is treated as exact. The high precision number
|
||||
// is obtained by adding zeros in the binary fraction.
|
||||
//
|
||||
// N.B. printing with precision 14 = digis10 - 1 allows short
|
||||
// numbers to be represended with trailing zeros. This isn't
|
||||
// necessarily the case with precision = digits10, e.g., 8.3
|
||||
// becomes 8.300000000000001.
|
||||
//
|
||||
// This prescription doesn't exactly implement the method
|
||||
// proposed. If the published table of numbers includes
|
||||
// 8.300000000000001, this will be interpreted as 8.3. This
|
||||
// doesn't apply to any published magnetic or gravity data.
|
||||
// E.g., the coefficients for EGM96, resp. EGM2008, are given
|
||||
// with precision 11, resp. 14.
|
||||
//
|
||||
// This conversion of doubles to Math::real comes at a
|
||||
// substantial cost. It adds about 14 s to the time it takes
|
||||
// to read the egm2008 gravity model for quad and mpfr
|
||||
// precisions. This is acceptable, however, because high
|
||||
// precision is only used for benchmarking.
|
||||
std::ostringstream str;
|
||||
str << std::scientific
|
||||
<< std::setprecision(std::numeric_limits<ExtT>::digits10-1)
|
||||
<< x;
|
||||
// Code for GEOGRAPHILIB_PRECISION > 2 and types double/real
|
||||
if (val<ExtT>(str.str()) == x)
|
||||
array[i++] = val<IntT>(str.str());
|
||||
else
|
||||
array[i++] = IntT(x);
|
||||
} else {
|
||||
// Code for GEOGRAPHILIB_PRECISION > 2 but types not
|
||||
// double/real
|
||||
array[i++] = IntT(x);
|
||||
}
|
||||
#else
|
||||
// Code for GEOGRAPHILIB_PRECISION <= 2
|
||||
array[i++] = IntT(x);
|
||||
#endif
|
||||
}
|
||||
k -= n;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data of type ExtT from a binary stream to a vector array of type
|
||||
* IntT. The data in the file is in (bigendp ? big : little)-endian
|
||||
* format.
|
||||
*
|
||||
* @tparam ExtT the type of the objects in the binary stream (external).
|
||||
* @tparam IntT the type of the objects in the array (internal).
|
||||
* @tparam bigendp true if the external storage format is big-endian.
|
||||
* @param[in] str the input stream containing the data of type ExtT
|
||||
* (external).
|
||||
* @param[out] array the output vector of type IntT (internal).
|
||||
* @exception GeographicErr if the data cannot be read.
|
||||
**********************************************************************/
|
||||
template<typename ExtT, typename IntT, bool bigendp>
|
||||
static void readarray(std::istream& str, std::vector<IntT>& array) {
|
||||
if (array.size() > 0)
|
||||
readarray<ExtT, IntT, bigendp>(str, &array[0], array.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data in an array of type IntT as type ExtT to a binary stream.
|
||||
* The data in the file is in (bigendp ? big : little)-endian format.
|
||||
*
|
||||
* @tparam ExtT the type of the objects in the binary stream (external).
|
||||
* @tparam IntT the type of the objects in the array (internal).
|
||||
* @tparam bigendp true if the external storage format is big-endian.
|
||||
* @param[out] str the output stream for the data of type ExtT (external).
|
||||
* @param[in] array the input array of type IntT (internal).
|
||||
* @param[in] num the size of the array.
|
||||
* @exception GeographicErr if the data cannot be written.
|
||||
**********************************************************************/
|
||||
template<typename ExtT, typename IntT, bool bigendp>
|
||||
static void writearray(std::ostream& str, const IntT array[], size_t num)
|
||||
{
|
||||
#if GEOGRAPHICLIB_PRECISION < 4
|
||||
if constexpr (sizeof(IntT) == sizeof(ExtT) &&
|
||||
std::numeric_limits<IntT>::is_integer ==
|
||||
std::numeric_limits<ExtT>::is_integer &&
|
||||
bigendp == Math::bigendian)
|
||||
{
|
||||
// Data is compatible (including endian-ness).
|
||||
str.write(reinterpret_cast<const char*>(array), num * sizeof(ExtT));
|
||||
if (!str.good())
|
||||
throw GeographicErr("Failure writing data");
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
const int bufsize = 1024; // write this many values at a time
|
||||
ExtT buffer[bufsize]; // temporary buffer
|
||||
int k = int(num); // data values left to write
|
||||
int i = 0; // index into output array
|
||||
while (k) {
|
||||
int n = (std::min)(k, bufsize);
|
||||
for (int j = 0; j < n; ++j)
|
||||
// cast to ExtT and fix endian-ness
|
||||
buffer[j] = bigendp == Math::bigendian ? ExtT(array[i++]) :
|
||||
Math::swab<ExtT>(ExtT(array[i++]));
|
||||
str.write(reinterpret_cast<const char*>(buffer), n * sizeof(ExtT));
|
||||
if (!str.good())
|
||||
throw GeographicErr("Failure writing data");
|
||||
k -= n;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data in an array of type IntT as type ExtT to a binary stream.
|
||||
* The data in the file is in (bigendp ? big : little)-endian format.
|
||||
*
|
||||
* @tparam ExtT the type of the objects in the binary stream (external).
|
||||
* @tparam IntT the type of the objects in the array (internal).
|
||||
* @tparam bigendp true if the external storage format is big-endian.
|
||||
* @param[out] str the output stream for the data of type ExtT (external).
|
||||
* @param[in] array the input vector of type IntT (internal).
|
||||
* @exception GeographicErr if the data cannot be written.
|
||||
**********************************************************************/
|
||||
template<typename ExtT, typename IntT, bool bigendp>
|
||||
static void writearray(std::ostream& str, std::vector<IntT>& array) {
|
||||
if (array.size() > 0)
|
||||
writearray<ExtT, IntT, bigendp>(str, &array[0], array.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a KEY [=] VALUE line.
|
||||
*
|
||||
* @param[in] line the input line.
|
||||
* @param[out] key the KEY.
|
||||
* @param[out] value the VALUE.
|
||||
* @param[in] equals character representing "equals" to separate KEY and
|
||||
* VALUE, if NULL (the default) use first space character.
|
||||
* @param[in] comment character to use as the comment character; if
|
||||
* non-NULL, this character and everything after it is discarded; default
|
||||
* is '#'.
|
||||
* @exception std::bad_alloc if memory for the internal strings can't be
|
||||
* allocated.
|
||||
* @return whether a key was found.
|
||||
*
|
||||
* The \e comment character (default is '#') and everything after it are
|
||||
* discarded and the result trimmed of leading and trailing white space.
|
||||
* Use the \e equals delimiter character (or, if it is NULL -- the default,
|
||||
* the first white space) to separate \e key and \e value. \e key and \e
|
||||
* value are trimmed of leading and trailing white space. If \e key is
|
||||
* empty, then \e value is set to "" and false is returned.
|
||||
**********************************************************************/
|
||||
static bool ParseLine(const std::string& line,
|
||||
std::string& key, std::string& value,
|
||||
char equals = '\0', char comment = '#');
|
||||
|
||||
/**
|
||||
* Set the binary precision of a real number.
|
||||
*
|
||||
* @param[in] ndigits the number of bits of precision. If ndigits is 0
|
||||
* (the default), then determine the precision from the environment
|
||||
* variable GEOGRAPHICLIB_DIGITS. If this is undefined, use ndigits =
|
||||
* 256 (i.e., about 77 decimal digits).
|
||||
* @return the resulting number of bits of precision.
|
||||
*
|
||||
* This only has an effect when GEOGRAPHICLIB_PRECISION >= 5. The
|
||||
* precision should only be set once and before calls to any other
|
||||
* GeographicLib functions. (Several functions, for example Math::pi(),
|
||||
* cache the return value in a static local variable. The precision needs
|
||||
* to be set before a call to any such functions.) In multi-threaded
|
||||
* applications, it is necessary also to set the precision in each thread
|
||||
* (see the example GeoidToGTX.cpp). If GEOGRAPHICLIB_PRECISION > 5, then
|
||||
* the precision is set to GEOGRAPHICLIB_PRECISION, the compile-time value,
|
||||
* and \e ndigits is ignored.
|
||||
*
|
||||
* \note Use Math::digits() to return the current precision in bits.
|
||||
**********************************************************************/
|
||||
static int set_digits(int ndigits = 0);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The specialization of Utility::val<T>() for strings.
|
||||
*
|
||||
* @param[in] s the string to be converted.
|
||||
* @exception GeographicErr is \e s is not readable as a T.
|
||||
* @return the string trimmed of its whitespace.
|
||||
**********************************************************************/
|
||||
template<> inline std::string Utility::val<std::string>(const std::string& s)
|
||||
{ return trim(s); }
|
||||
|
||||
/**
|
||||
* The specialization of Utility::val<T>() for bools.
|
||||
*
|
||||
* @param[in] s the string to be converted.
|
||||
* @exception GeographicErr is \e s is not readable as a T.
|
||||
* @return boolean value.
|
||||
*
|
||||
* \e s should either be string a representing 0 (false)
|
||||
* or 1 (true) or one of the strings
|
||||
* - "false", "f", "nil", "no", "n", "off", or "" meaning false,
|
||||
* - "true", "t", "yes", "y", or "on" meaning true;
|
||||
* .
|
||||
* case is ignored.
|
||||
**********************************************************************/
|
||||
template<> inline bool Utility::val<bool>(const std::string& s) {
|
||||
std::string t(trim(s));
|
||||
if (t.empty()) return false;
|
||||
bool x;
|
||||
{
|
||||
std::istringstream is(t);
|
||||
if (is >> x) {
|
||||
int pos = int(is.tellg()); // Returns -1 at end of string?
|
||||
if (!(pos < 0 || pos == int(t.size())))
|
||||
throw GeographicErr("Extra text " + t.substr(pos) +
|
||||
" at end of " + t);
|
||||
return x;
|
||||
}
|
||||
}
|
||||
for (std::string::iterator p = t.begin(); p != t.end(); ++p)
|
||||
*p = char(std::tolower(*p));
|
||||
switch (t[0]) { // already checked that t isn't empty
|
||||
case 'f':
|
||||
if (t == "f" || t == "false") return false;
|
||||
break;
|
||||
case 'n':
|
||||
if (t == "n" || t == "nil" || t == "no") return false;
|
||||
break;
|
||||
case 'o':
|
||||
if (t == "off") return false;
|
||||
else if (t == "on") return true;
|
||||
break;
|
||||
case 't':
|
||||
if (t == "t" || t == "true") return true;
|
||||
break;
|
||||
case 'y':
|
||||
if (t == "y" || t == "yes") return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
throw GeographicErr("Cannot decode " + t + " as a bool");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Math::real object to a string.
|
||||
*
|
||||
* @param[in] x the value to be converted.
|
||||
* @param[in] p the precision used (default −1).
|
||||
* @exception std::bad_alloc if memory for the string can't be allocated.
|
||||
* @return the string representation.
|
||||
*
|
||||
* If \e p ≥ 0, then the number fixed format is used with p bits of
|
||||
* precision. With p < 0, there is no manipulation of the format. This is
|
||||
* an overload of str<T> which deals with inf and nan.
|
||||
**********************************************************************/
|
||||
template<> inline std::string Utility::str<Math::real>(Math::real x, int p) {
|
||||
using std::isfinite;
|
||||
if (!isfinite(x))
|
||||
return x < 0 ? std::string("-inf") :
|
||||
(x > 0 ? std::string("inf") : std::string("nan"));
|
||||
std::ostringstream s;
|
||||
if (p >= 0) s << std::fixed << std::setprecision(p);
|
||||
s << x; return s.str();
|
||||
}
|
||||
|
||||
} // namespace GeographicLib
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#endif // GEOGRAPHICLIB_UTILITY_HPP
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
# Version checking for GeographicLib
|
||||
|
||||
set (PACKAGE_VERSION "2.7")
|
||||
set (PACKAGE_VERSION_MAJOR "2")
|
||||
set (PACKAGE_VERSION_MINOR "7")
|
||||
set (PACKAGE_VERSION_PATCH "0")
|
||||
|
||||
# These variable definitions parallel those in GeographicLib's
|
||||
# cmake/CMakeLists.txt.
|
||||
if (MSVC)
|
||||
# For checking the compatibility of MSVC_TOOLSET_VERSION; see
|
||||
# https://docs.microsoft.com/en-us/cpp/porting/overview-of-potential-upgrade-issues-visual-cpp
|
||||
# Assume major version number is obtained by dropping the last decimal
|
||||
# digit.
|
||||
math (EXPR MSVC_TOOLSET_MAJOR "${MSVC_TOOLSET_VERSION}/10")
|
||||
endif ()
|
||||
|
||||
if (NOT PACKAGE_FIND_NAME STREQUAL "GeographicLib")
|
||||
# Check package name (in particular, because of the way cmake finds
|
||||
# package config files, the capitalization could easily be "wrong").
|
||||
# This is necessary to ensure that the automatically generated
|
||||
# variables, e.g., <package>_FOUND, are consistently spelled.
|
||||
set (REASON "package = GeographicLib, NOT ${PACKAGE_FIND_NAME}")
|
||||
set (PACKAGE_VERSION_UNSUITABLE TRUE)
|
||||
elseif (NOT (APPLE OR (NOT DEFINED CMAKE_SIZEOF_VOID_P) OR
|
||||
CMAKE_SIZEOF_VOID_P EQUAL 8))
|
||||
# Reject if there's a 32-bit/64-bit mismatch (not necessary with Apple
|
||||
# since a multi-architecture library is built for that platform).
|
||||
set (REASON "sizeof(*void) = 8")
|
||||
set (PACKAGE_VERSION_UNSUITABLE TRUE)
|
||||
elseif (MSVC AND NOT (
|
||||
# toolset version must be at least as great as GeographicLib's.
|
||||
MSVC_TOOLSET_VERSION GREATER_EQUAL 0
|
||||
# and major versions must match
|
||||
AND MSVC_TOOLSET_MAJOR EQUAL 0 ))
|
||||
# Reject if there's a mismatch in MSVC compiler versions.
|
||||
set (REASON "MSVC_TOOLSET_VERSION = 0")
|
||||
set (PACKAGE_VERSION_UNSUITABLE TRUE)
|
||||
elseif (GEOGRAPHICLIB_PRECISION MATCHES "^[1-9][0-9]*\$" AND NOT (
|
||||
GEOGRAPHICLIB_PRECISION EQUAL 2 ))
|
||||
# Reject if the user asks for an incompatible precsision.
|
||||
set (REASON "GEOGRAPHICLIB_PRECISION = 2")
|
||||
set (PACKAGE_VERSION_UNSUITABLE TRUE)
|
||||
elseif (CMAKE_CROSSCOMPILING AND
|
||||
NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND
|
||||
CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64"))
|
||||
# Reject if cross-compiling and there's a mismatch in the target system.
|
||||
set (REASON "target = Linux-x86_64")
|
||||
set (PACKAGE_VERSION_UNSUITABLE TRUE)
|
||||
elseif (PACKAGE_FIND_VERSION)
|
||||
if (PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION)
|
||||
set (PACKAGE_VERSION_EXACT TRUE)
|
||||
elseif (PACKAGE_FIND_VERSION VERSION_LESS PACKAGE_VERSION
|
||||
AND PACKAGE_FIND_VERSION_MAJOR EQUAL PACKAGE_VERSION_MAJOR)
|
||||
set (PACKAGE_VERSION_COMPATIBLE TRUE)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
set (GeographicLib_SHARED_FOUND ON)
|
||||
set (GeographicLib_STATIC_FOUND OFF)
|
||||
|
||||
# Check for the components requested. The convention is that
|
||||
# GeographicLib_${comp}_FOUND should be true for all the required
|
||||
# components.
|
||||
if (GeographicLib_FIND_COMPONENTS)
|
||||
foreach (comp ${GeographicLib_FIND_COMPONENTS})
|
||||
if (GeographicLib_FIND_REQUIRED_${comp} AND
|
||||
NOT GeographicLib_${comp}_FOUND)
|
||||
set (REASON "without ${comp}")
|
||||
set (PACKAGE_VERSION_UNSUITABLE TRUE)
|
||||
endif ()
|
||||
endforeach ()
|
||||
endif ()
|
||||
|
||||
# If unsuitable, append the reason to the package version so that it's
|
||||
# visible to the user.
|
||||
if (PACKAGE_VERSION_UNSUITABLE)
|
||||
set (PACKAGE_VERSION "${PACKAGE_VERSION} (${REASON})")
|
||||
endif ()
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
# Configure GeographicLib
|
||||
#
|
||||
# Set
|
||||
# GeographicLib_FOUND = TRUE
|
||||
# GeographicLib_INCLUDE_DIRS = /usr/local/include
|
||||
# GeographicLib_SHARED_LIBRARIES = GeographicLib_SHARED (or empty)
|
||||
# GeographicLib_STATIC_LIBRARIES = GeographicLib_STATIC (or empty)
|
||||
# GeographicLib_LIBRARY_DIRS = /usr/local/lib
|
||||
# GeographicLib_BINARY_DIRS = /usr/local/bin
|
||||
# GeographicLib_VERSION = 1.34 (for example)
|
||||
# GEOGRAPHICLIB_DATA = /usr/local/share/GeographicLib (for example)
|
||||
# Depending on GeographicLib_USE_STATIC_LIBS
|
||||
# GeographicLib_LIBRARIES = ${GeographicLib_SHARED_LIBRARIES}, if OFF
|
||||
# GeographicLib_LIBRARIES = ${GeographicLib_STATIC_LIBRARIES}, if ON
|
||||
# If only one of the libraries is provided, then
|
||||
# GeographicLib_USE_STATIC_LIBS is ignored.
|
||||
#
|
||||
# Since cmake 2.8.11 or later, there's no need to include
|
||||
# include_directories (${GeographicLib_INCLUDE_DIRS})
|
||||
# The variables are retained for information.
|
||||
#
|
||||
# The following variables are only relevant if the library has been
|
||||
# compiled with a default precision different from double:
|
||||
# GEOGRAPHICLIB_PRECISION = the precision of the library (usually 2)
|
||||
# GeographicLib_HIGHPREC_LIBRARIES = the libraries need for high precision
|
||||
|
||||
message (STATUS "Reading ${CMAKE_CURRENT_LIST_FILE}")
|
||||
# GeographicLib_VERSION is set by version file
|
||||
message (STATUS
|
||||
"GeographicLib configuration, version ${GeographicLib_VERSION}")
|
||||
|
||||
# Tell the user project where to find our headers and libraries
|
||||
get_filename_component (_DIR ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||
if (IS_ABSOLUTE "../../..")
|
||||
# This is an uninstalled package (still in the build tree)
|
||||
set (_ROOT "../../..")
|
||||
set (GeographicLib_INCLUDE_DIRS "")
|
||||
set (GeographicLib_LIBRARY_DIRS "${_ROOT}/src")
|
||||
set (GeographicLib_BINARY_DIRS "${_ROOT}/tools")
|
||||
else ()
|
||||
# This is an installed package; figure out the paths relative to the
|
||||
# current directory.
|
||||
get_filename_component (_ROOT "${_DIR}/../../.." ABSOLUTE)
|
||||
set (GeographicLib_INCLUDE_DIRS "${_ROOT}/include")
|
||||
set (GeographicLib_LIBRARY_DIRS "${_ROOT}/lib")
|
||||
set (GeographicLib_BINARY_DIRS "${_ROOT}/bin")
|
||||
endif ()
|
||||
set (GEOGRAPHICLIB_DATA "/usr/local/share/GeographicLib")
|
||||
set (GEOGRAPHICLIB_PRECISION 2)
|
||||
set (GeographicLib_HIGHPREC_LIBRARIES "")
|
||||
|
||||
set (GeographicLib_SHARED_LIBRARIES GeographicLib::GeographicLib_SHARED)
|
||||
set (GeographicLib_STATIC_LIBRARIES )
|
||||
# Read in the exported definition of the library
|
||||
include ("${_DIR}/geographiclib-targets.cmake")
|
||||
|
||||
# For interoperability with older installations of GeographicLib and
|
||||
# with packages which depend on GeographicLib, GeographicLib_LIBRARIES
|
||||
# etc. still point to the non-namespace variables. Tentatively plan to
|
||||
# transition to namespace variables as follows:
|
||||
#
|
||||
# * namespace targets were introduced with version 1.47 (2017-02-15)
|
||||
# * switch GeographicLib_LIBRARIES to point to namespace variable after
|
||||
# 2020-02
|
||||
# * remove non-namespace variables after 2023-02
|
||||
|
||||
unset (_ROOT)
|
||||
unset (_DIR)
|
||||
|
||||
if ((NOT GeographicLib_SHARED_LIBRARIES) OR
|
||||
(GeographicLib_USE_STATIC_LIBS AND GeographicLib_STATIC_LIBRARIES))
|
||||
set (GeographicLib_LIBRARIES ${GeographicLib_STATIC_LIBRARIES})
|
||||
message (STATUS " \${GeographicLib_LIBRARIES} set to static library")
|
||||
else ()
|
||||
set (GeographicLib_LIBRARIES ${GeographicLib_SHARED_LIBRARIES})
|
||||
message (STATUS " \${GeographicLib_LIBRARIES} set to shared library")
|
||||
endif ()
|
||||
|
||||
# Check for the components requested. This only supports components
|
||||
# STATIC and SHARED by checking the value of
|
||||
# GeographicLib_${comp}_LIBRARIES. No need to check if the component
|
||||
# is required or not--the version file took care of that.
|
||||
# GeographicLib_${comp}_FOUND is set appropriately for each component.
|
||||
if (GeographicLib_FIND_COMPONENTS)
|
||||
foreach (comp ${GeographicLib_FIND_COMPONENTS})
|
||||
if (GeographicLib_${comp}_LIBRARIES)
|
||||
set (GeographicLib_${comp}_FOUND TRUE)
|
||||
message (STATUS "GeographicLib component ${comp} found")
|
||||
else ()
|
||||
set (GeographicLib_${comp}_FOUND FALSE)
|
||||
message (WARNING "GeographicLib component ${comp} not found")
|
||||
endif ()
|
||||
endforeach ()
|
||||
endif ()
|
||||
|
||||
# GeographicLib_FOUND is set to TRUE automatically
|
||||
set (GEOGRAPHICLIB_FOUND TRUE) # for backwards compatibility, deprecated
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
#----------------------------------------------------------------
|
||||
# Generated CMake target import file for configuration "Release".
|
||||
#----------------------------------------------------------------
|
||||
|
||||
# Commands may need to know the format version.
|
||||
set(CMAKE_IMPORT_FILE_VERSION 1)
|
||||
|
||||
# Import target "GeographicLib::GeographicLib_SHARED" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::GeographicLib_SHARED APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::GeographicLib_SHARED PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libGeographicLib.so.26.2.1"
|
||||
IMPORTED_SONAME_RELEASE "libGeographicLib.so.26"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::GeographicLib_SHARED )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::GeographicLib_SHARED "${_IMPORT_PREFIX}/lib/libGeographicLib.so.26.2.1" )
|
||||
|
||||
# Import target "GeographicLib::CartConvert" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::CartConvert APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::CartConvert PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/CartConvert"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::CartConvert )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::CartConvert "${_IMPORT_PREFIX}/bin/CartConvert" )
|
||||
|
||||
# Import target "GeographicLib::ConicProj" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::ConicProj APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::ConicProj PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/ConicProj"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::ConicProj )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::ConicProj "${_IMPORT_PREFIX}/bin/ConicProj" )
|
||||
|
||||
# Import target "GeographicLib::GeodesicProj" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::GeodesicProj APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::GeodesicProj PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/GeodesicProj"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::GeodesicProj )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::GeodesicProj "${_IMPORT_PREFIX}/bin/GeodesicProj" )
|
||||
|
||||
# Import target "GeographicLib::GeoConvert" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::GeoConvert APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::GeoConvert PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/GeoConvert"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::GeoConvert )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::GeoConvert "${_IMPORT_PREFIX}/bin/GeoConvert" )
|
||||
|
||||
# Import target "GeographicLib::GeodSolve" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::GeodSolve APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::GeodSolve PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/GeodSolve"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::GeodSolve )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::GeodSolve "${_IMPORT_PREFIX}/bin/GeodSolve" )
|
||||
|
||||
# Import target "GeographicLib::GeoidEval" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::GeoidEval APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::GeoidEval PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/GeoidEval"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::GeoidEval )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::GeoidEval "${_IMPORT_PREFIX}/bin/GeoidEval" )
|
||||
|
||||
# Import target "GeographicLib::Gravity" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::Gravity APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::Gravity PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/Gravity"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::Gravity )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::Gravity "${_IMPORT_PREFIX}/bin/Gravity" )
|
||||
|
||||
# Import target "GeographicLib::IntersectTool" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::IntersectTool APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::IntersectTool PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/IntersectTool"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::IntersectTool )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::IntersectTool "${_IMPORT_PREFIX}/bin/IntersectTool" )
|
||||
|
||||
# Import target "GeographicLib::MagneticField" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::MagneticField APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::MagneticField PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/MagneticField"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::MagneticField )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::MagneticField "${_IMPORT_PREFIX}/bin/MagneticField" )
|
||||
|
||||
# Import target "GeographicLib::Planimeter" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::Planimeter APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::Planimeter PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/Planimeter"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::Planimeter )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::Planimeter "${_IMPORT_PREFIX}/bin/Planimeter" )
|
||||
|
||||
# Import target "GeographicLib::RhumbSolve" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::RhumbSolve APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::RhumbSolve PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/RhumbSolve"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::RhumbSolve )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::RhumbSolve "${_IMPORT_PREFIX}/bin/RhumbSolve" )
|
||||
|
||||
# Import target "GeographicLib::TransverseMercatorProj" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::TransverseMercatorProj APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::TransverseMercatorProj PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/TransverseMercatorProj"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::TransverseMercatorProj )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::TransverseMercatorProj "${_IMPORT_PREFIX}/bin/TransverseMercatorProj" )
|
||||
|
||||
# Import target "GeographicLib::Cart3Convert" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::Cart3Convert APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::Cart3Convert PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/Cart3Convert"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::Cart3Convert )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::Cart3Convert "${_IMPORT_PREFIX}/bin/Cart3Convert" )
|
||||
|
||||
# Import target "GeographicLib::Conformal3Proj" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::Conformal3Proj APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::Conformal3Proj PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/Conformal3Proj"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::Conformal3Proj )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::Conformal3Proj "${_IMPORT_PREFIX}/bin/Conformal3Proj" )
|
||||
|
||||
# Import target "GeographicLib::Geod3Solve" for configuration "Release"
|
||||
set_property(TARGET GeographicLib::Geod3Solve APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(GeographicLib::Geod3Solve PROPERTIES
|
||||
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/Geod3Solve"
|
||||
)
|
||||
|
||||
list(APPEND _cmake_import_check_targets GeographicLib::Geod3Solve )
|
||||
list(APPEND _cmake_import_check_files_for_GeographicLib::Geod3Solve "${_IMPORT_PREFIX}/bin/Geod3Solve" )
|
||||
|
||||
# Commands beyond this point should not need to know the version.
|
||||
set(CMAKE_IMPORT_FILE_VERSION)
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
# Generated by CMake
|
||||
|
||||
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.8)
|
||||
message(FATAL_ERROR "CMake >= 2.8.0 required")
|
||||
endif()
|
||||
if(CMAKE_VERSION VERSION_LESS "2.8.3")
|
||||
message(FATAL_ERROR "CMake >= 2.8.3 required")
|
||||
endif()
|
||||
cmake_policy(PUSH)
|
||||
cmake_policy(VERSION 2.8.3...3.26)
|
||||
#----------------------------------------------------------------
|
||||
# Generated CMake target import file.
|
||||
#----------------------------------------------------------------
|
||||
|
||||
# Commands may need to know the format version.
|
||||
set(CMAKE_IMPORT_FILE_VERSION 1)
|
||||
|
||||
# Protect against multiple inclusion, which would fail when already imported targets are added once more.
|
||||
set(_cmake_targets_defined "")
|
||||
set(_cmake_targets_not_defined "")
|
||||
set(_cmake_expected_targets "")
|
||||
foreach(_cmake_expected_target IN ITEMS GeographicLib::GeographicLib_SHARED GeographicLib::GeographicLib GeographicLib::CartConvert GeographicLib::ConicProj GeographicLib::GeodesicProj GeographicLib::GeoConvert GeographicLib::GeodSolve GeographicLib::GeoidEval GeographicLib::Gravity GeographicLib::IntersectTool GeographicLib::MagneticField GeographicLib::Planimeter GeographicLib::RhumbSolve GeographicLib::TransverseMercatorProj GeographicLib::Cart3Convert GeographicLib::Conformal3Proj GeographicLib::Geod3Solve)
|
||||
list(APPEND _cmake_expected_targets "${_cmake_expected_target}")
|
||||
if(TARGET "${_cmake_expected_target}")
|
||||
list(APPEND _cmake_targets_defined "${_cmake_expected_target}")
|
||||
else()
|
||||
list(APPEND _cmake_targets_not_defined "${_cmake_expected_target}")
|
||||
endif()
|
||||
endforeach()
|
||||
unset(_cmake_expected_target)
|
||||
if(_cmake_targets_defined STREQUAL _cmake_expected_targets)
|
||||
unset(_cmake_targets_defined)
|
||||
unset(_cmake_targets_not_defined)
|
||||
unset(_cmake_expected_targets)
|
||||
unset(CMAKE_IMPORT_FILE_VERSION)
|
||||
cmake_policy(POP)
|
||||
return()
|
||||
endif()
|
||||
if(NOT _cmake_targets_defined STREQUAL "")
|
||||
string(REPLACE ";" ", " _cmake_targets_defined_text "${_cmake_targets_defined}")
|
||||
string(REPLACE ";" ", " _cmake_targets_not_defined_text "${_cmake_targets_not_defined}")
|
||||
message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_cmake_targets_defined_text}\nTargets not yet defined: ${_cmake_targets_not_defined_text}\n")
|
||||
endif()
|
||||
unset(_cmake_targets_defined)
|
||||
unset(_cmake_targets_not_defined)
|
||||
unset(_cmake_expected_targets)
|
||||
|
||||
|
||||
# Compute the installation prefix relative to this file.
|
||||
get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
|
||||
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
|
||||
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
|
||||
if(_IMPORT_PREFIX STREQUAL "/")
|
||||
set(_IMPORT_PREFIX "")
|
||||
endif()
|
||||
|
||||
# Create imported target GeographicLib::GeographicLib_SHARED
|
||||
add_library(GeographicLib::GeographicLib_SHARED SHARED IMPORTED)
|
||||
|
||||
set_target_properties(GeographicLib::GeographicLib_SHARED PROPERTIES
|
||||
INTERFACE_COMPILE_FEATURES "cxx_std_17"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
|
||||
)
|
||||
|
||||
# Create imported target GeographicLib::GeographicLib
|
||||
add_library(GeographicLib::GeographicLib INTERFACE IMPORTED)
|
||||
|
||||
set_target_properties(GeographicLib::GeographicLib PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES "GeographicLib::GeographicLib_SHARED"
|
||||
)
|
||||
|
||||
# Create imported target GeographicLib::CartConvert
|
||||
add_executable(GeographicLib::CartConvert IMPORTED)
|
||||
|
||||
# Create imported target GeographicLib::ConicProj
|
||||
add_executable(GeographicLib::ConicProj IMPORTED)
|
||||
|
||||
# Create imported target GeographicLib::GeodesicProj
|
||||
add_executable(GeographicLib::GeodesicProj IMPORTED)
|
||||
|
||||
# Create imported target GeographicLib::GeoConvert
|
||||
add_executable(GeographicLib::GeoConvert IMPORTED)
|
||||
|
||||
# Create imported target GeographicLib::GeodSolve
|
||||
add_executable(GeographicLib::GeodSolve IMPORTED)
|
||||
|
||||
# Create imported target GeographicLib::GeoidEval
|
||||
add_executable(GeographicLib::GeoidEval IMPORTED)
|
||||
|
||||
# Create imported target GeographicLib::Gravity
|
||||
add_executable(GeographicLib::Gravity IMPORTED)
|
||||
|
||||
# Create imported target GeographicLib::IntersectTool
|
||||
add_executable(GeographicLib::IntersectTool IMPORTED)
|
||||
|
||||
# Create imported target GeographicLib::MagneticField
|
||||
add_executable(GeographicLib::MagneticField IMPORTED)
|
||||
|
||||
# Create imported target GeographicLib::Planimeter
|
||||
add_executable(GeographicLib::Planimeter IMPORTED)
|
||||
|
||||
# Create imported target GeographicLib::RhumbSolve
|
||||
add_executable(GeographicLib::RhumbSolve IMPORTED)
|
||||
|
||||
# Create imported target GeographicLib::TransverseMercatorProj
|
||||
add_executable(GeographicLib::TransverseMercatorProj IMPORTED)
|
||||
|
||||
# Create imported target GeographicLib::Cart3Convert
|
||||
add_executable(GeographicLib::Cart3Convert IMPORTED)
|
||||
|
||||
# Create imported target GeographicLib::Conformal3Proj
|
||||
add_executable(GeographicLib::Conformal3Proj IMPORTED)
|
||||
|
||||
# Create imported target GeographicLib::Geod3Solve
|
||||
add_executable(GeographicLib::Geod3Solve IMPORTED)
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS 3.0.0)
|
||||
message(FATAL_ERROR "This file relies on consumers using CMake 3.0.0 or greater.")
|
||||
endif()
|
||||
|
||||
# Load information for each installed configuration.
|
||||
file(GLOB _cmake_config_files "${CMAKE_CURRENT_LIST_DIR}/geographiclib-targets-*.cmake")
|
||||
foreach(_cmake_config_file IN LISTS _cmake_config_files)
|
||||
include("${_cmake_config_file}")
|
||||
endforeach()
|
||||
unset(_cmake_config_file)
|
||||
unset(_cmake_config_files)
|
||||
|
||||
# Cleanup temporary variables.
|
||||
set(_IMPORT_PREFIX)
|
||||
|
||||
# Loop over all imported files and verify that they actually exist
|
||||
foreach(_cmake_target IN LISTS _cmake_import_check_targets)
|
||||
foreach(_cmake_file IN LISTS "_cmake_import_check_files_for_${_cmake_target}")
|
||||
if(NOT EXISTS "${_cmake_file}")
|
||||
message(FATAL_ERROR "The imported target \"${_cmake_target}\" references the file
|
||||
\"${_cmake_file}\"
|
||||
but this file does not exist. Possible reasons include:
|
||||
* The file was deleted, renamed, or moved to another location.
|
||||
* An install or uninstall procedure did not complete successfully.
|
||||
* The installation package was faulty and contained
|
||||
\"${CMAKE_CURRENT_LIST_FILE}\"
|
||||
but not all the files it references.
|
||||
")
|
||||
endif()
|
||||
endforeach()
|
||||
unset(_cmake_file)
|
||||
unset("_cmake_import_check_files_for_${_cmake_target}")
|
||||
endforeach()
|
||||
unset(_cmake_target)
|
||||
unset(_cmake_import_check_targets)
|
||||
|
||||
# This file does not depend on other imported targets which have
|
||||
# been exported from the same project but in a separate export set.
|
||||
|
||||
# Commands beyond this point should not need to know the version.
|
||||
set(CMAKE_IMPORT_FILE_VERSION)
|
||||
cmake_policy(POP)
|
||||
@@ -0,0 +1 @@
|
||||
libGeographicLib.so.26
|
||||
+1
@@ -0,0 +1 @@
|
||||
libGeographicLib.so.26.2.1
|
||||
Binary file not shown.
@@ -0,0 +1,14 @@
|
||||
prefix=/repo/external/GeographicLib
|
||||
exec_prefix=${prefix}
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
bindir=${exec_prefix}/bin
|
||||
|
||||
Name: GeographicLib
|
||||
Description: A library for geographic projections
|
||||
Version: 2.7
|
||||
URL: https://geographiclib.sourceforge.io
|
||||
|
||||
Requires:
|
||||
Libs: -L${libdir} -lGeographicLib
|
||||
Cflags: -I${includedir}
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
#! /bin/sh
|
||||
#
|
||||
# Download geoid datasets for use by GeographicLib::Geoid. This is
|
||||
# modeled on a similar script geographiclib-datasets-download by
|
||||
# Francesco P. Lovergine <frankie@debian.org>
|
||||
#
|
||||
# Copyright (c) Charles Karney (2011-2022) <karney@alum.mit.edu> and
|
||||
# licensed under the MIT/X11 License. For more information, see
|
||||
# https://geographiclib.sourceforge.io/
|
||||
|
||||
DEFAULTDIR="/usr/local/share/GeographicLib"
|
||||
SUBDIR=geoids
|
||||
NAME=geoid
|
||||
MODEL=geoid
|
||||
CLASS=Geoid
|
||||
TOOL=GeoidEval
|
||||
EXT=pgm
|
||||
usage() {
|
||||
cat <<EOF
|
||||
usage: $0 [-p parentdir] [-f] [-d] [-h] $MODEL...
|
||||
|
||||
This program downloads and installs the datasets used by the
|
||||
GeographicLib::$CLASS class and the $TOOL tool to compute geoid
|
||||
heights. These datasets are NGA earth gravity models evaluated on a
|
||||
rectangular grid in latitude and longitude. $MODEL is one of more of the
|
||||
names from this table:
|
||||
|
||||
size (MB)
|
||||
name geoid grid tar.bz2 disk
|
||||
egm84-30 EGM84 30' 0.5 0.6
|
||||
egm84-15 EGM84 15' 1.5 2.1
|
||||
egm96-15 EGM96 15' 1.5 2.1
|
||||
egm96-5 EGM96 5' 11 19
|
||||
egm2008-5 EGM2008 5' 11 19
|
||||
egm2008-2_5 EGM2008 2.5' 35 75
|
||||
egm2008-1 EGM2008 1' 170 470
|
||||
|
||||
The size columns give the download and installed sizes of the datasets.
|
||||
In addition you can specify
|
||||
|
||||
all = all of the supported geoids
|
||||
minimal = emg96-5
|
||||
best = egm84-15 egm96-5 egm2008-1 (the highest resolution for each
|
||||
earth gravity model)
|
||||
good = same as best but substitute egm2008-2_5 for egm2008-1 to save
|
||||
on disk space
|
||||
|
||||
-p parentdir (default $DEFAULTDIR) specifies where the
|
||||
datasets should be stored. The "Default $NAME path" listed when running
|
||||
|
||||
$TOOL -h
|
||||
|
||||
should be parentdir/$SUBDIR. This script must be run by a user with
|
||||
write access to this directory.
|
||||
|
||||
Normally only datasets which are not already in parentdir are
|
||||
downloaded. You can force the download and reinstallation with -f.
|
||||
The -f flag also let you download new models (not yet in the set
|
||||
defined by "all").
|
||||
|
||||
If -d is provided, the temporary directory which holds the downloads,
|
||||
\$TMPDIR/$NAME-XXXXXXXX or ${TMPDIR:-/tmp}/$NAME-XXXXXXXX,
|
||||
will be saved. -h prints this help.
|
||||
|
||||
For more information on the $NAME datasets, visit
|
||||
|
||||
https://geographiclib.sourceforge.io/C++/doc/$NAME.html
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
PARENTDIR="$DEFAULTDIR"
|
||||
DEBUG=
|
||||
FORCE=
|
||||
while getopts hp:fd c; do
|
||||
case $c in
|
||||
h )
|
||||
usage;
|
||||
exit 0
|
||||
;;
|
||||
p ) PARENTDIR="$OPTARG"
|
||||
;;
|
||||
f ) FORCE=y
|
||||
;;
|
||||
d ) DEBUG=y
|
||||
;;
|
||||
* )
|
||||
usage 1>&2;
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift `expr $OPTIND - 1`
|
||||
if test $# -eq 0; then
|
||||
usage 1>&2;
|
||||
exit 1
|
||||
fi
|
||||
|
||||
test -d "$PARENTDIR"/$SUBDIR || mkdir -p "$PARENTDIR"/$SUBDIR 2> /dev/null
|
||||
if test ! -d "$PARENTDIR"/$SUBDIR; then
|
||||
echo Cannot create directory $PARENTDIR/$SUBDIR 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TEMP=
|
||||
if test -z "$DEBUG"; then
|
||||
trap 'trap "" 0; test "$TEMP" && rm -rf "$TEMP"; exit 1' 1 2 3 9 15
|
||||
trap 'test "$TEMP" && rm -rf "$TEMP"' 0
|
||||
fi
|
||||
TEMP=`mktemp -d -q -t $NAME-XXXXXXXX`
|
||||
|
||||
if test -z "$TEMP" -o ! -d "$TEMP"; then
|
||||
echo Cannot create temporary directory 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WRITETEST="$PARENTDIR"/$SUBDIR/write-test-`basename $TEMP`
|
||||
if touch "$WRITETEST" 2> /dev/null; then
|
||||
rm -f "$WRITETEST"
|
||||
else
|
||||
echo Cannot write in directory $PARENTDIR/$SUBDIR 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -e
|
||||
|
||||
cat > $TEMP/all <<EOF
|
||||
egm84-30
|
||||
egm84-15
|
||||
egm96-15
|
||||
egm96-5
|
||||
egm2008-5
|
||||
egm2008-2_5
|
||||
egm2008-1
|
||||
EOF
|
||||
|
||||
while test $# -gt 0; do
|
||||
if grep "^$1\$" $TEMP/all > /dev/null; then
|
||||
echo $1
|
||||
else
|
||||
case "$1" in
|
||||
all )
|
||||
cat $TEMP/all
|
||||
;;
|
||||
minimal )
|
||||
echo egm96-5
|
||||
;;
|
||||
best ) # highest resolution models
|
||||
cat <<EOF
|
||||
egm2008-1
|
||||
egm96-5
|
||||
egm84-15
|
||||
EOF
|
||||
;;
|
||||
good ) # like best but with egm2008-1 -> egm2008-2_5
|
||||
cat <<EOF
|
||||
egm2008-2_5
|
||||
egm96-5
|
||||
egm84-15
|
||||
EOF
|
||||
;;
|
||||
* )
|
||||
if test -n "$FORCE"; then
|
||||
echo $1
|
||||
else
|
||||
echo Unknown $MODEL $1 1>&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done > $TEMP/list
|
||||
|
||||
sort -u $TEMP/list > $TEMP/todo
|
||||
|
||||
while read file; do
|
||||
if test -z "$FORCE" -a -s $PARENTDIR/$SUBDIR/$file.$EXT; then
|
||||
echo $PARENTDIR/$SUBDIR/$file.$EXT already installed, skipping $file...
|
||||
echo $file >> $TEMP/skip
|
||||
continue
|
||||
fi
|
||||
echo download $file.tar.bz2 ...
|
||||
echo $file >> $TEMP/download
|
||||
URL="https://downloads.sourceforge.net/project/geographiclib/$SUBDIR-distrib/$file.tar.bz2?use_mirror=autoselect"
|
||||
ARCHIVE=$TEMP/$file.tar.bz2
|
||||
wget -O$ARCHIVE $URL
|
||||
echo unpack $file.tar.bz2 ...
|
||||
tar vxojf $ARCHIVE -C $PARENTDIR
|
||||
echo $MODEL $file installed.
|
||||
done < $TEMP/todo
|
||||
|
||||
if test "$DEBUG"; then
|
||||
echo Saving temporary directory $TEMP
|
||||
fi
|
||||
echo
|
||||
if test -s $TEMP/download; then
|
||||
n=`wc -l < $TEMP/download`
|
||||
s=; test $n -gt 1 && s=s
|
||||
cat <<EOF
|
||||
Installed $NAME dataset$s `tr '\n' ' ' < $TEMP/download`in $PARENTDIR/$SUBDIR.
|
||||
EOF
|
||||
fi
|
||||
if test -s $TEMP/skip; then
|
||||
n=`wc -l < $TEMP/skip`
|
||||
s=; test $n -gt 1 && s=s
|
||||
cat <<EOF
|
||||
Skipped $NAME dataset$s `tr '\n' ' ' < $TEMP/skip | sed 's/ $//'`.
|
||||
EOF
|
||||
fi
|
||||
echo
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
#! /bin/sh
|
||||
#
|
||||
# Download gravity models for use by GeographicLib::GravityModel.
|
||||
#
|
||||
# Copyright (c) Charles Karney (2011-2022) <karney@alum.mit.edu> and
|
||||
# licensed under the MIT/X11 License. For more information, see
|
||||
# https://geographiclib.sourceforge.io/
|
||||
|
||||
DEFAULTDIR="/usr/local/share/GeographicLib"
|
||||
SUBDIR=gravity
|
||||
NAME=gravity
|
||||
MODEL=gravitymodel
|
||||
CLASS=GravityModel
|
||||
TOOL=Gravity
|
||||
EXT=egm.cof
|
||||
usage() {
|
||||
cat <<EOF
|
||||
usage: $0 [-p parentdir] [-f] [-d] [-h] $MODEL...
|
||||
|
||||
This program downloads and installs the datasets used by the
|
||||
GeographicLib::$CLASS class and the $TOOL tool to compute
|
||||
gravity fields. $MODEL is one of more of the names from this
|
||||
table:
|
||||
|
||||
size (kB)
|
||||
name degree tar.bz2 disk
|
||||
egm84 18 27 26
|
||||
egm96 360 2100 2100
|
||||
egm2008 2190 76000 75000
|
||||
wgs84 20 1 1
|
||||
grs80 20 1 1
|
||||
|
||||
The size columns give the download and installed sizes of the datasets.
|
||||
In addition you can specify
|
||||
|
||||
all = all of the supported gravity models
|
||||
minimal = egm96 wgs84
|
||||
|
||||
-p parentdir (default $DEFAULTDIR) specifies where the
|
||||
datasets should be stored. The "Default $NAME path" listed when running
|
||||
|
||||
$TOOL -h
|
||||
|
||||
should be parentdir/$SUBDIR. This script must be run by a user with
|
||||
write access to this directory.
|
||||
|
||||
Normally only datasets which are not already in parentdir are
|
||||
downloaded. You can force the download and reinstallation with -f.
|
||||
The -f flag also let you download new models (not yet in the set
|
||||
defined by "all").
|
||||
|
||||
If -d is provided, the temporary directory which holds the downloads,
|
||||
\$TMPDIR/$NAME-XXXXXXXX or ${TMPDIR:-/tmp}/$NAME-XXXXXXXX,
|
||||
will be saved. -h prints this help.
|
||||
|
||||
For more information on the $NAME datasets, visit
|
||||
|
||||
https://geographiclib.sourceforge.io/C++/doc/$NAME.html
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
PARENTDIR="$DEFAULTDIR"
|
||||
DEBUG=
|
||||
FORCE=
|
||||
while getopts hp:fd c; do
|
||||
case $c in
|
||||
h )
|
||||
usage;
|
||||
exit 0
|
||||
;;
|
||||
p ) PARENTDIR="$OPTARG"
|
||||
;;
|
||||
f ) FORCE=y
|
||||
;;
|
||||
d ) DEBUG=y
|
||||
;;
|
||||
* )
|
||||
usage 1>&2;
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift `expr $OPTIND - 1`
|
||||
if test $# -eq 0; then
|
||||
usage 1>&2;
|
||||
exit 1
|
||||
fi
|
||||
|
||||
test -d "$PARENTDIR"/$SUBDIR || mkdir -p "$PARENTDIR"/$SUBDIR 2> /dev/null
|
||||
if test ! -d "$PARENTDIR"/$SUBDIR; then
|
||||
echo Cannot create directory $PARENTDIR/$SUBDIR 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TEMP=
|
||||
if test -z "$DEBUG"; then
|
||||
trap 'trap "" 0; test "$TEMP" && rm -rf "$TEMP"; exit 1' 1 2 3 9 15
|
||||
trap 'test "$TEMP" && rm -rf "$TEMP"' 0
|
||||
fi
|
||||
TEMP=`mktemp -d -q -t $NAME-XXXXXXXX`
|
||||
|
||||
if test -z "$TEMP" -o ! -d "$TEMP"; then
|
||||
echo Cannot create temporary directory 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WRITETEST="$PARENTDIR"/$SUBDIR/write-test-`basename $TEMP`
|
||||
if touch "$WRITETEST" 2> /dev/null; then
|
||||
rm -f "$WRITETEST"
|
||||
else
|
||||
echo Cannot write in directory $PARENTDIR/$SUBDIR 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -e
|
||||
|
||||
cat > $TEMP/all <<EOF
|
||||
egm84
|
||||
egm96
|
||||
egm2008
|
||||
grs80
|
||||
wgs84
|
||||
EOF
|
||||
|
||||
while test $# -gt 0; do
|
||||
if grep "^$1\$" $TEMP/all > /dev/null; then
|
||||
echo $1
|
||||
else
|
||||
case "$1" in
|
||||
all )
|
||||
cat $TEMP/all
|
||||
;;
|
||||
minimal )
|
||||
echo egm96; echo wgs84
|
||||
;;
|
||||
* )
|
||||
if test -n "$FORCE"; then
|
||||
echo $1
|
||||
else
|
||||
echo Unknown $MODEL $1 1>&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done > $TEMP/list
|
||||
|
||||
sort -u $TEMP/list > $TEMP/todo
|
||||
|
||||
while read file; do
|
||||
if test -z "$FORCE" -a -s $PARENTDIR/$SUBDIR/$file.$EXT; then
|
||||
echo $PARENTDIR/$SUBDIR/$file.$EXT already installed, skipping $file...
|
||||
echo $file >> $TEMP/skip
|
||||
continue
|
||||
fi
|
||||
echo download $file.tar.bz2 ...
|
||||
echo $file >> $TEMP/download
|
||||
URL="https://downloads.sourceforge.net/project/geographiclib/$SUBDIR-distrib/$file.tar.bz2?use_mirror=autoselect"
|
||||
ARCHIVE=$TEMP/$file.tar.bz2
|
||||
wget -O$ARCHIVE $URL
|
||||
echo unpack $file.tar.bz2 ...
|
||||
tar vxojf $ARCHIVE -C $PARENTDIR
|
||||
echo $MODEL $file installed.
|
||||
done < $TEMP/todo
|
||||
|
||||
if test "$DEBUG"; then
|
||||
echo Saving temporary directory $TEMP
|
||||
fi
|
||||
echo
|
||||
if test -s $TEMP/download; then
|
||||
n=`wc -l < $TEMP/download`
|
||||
s=; test $n -gt 1 && s=s
|
||||
cat <<EOF
|
||||
Installed $NAME dataset$s `tr '\n' ' ' < $TEMP/download`in $PARENTDIR/$SUBDIR.
|
||||
EOF
|
||||
fi
|
||||
if test -s $TEMP/skip; then
|
||||
n=`wc -l < $TEMP/skip`
|
||||
s=; test $n -gt 1 && s=s
|
||||
cat <<EOF
|
||||
Skipped $NAME dataset$s `tr '\n' ' ' < $TEMP/skip | sed 's/ $//'`.
|
||||
EOF
|
||||
fi
|
||||
echo
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
#! /bin/sh
|
||||
#
|
||||
# Download magnetic models for use by GeographicLib::MagneticModel.
|
||||
#
|
||||
# Copyright (c) Charles Karney (2011-2022) <karney@alum.mit.edu> and
|
||||
# licensed under the MIT/X11 License. For more information, see
|
||||
# https://geographiclib.sourceforge.io/
|
||||
|
||||
DEFAULTDIR="/usr/local/share/GeographicLib"
|
||||
SUBDIR=magnetic
|
||||
NAME=magnetic
|
||||
MODEL=magneticmodel
|
||||
CLASS=MagneticModel
|
||||
TOOL=MagneticField
|
||||
EXT=wmm.cof
|
||||
usage() {
|
||||
cat <<EOF
|
||||
usage: $0 [-p parentdir] [-f] [-d] [-h] $MODEL...
|
||||
|
||||
This program downloads and installs the datasets used by the
|
||||
GeographicLib::$CLASS class and the $TOOL tool to compute
|
||||
magnetic fields. $MODEL is one of more of the names from this
|
||||
table:
|
||||
|
||||
size (kB)
|
||||
name degree years tar.bz2 disk
|
||||
wmm2010 12 2010-2015 2 3
|
||||
wmm2015 12 2015-2020 2 3 *deprecated*
|
||||
wmm2015v2 12 2015-2020 2 3
|
||||
wmm2020 12 2020-2025 2 3
|
||||
wmm2025 12 2025-2030 2 3
|
||||
wmmhr2025 133 2025-2030 37 281
|
||||
igrf11 13 1900-2015 7 25
|
||||
igrf12 13 1900-2020 7 26
|
||||
igrf13 13 1900-2025 7 28
|
||||
igrf14 13 1900-2025 7 29
|
||||
emm2010 739 2010-2015 3700 4400
|
||||
emm2015 729 2000-2020 660 4300
|
||||
emm2017 790 2000-2022 1740 5050
|
||||
|
||||
The size columns give the download and installed sizes of the datasets.
|
||||
In addition you can specify
|
||||
|
||||
all = all of the supported magnetic models
|
||||
minimal = wmm2025 igrf14
|
||||
|
||||
-p parentdir (default $DEFAULTDIR) specifies where the
|
||||
datasets should be stored. The "Default $NAME path" listed when running
|
||||
|
||||
$TOOL -h
|
||||
|
||||
should be parentdir/$SUBDIR. This script must be run by a user with
|
||||
write access to this directory.
|
||||
|
||||
Normally only datasets which are not already in parentdir are
|
||||
downloaded. You can force the download and reinstallation with -f.
|
||||
The -f flag also let you download new models (not yet in the set
|
||||
defined by "all").
|
||||
|
||||
If -d is provided, the temporary directory which holds the downloads,
|
||||
\$TMPDIR/$NAME-XXXXXXXX or ${TMPDIR:-/tmp}/$NAME-XXXXXXXX,
|
||||
will be saved. -h prints this help.
|
||||
|
||||
For more information on the magnetic models, visit
|
||||
|
||||
https://geographiclib.sourceforge.io/C++/doc/$NAME.html
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
PARENTDIR="$DEFAULTDIR"
|
||||
FORCE=
|
||||
while getopts hp:fd c; do
|
||||
case $c in
|
||||
h )
|
||||
usage;
|
||||
exit 0
|
||||
;;
|
||||
p ) PARENTDIR="$OPTARG"
|
||||
;;
|
||||
f ) FORCE=y
|
||||
;;
|
||||
d ) DEBUG=y
|
||||
;;
|
||||
* )
|
||||
usage 1>&2;
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift `expr $OPTIND - 1`
|
||||
if test $# -eq 0; then
|
||||
usage 1>&2;
|
||||
exit 1
|
||||
fi
|
||||
|
||||
test -d "$PARENTDIR"/$SUBDIR || mkdir -p "$PARENTDIR"/$SUBDIR 2> /dev/null
|
||||
if test ! -d "$PARENTDIR"/$SUBDIR; then
|
||||
echo Cannot create directory $PARENTDIR/$SUBDIR 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TEMP=
|
||||
if test -z "$DEBUG"; then
|
||||
trap 'trap "" 0; test "$TEMP" && rm -rf "$TEMP"; exit 1' 1 2 3 9 15
|
||||
trap 'test "$TEMP" && rm -rf "$TEMP"' 0
|
||||
fi
|
||||
TEMP=`mktemp -d -q -t $NAME-XXXXXXXX`
|
||||
|
||||
if test -z "$TEMP" -o ! -d "$TEMP"; then
|
||||
echo Cannot create temporary directory 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WRITETEST="$PARENTDIR"/$SUBDIR/write-test-`basename $TEMP`
|
||||
if touch "$WRITETEST" 2> /dev/null; then
|
||||
rm -f "$WRITETEST"
|
||||
else
|
||||
echo Cannot write in directory $PARENTDIR/$SUBDIR 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -e
|
||||
|
||||
cat > $TEMP/all <<EOF
|
||||
wmm2010
|
||||
wmm2015
|
||||
wmm2015v2
|
||||
wmm2020
|
||||
wmm2025
|
||||
wmmhr2025
|
||||
emm2010
|
||||
emm2015
|
||||
emm2017
|
||||
igrf11
|
||||
igrf12
|
||||
igrf13
|
||||
igrf14
|
||||
EOF
|
||||
|
||||
while test $# -gt 0; do
|
||||
if grep "^$1\$" $TEMP/all > /dev/null; then
|
||||
echo $1
|
||||
else
|
||||
case "$1" in
|
||||
all )
|
||||
cat $TEMP/all
|
||||
;;
|
||||
minimal )
|
||||
echo wmm2025; echo igrf14
|
||||
;;
|
||||
* )
|
||||
if test -n "$FORCE"; then
|
||||
echo $1
|
||||
else
|
||||
echo Unknown $MODEL $1 1>&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done > $TEMP/list
|
||||
|
||||
sort -u $TEMP/list > $TEMP/todo
|
||||
|
||||
while read file; do
|
||||
if test -z "$FORCE" -a -s $PARENTDIR/$SUBDIR/$file.$EXT; then
|
||||
echo $PARENTDIR/$SUBDIR/$file.$EXT already installed, skipping $file...
|
||||
echo $file >> $TEMP/skip
|
||||
continue
|
||||
fi
|
||||
echo download $file.tar.bz2 ...
|
||||
echo $file >> $TEMP/download
|
||||
URL="https://downloads.sourceforge.net/project/geographiclib/$SUBDIR-distrib/$file.tar.bz2?use_mirror=autoselect"
|
||||
ARCHIVE=$TEMP/$file.tar.bz2
|
||||
wget -O$ARCHIVE $URL
|
||||
echo unpack $file.tar.bz2 ...
|
||||
tar vxojf $ARCHIVE -C $PARENTDIR
|
||||
echo $MODEL $file installed.
|
||||
done < $TEMP/todo
|
||||
|
||||
if test "$DEBUG"; then
|
||||
echo Saving temporary directory $TEMP
|
||||
fi
|
||||
echo
|
||||
if test -s $TEMP/download; then
|
||||
n=`wc -l < $TEMP/download`
|
||||
s=; test $n -gt 1 && s=s
|
||||
cat <<EOF
|
||||
Installed $NAME dataset$s `tr '\n' ' ' < $TEMP/download`in $PARENTDIR/$SUBDIR.
|
||||
EOF
|
||||
fi
|
||||
if test -s $TEMP/skip; then
|
||||
n=`wc -l < $TEMP/skip`
|
||||
s=; test $n -gt 1 && s=s
|
||||
cat <<EOF
|
||||
Skipped $NAME dataset$s `tr '\n' ' ' < $TEMP/skip | sed 's/ $//'`.
|
||||
EOF
|
||||
fi
|
||||
echo
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
# This CMakeLists.txt is invoked in two different ways
|
||||
|
||||
# (1) With "add_subdirectory (examples)" from GeographicLib's top-level
|
||||
# CMakeLists.txt. This mode of invocation is flagged by the variable
|
||||
#
|
||||
# CALLED_FROM_TOPLEVEL
|
||||
#
|
||||
# In this case, the only action taken is to install the examples and
|
||||
# this CMakeLists.txt in ${EXAMPLEDIR}.
|
||||
|
||||
# (2) As an independent invocation of
|
||||
#
|
||||
# cmake -S <this-directory> -B <build-directory>
|
||||
#
|
||||
# In this case, find_package (GeographicLib) is called and the examples
|
||||
# are compiled. This mode of invocation is triggered by the
|
||||
# exampleprograms target in the top-level CMakeLists.txt. In this case,
|
||||
# the current version of GeographicLib is found by specifying
|
||||
#
|
||||
# -D GeographicLib_DIR=${PROJECT_BINARY_DIR}
|
||||
|
||||
cmake_minimum_required (VERSION 3.17.0)
|
||||
|
||||
set (EXAMPLES0
|
||||
example-Accumulator.cpp
|
||||
example-AlbersEqualArea.cpp
|
||||
example-Angle.cpp
|
||||
example-AuxAngle.cpp
|
||||
example-AuxLatitude.cpp
|
||||
example-AzimuthalEquidistant.cpp
|
||||
example-CassiniSoldner.cpp
|
||||
example-CircularEngine.cpp
|
||||
example-Constants.cpp
|
||||
example-DMS.cpp
|
||||
example-DST.cpp
|
||||
example-Ellipsoid.cpp
|
||||
example-EllipticFunction.cpp
|
||||
example-GARS.cpp
|
||||
example-GeoCoords.cpp
|
||||
example-Geocentric.cpp
|
||||
example-Geodesic.cpp
|
||||
example-Geodesic-small.cpp
|
||||
example-GeodesicExact.cpp
|
||||
example-GeodesicLine.cpp
|
||||
example-GeodesicLineExact.cpp
|
||||
example-GeographicErr.cpp
|
||||
example-Geohash.cpp
|
||||
example-Geoid.cpp
|
||||
example-Georef.cpp
|
||||
example-Gnomonic.cpp
|
||||
example-GravityCircle.cpp
|
||||
example-GravityModel.cpp
|
||||
example-Intersect.cpp
|
||||
example-LambertConformalConic.cpp
|
||||
example-LocalCartesian.cpp
|
||||
example-MGRS.cpp
|
||||
example-MagneticCircle.cpp
|
||||
example-MagneticModel.cpp
|
||||
example-Math.cpp
|
||||
example-NearestNeighbor.cpp
|
||||
example-NormalGravity.cpp
|
||||
example-OSGB.cpp
|
||||
example-PolarStereographic.cpp
|
||||
example-PolygonArea.cpp
|
||||
example-Rhumb.cpp
|
||||
example-RhumbLine.cpp
|
||||
example-SphericalEngine.cpp
|
||||
example-SphericalHarmonic.cpp
|
||||
example-SphericalHarmonic1.cpp
|
||||
example-SphericalHarmonic2.cpp
|
||||
example-TransverseMercator.cpp
|
||||
example-TransverseMercatorExact.cpp
|
||||
example-Trigfun.cpp
|
||||
example-TrigfunExt.cpp
|
||||
example-UTMUPS.cpp
|
||||
example-Utility.cpp
|
||||
example-Cartesian3.cpp
|
||||
example-Conformal3.cpp
|
||||
example-Ellipsoid3.cpp
|
||||
example-Geodesic3.cpp
|
||||
example-GeodesicLine3.cpp
|
||||
)
|
||||
set (EXAMPLES1
|
||||
GeoidToGTX.cpp make-egmcof.cpp)
|
||||
|
||||
if (CALLED_FROM_TOPLEVEL)
|
||||
if (EXAMPLEDIR)
|
||||
install (FILES CMakeLists.txt ${EXAMPLES0} ${EXAMPLES1} ${EXAMPLEHEADERS}
|
||||
DESTINATION ${EXAMPLEDIR})
|
||||
endif ()
|
||||
# No more to do in add_subdirectory mode, so exit
|
||||
return ()
|
||||
endif ()
|
||||
|
||||
project (GeographicLib-examples)
|
||||
|
||||
# Set a default build type for single-configuration cmake generators if
|
||||
# no build type is set.
|
||||
if (NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
|
||||
set (CMAKE_BUILD_TYPE Release)
|
||||
endif ()
|
||||
|
||||
if (MSVC OR CMAKE_CONFIGURATION_TYPES)
|
||||
# For multi-config systems and for Visual Studio, the debug version of
|
||||
# the library is called Geographic_d.
|
||||
set (CMAKE_DEBUG_POSTFIX "_d" CACHE STRING "The suffix for debug objects")
|
||||
else ()
|
||||
set (CMAKE_DEBUG_POSTFIX "" CACHE STRING "The suffix for debug objects")
|
||||
endif ()
|
||||
|
||||
find_package (GeographicLib 2.7 REQUIRED)
|
||||
include_directories (${GeographicLib_INCLUDE_DIRS})
|
||||
|
||||
set (CMAKE_CXX_STANDARD 17)
|
||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
option (USE_BOOST
|
||||
"Look for Boost library when compiling examples" ON)
|
||||
|
||||
if (USE_BOOST)
|
||||
# The earliest Boost version to include
|
||||
# https://github.com/boostorg/multiprecision/pull/389
|
||||
find_package (Boost 1.79 CONFIG COMPONENTS serialization)
|
||||
elseif (GEOGRAPHICLIB_PRECISION EQUAL 4)
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
find_package (Boost 1.79 CONFIG)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Compile a bunch of tiny example programs. These are built with the
|
||||
# "exampleprograms" target. These are mainly for including as examples
|
||||
# within the doxygen documentation; however, compiling them catches some
|
||||
# obvious blunders.
|
||||
|
||||
if (NOT GEOGRAPHICLIB_PRECISION OR GEOGRAPHICLIB_PRECISION EQUAL 2)
|
||||
# These examples all assume real = double, so check
|
||||
# GEOGRAPHICLIB_PRECISION. Allow GEOGRAPHICLIB_PRECISION to be unset
|
||||
# to accommodate lame FindGeographicLib.cmake.
|
||||
set (EXAMPLE_SOURCES ${EXAMPLES0})
|
||||
if (USE_BOOST AND Boost_FOUND)
|
||||
add_definitions (-DGEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION=1)
|
||||
include_directories ("${Boost_INCLUDE_DIRS}")
|
||||
endif ()
|
||||
else ()
|
||||
set (EXAMPLE_SOURCES)
|
||||
endif ()
|
||||
set (EXAMPLE_SOURCES ${EXAMPLE_SOURCES} ${EXAMPLES1})
|
||||
|
||||
set (EXAMPLES)
|
||||
foreach (EXAMPLE_SOURCE ${EXAMPLE_SOURCES})
|
||||
get_filename_component (EXAMPLE ${EXAMPLE_SOURCE} NAME_WE)
|
||||
set (EXAMPLES ${EXAMPLES} ${EXAMPLE})
|
||||
add_executable (${EXAMPLE} ${EXAMPLE_SOURCE})
|
||||
target_link_libraries (${EXAMPLE}
|
||||
${GeographicLib_LIBRARIES} ${GeographicLib_HIGHPREC_LIBRARIES})
|
||||
endforeach ()
|
||||
if (Boost_FOUND AND GEOGRAPHICLIB_PRECISION EQUAL 2)
|
||||
target_link_libraries (example-NearestNeighbor ${Boost_LIBRARIES})
|
||||
endif ()
|
||||
|
||||
find_package (OpenMP QUIET)
|
||||
|
||||
if (OPENMP_FOUND OR OpenMP_FOUND)
|
||||
set_target_properties (GeoidToGTX PROPERTIES
|
||||
COMPILE_FLAGS ${OpenMP_CXX_FLAGS})
|
||||
if (OpenMP_CXX_INCLUDE_DIRS)
|
||||
set_target_properties (GeoidToGTX PROPERTIES
|
||||
INCLUDE_DIRECTORIES ${OpenMP_CXX_INCLUDE_DIRS})
|
||||
endif ()
|
||||
if (NOT WIN32)
|
||||
set_target_properties (GeoidToGTX PROPERTIES
|
||||
LINK_FLAGS ${OpenMP_CXX_FLAGS})
|
||||
endif ()
|
||||
target_link_libraries (GeoidToGTX ${OpenMP_CXX_LIBRARIES})
|
||||
endif ()
|
||||
|
||||
if (MSVC OR CMAKE_CONFIGURATION_TYPES)
|
||||
# Add _d suffix for your debug versions of the tools
|
||||
set_target_properties (${EXAMPLES} PROPERTIES
|
||||
DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
|
||||
endif ()
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
// Write out a gtx file of geoid heights above the ellipsoid. For egm2008 at
|
||||
// 1' resolution this takes about 10 mins on a 8-processor Intel 3.0 GHz
|
||||
// machine using OpenMP.
|
||||
//
|
||||
// For the format of gtx files, see
|
||||
// https://vdatum.noaa.gov/docs/gtx_info.html#dev_gtx_binary
|
||||
//
|
||||
// data is binary big-endian:
|
||||
// south latitude edge (degrees double)
|
||||
// west longitude edge (degrees double)
|
||||
// delta latitude (degrees double)
|
||||
// delta longitude (degrees double)
|
||||
// nlat = number of latitude rows (integer)
|
||||
// nlong = number of longitude columns (integer)
|
||||
// nlat * nlong geoid heights (meters float)
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#if defined(_OPENMP)
|
||||
#define HAVE_OPENMP 1
|
||||
#else
|
||||
#define HAVE_OPENMP 0
|
||||
#endif
|
||||
|
||||
#if HAVE_OPENMP
|
||||
# include <omp.h>
|
||||
#endif
|
||||
|
||||
#include <GeographicLib/GravityModel.hpp>
|
||||
#include <GeographicLib/GravityCircle.hpp>
|
||||
#include <GeographicLib/Utility.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace GeographicLib;
|
||||
|
||||
int main(int argc, const char* const argv[]) {
|
||||
// Hardwired for 3 args:
|
||||
// 1 = the gravity model (e.g., egm2008)
|
||||
// 2 = intervals per degree
|
||||
// 3 = output GTX file
|
||||
if (argc != 4) {
|
||||
cerr << "Usage: " << argv[0]
|
||||
<< " gravity-model intervals-per-degree output.gtx\n";
|
||||
return 1;
|
||||
}
|
||||
try {
|
||||
// Will need to set the precision for each thread, so save return value
|
||||
int ndigits = Utility::set_digits();
|
||||
string model(argv[1]);
|
||||
// Number of intervals per degree
|
||||
int ndeg = Utility::val<int>(string(argv[2]));
|
||||
string filename(argv[3]);
|
||||
GravityModel g(model);
|
||||
int
|
||||
nlat = Math::hd * ndeg + 1,
|
||||
nlon = Math::td * ndeg;
|
||||
Math::real
|
||||
delta = 1 / Math::real(ndeg), // Grid spacing
|
||||
latorg = -Math::qd,
|
||||
lonorg = -Math::hd;
|
||||
// Write results as floats in binary mode
|
||||
ofstream file(filename.c_str(), ios::binary);
|
||||
|
||||
// Write header
|
||||
{
|
||||
Math::real transform[] = {latorg, lonorg,
|
||||
1/Math::real(ndeg), 1/Math::real(ndeg)};
|
||||
unsigned sizes[] = {unsigned(nlat), unsigned(nlon)};
|
||||
Utility::writearray<double, Math::real, true>(file, transform, 4);
|
||||
Utility::writearray<unsigned, unsigned, true>(file, sizes, 2);
|
||||
}
|
||||
|
||||
// Compute and store results for nbatch latitudes at a time
|
||||
const int nbatch = 64;
|
||||
vector< vector<float> > N(nbatch, vector<float>(nlon));
|
||||
|
||||
for (int ilat0 = 0; ilat0 < nlat; ilat0 += nbatch) { // Loop over batches
|
||||
int nlat0 = min(nlat, ilat0 + nbatch);
|
||||
|
||||
#if HAVE_OPENMP
|
||||
# pragma omp parallel for
|
||||
#endif
|
||||
for (int ilat = ilat0; ilat < nlat0; ++ilat) { // Loop over latitudes
|
||||
Utility::set_digits(ndigits); // Set the precision
|
||||
Math::real lat = (latorg * ndeg + ilat) / ndeg, h = 0;
|
||||
GravityCircle c(g.Circle(lat, h, GravityModel::GEOID_HEIGHT));
|
||||
for (int ilon = 0; ilon < nlon; ++ilon) { // Loop over longitudes
|
||||
Math::real lon = (lonorg * ndeg + ilon) / ndeg;
|
||||
N[ilat - ilat0][ilon] = float(c.GeoidHeight(lon));
|
||||
} // longitude loop
|
||||
} // latitude loop -- end of parallel section
|
||||
|
||||
for (int ilat = ilat0; ilat < nlat0; ++ilat) // write out data
|
||||
Utility::writearray<float, float, true>(file, N[ilat - ilat0]);
|
||||
} // batch loop
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
catch (...) {
|
||||
cerr << "Caught unknown exception\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// Example of using the GeographicLib::Accumulator class
|
||||
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <GeographicLib/Accumulator.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace GeographicLib;
|
||||
|
||||
int main() {
|
||||
try {
|
||||
// Compare using Accumulator and ordinary summation for a sum of large and
|
||||
// small terms.
|
||||
double sum = 0;
|
||||
Accumulator<> acc = 0;
|
||||
sum += 1e20; sum += 1; sum += 2; sum += 100; sum += 5000; sum += -1e20;
|
||||
acc += 1e20; acc += 1; acc += 2; acc += 100; acc += 5000; acc += -1e20;
|
||||
cout << sum << " " << acc() << "\n";
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// Example of using the GeographicLib::AlbersEqualArea class
|
||||
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <GeographicLib/AlbersEqualArea.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace GeographicLib;
|
||||
|
||||
int main() {
|
||||
try {
|
||||
const double
|
||||
a = Constants::WGS84_a(),
|
||||
f = Constants::WGS84_f(),
|
||||
lat1 = 40 + 58/60.0, lat2 = 39 + 56/60.0, // standard parallels
|
||||
k1 = 1, // scale
|
||||
lon0 = -77 - 45/60.0; // Central meridian
|
||||
// Set up basic projection
|
||||
const AlbersEqualArea albers(a, f, lat1, lat2, k1);
|
||||
{
|
||||
// Sample conversion from geodetic to Albers Equal Area
|
||||
double lat = 39.95, lon = -75.17; // Philadelphia
|
||||
double x, y;
|
||||
albers.Forward(lon0, lat, lon, x, y);
|
||||
cout << x << " " << y << "\n";
|
||||
}
|
||||
{
|
||||
// Sample conversion from Albers Equal Area grid to geodetic
|
||||
double x = 220e3, y = -53e3;
|
||||
double lat, lon;
|
||||
albers.Reverse(lon0, x, y, lat, lon);
|
||||
cout << lat << " " << lon << "\n";
|
||||
}
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// Example of using the GeographicLib::Angle class.
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <exception>
|
||||
#include <GeographicLib/Angle.hpp>
|
||||
|
||||
int main(int argc, const char* const argv[]) {
|
||||
try {
|
||||
using ang = GeographicLib::Angle;
|
||||
// Print table of parametric latitudes for f = 0.5
|
||||
double f = 0.5;
|
||||
std::cout << std::fixed << std::setprecision(4);
|
||||
for (double d = 0; d <= 90; d+=10) {
|
||||
ang phi{d};
|
||||
std::cout << double(d) << " " << double(phi.modang(1-f)) << "\n";
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// Example of using the GeographicLib::AuxAngle class.
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <exception>
|
||||
#include <GeographicLib/AuxAngle.hpp>
|
||||
|
||||
int main(int argc, const char* const argv[]) {
|
||||
try {
|
||||
using angle = GeographicLib::AuxAngle;
|
||||
// Print table of parametric latitudes for f = 0.5
|
||||
double f = 0.5;
|
||||
std::cout << std::fixed << std::setprecision(4);
|
||||
for (int d = 0; d <= 90; d+=10) {
|
||||
angle phi(angle::degrees(d)), beta(phi);
|
||||
beta.y() *= (1 - f);
|
||||
std::cout << d << " " << beta.degrees() << "\n";
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// Example of using the GeographicLib::AuxLatitude class. See the paper
|
||||
//
|
||||
// - C. F. F. Karney,
|
||||
// On auxiliary latitudes,
|
||||
// Survey Review 56(395), 165--180 (2024).
|
||||
// https://doi.org/10.1080/00396265.2023.2217604
|
||||
// preprint: https://arxiv.org/abs/2212.05818
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <exception>
|
||||
#include <GeographicLib/Utility.hpp>
|
||||
#include <GeographicLib/AuxLatitude.hpp>
|
||||
|
||||
int main(int argc, const char* const argv[]) {
|
||||
try {
|
||||
using latitude = GeographicLib::AuxLatitude;
|
||||
using angle = GeographicLib::AuxAngle;
|
||||
if (argc != 3) {
|
||||
std::cerr << "Usage: example-AuxLatitude <n> <base-lat>\n";
|
||||
return 1;
|
||||
}
|
||||
double n = GeographicLib::Utility::fract<double>(std::string(argv[1]));
|
||||
int auxin = GeographicLib::Utility::val<int>(std::string(argv[2]));
|
||||
double a = 1+n, b = 1-n; // Equatorial radius and polar semiaxis
|
||||
latitude aux(latitude::axes(a, b));
|
||||
bool exact = true; // Do the exact calculation
|
||||
std::cout << std::setprecision(9) << std::fixed;
|
||||
int m = 1;
|
||||
for (int l = 0; l < 90*m; ++l) {
|
||||
angle phi(angle::degrees((l+0.5)/m));
|
||||
for (int auxout = 0; auxout < latitude::AUXNUMBER; ++auxout) {
|
||||
angle eta = aux.Convert(auxin, auxout, phi, exact);
|
||||
std::cout << (auxout ? " " : "") << eta.degrees();
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Example of using the GeographicLib::AzimuthalEquidistant class
|
||||
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <GeographicLib/Geodesic.hpp>
|
||||
#include <GeographicLib/AzimuthalEquidistant.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace GeographicLib;
|
||||
|
||||
int main() {
|
||||
try {
|
||||
Geodesic geod(Constants::WGS84_a(), Constants::WGS84_f());
|
||||
// Alternatively: const Geodesic& geod = Geodesic::WGS84();
|
||||
const double lat0 = 48 + 50/60.0, lon0 = 2 + 20/60.0; // Paris
|
||||
AzimuthalEquidistant proj(geod);
|
||||
{
|
||||
// Sample forward calculation
|
||||
double lat = 50.9, lon = 1.8; // Calais
|
||||
double x, y;
|
||||
proj.Forward(lat0, lon0, lat, lon, x, y);
|
||||
cout << x << " " << y << "\n";
|
||||
}
|
||||
{
|
||||
// Sample reverse calculation
|
||||
double x = -38e3, y = 230e3;
|
||||
double lat, lon;
|
||||
proj.Reverse(lat0, lon0, x, y, lat, lon);
|
||||
cout << lat << " " << lon << "\n";
|
||||
}
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Example of using the Triaxial::Cartesian3 class.
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <exception>
|
||||
#include <random>
|
||||
#include <GeographicLib/Triaxial/Cartesian3.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace GeographicLib;
|
||||
|
||||
int main() {
|
||||
try {
|
||||
using Triaxial::Cartesian3;
|
||||
Cartesian3 cart(3, 2, 1);
|
||||
unsigned long long
|
||||
s1 = std::random_device()(),
|
||||
s2 = std::random_device()();
|
||||
std::seed_seq seq{s1, s2};
|
||||
std::mt19937 g(seq);
|
||||
cout << "10 random points on the ellipsoid with semiaxes [3, 2, 1]\n"
|
||||
<< "X Y Z beta omega\n" << fixed << setprecision(3);
|
||||
Cartesian3::vec3 r;
|
||||
Angle bet, omg;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
cart.cart2rand(g, r);
|
||||
cart.cart2toany(r, Cartesian3::ELLIPSOIDAL, bet, omg);
|
||||
cout << r[0] << " " << r[1] << " " << r[2] << " "
|
||||
<< double(bet) << " " << double(omg) << "\n";
|
||||
}
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Example of using the GeographicLib::CassiniSoldner class
|
||||
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <GeographicLib/Geodesic.hpp>
|
||||
#include <GeographicLib/CassiniSoldner.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace GeographicLib;
|
||||
|
||||
int main() {
|
||||
try {
|
||||
Geodesic geod(Constants::WGS84_a(), Constants::WGS84_f());
|
||||
// Alternatively: const Geodesic& geod = Geodesic::WGS84();
|
||||
const double lat0 = 48 + 50/60.0, lon0 = 2 + 20/60.0; // Paris
|
||||
CassiniSoldner proj(lat0, lon0, geod);
|
||||
{
|
||||
// Sample forward calculation
|
||||
double lat = 50.9, lon = 1.8; // Calais
|
||||
double x, y;
|
||||
proj.Forward(lat, lon, x, y);
|
||||
cout << x << " " << y << "\n";
|
||||
}
|
||||
{
|
||||
// Sample reverse calculation
|
||||
double x = -38e3, y = 230e3;
|
||||
double lat, lon;
|
||||
proj.Reverse(x, y, lat, lon);
|
||||
cout << lat << " " << lon << "\n";
|
||||
}
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// Example of using the GeographicLib::CircularEngine class
|
||||
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <vector>
|
||||
#include <GeographicLib/CircularEngine.hpp>
|
||||
#include <GeographicLib/SphericalHarmonic.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace GeographicLib;
|
||||
|
||||
int main() {
|
||||
// This computes the same value as example-SphericalHarmonic.cpp using a
|
||||
// CircularEngine (which will be faster if many values on a circle of
|
||||
// latitude are to be found).
|
||||
try {
|
||||
using std::hypot;
|
||||
int N = 3; // The maxium degree
|
||||
double ca[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; // cosine coefficients
|
||||
vector<double> C(ca, ca + (N + 1) * (N + 2) / 2);
|
||||
double sa[] = {6, 5, 4, 3, 2, 1}; // sine coefficients
|
||||
vector<double> S(sa, sa + N * (N + 1) / 2);
|
||||
double a = 1;
|
||||
SphericalHarmonic h(C, S, N, a);
|
||||
double x = 2, y = 3, z = 1, p = hypot(x, y);
|
||||
CircularEngine circ = h.Circle(p, z, true);
|
||||
double v, vx, vy, vz;
|
||||
v = circ(x/p, y/p, vx, vy, vz);
|
||||
cout << v << " " << vx << " " << vy << " " << vz << "\n";
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// Example of using the Triaxial::Conformal3 class.
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <exception>
|
||||
#include <GeographicLib/Triaxial/Conformal3.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace GeographicLib;
|
||||
|
||||
int main() {
|
||||
try {
|
||||
Triaxial::Conformal3 proj(Triaxial::Ellipsoid3::Earth());
|
||||
cout << fixed << setprecision(1)
|
||||
<< "Ellipsoid parameters: a = " << proj.t().a()
|
||||
<< ", b = " << proj.t().b() << ", c = " << proj.t().c() << "\n"
|
||||
<< setprecision(3)
|
||||
<< "Quadrants: x = " << proj.x0() << ", y = " << proj.y0() << "\n";
|
||||
cout << "Coordinates angle (deg) x (m) y (m):\n";
|
||||
cout << setprecision(2);
|
||||
for (int i = -180; i <= 180; i += 15) {
|
||||
double omg = i + 90, bet = i,
|
||||
x = proj.x(Angle(omg)), y = proj.y(Angle(bet));
|
||||
cout << i << " " << x << " " << y << "\n";
|
||||
}
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// Example of using the GeographicLib::Constants class
|
||||
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <GeographicLib/Constants.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace GeographicLib;
|
||||
|
||||
int main() {
|
||||
try {
|
||||
cout << "WGS84 parameters:\n"
|
||||
<< "a = " << Constants::WGS84_a() << " m\n"
|
||||
<< "f = 1/" << 1/Constants::WGS84_f() << "\n";
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// Example of using the GeographicLib::DMS class
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <exception>
|
||||
#include <GeographicLib/DMS.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace GeographicLib;
|
||||
|
||||
int main() {
|
||||
try {
|
||||
{
|
||||
string dms = "30d14'45.6\"S";
|
||||
DMS::flag type;
|
||||
double ang = DMS::Decode(dms, type);
|
||||
cout << type << " " << ang << "\n";
|
||||
}
|
||||
{
|
||||
double ang = -30.245715;
|
||||
string dms = DMS::Encode(ang, 6, DMS::LATITUDE);
|
||||
cout << dms << "\n";
|
||||
}
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// Example of using the GeographicLib::DST class
|
||||
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <vector>
|
||||
#include <GeographicLib/Math.hpp>
|
||||
#include <GeographicLib/DST.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace GeographicLib;
|
||||
|
||||
class sawtooth {
|
||||
private:
|
||||
double _a;
|
||||
public:
|
||||
sawtooth(double a) : _a(a) {}
|
||||
// only called for x in (0, pi/2]. DST assumes function is periodic, period
|
||||
// 2*pi, is odd about 0, and is even about pi/2.
|
||||
double operator()(double x) const { return _a * x; }
|
||||
};
|
||||
|
||||
int main() {
|
||||
try {
|
||||
sawtooth f(Math::pi()/4);
|
||||
DST dst;
|
||||
int N = 5, K = 2*N;
|
||||
vector<double> tx(N), txa(2*N);
|
||||
dst.reset(N);
|
||||
dst.transform(f, tx.data());
|
||||
cout << "Transform of sawtooth based on " << N << " points\n"
|
||||
<< "approx 1, -1/9, 1/25, -1/49, ...\n";
|
||||
for (int i = 0; i < min(K,N); ++i) {
|
||||
int j = (2*i+1)*(2*i+1)*(1-((i&1)<<1));
|
||||
cout << i << " " << tx[i] << " " << tx[i]*j << "\n";
|
||||
}
|
||||
tx.resize(2*N);
|
||||
dst.refine(f, tx.data());
|
||||
cout << "Add another " << N << " points\n";
|
||||
for (int i = 0; i < min(K,2*N); ++i) {
|
||||
int j = (2*i+1)*(2*i+1)*(1-((i&1)<<1));
|
||||
cout << i << " " << tx[i] << " " << tx[i]*j << "\n";
|
||||
}
|
||||
dst.reset(2*N);
|
||||
dst.transform(f, txa.data());
|
||||
cout << "Retransform of sawtooth based on " << 2*N << " points\n";
|
||||
for (int i = 0; i < min(K,2*N); ++i) {
|
||||
int j = (2*i+1)*(2*i+1)*(1-((i&1)<<1));
|
||||
cout << i << " " << txa[i] << " " << txa[i]*j << "\n";
|
||||
}
|
||||
cout << "Table of values and integral\n";
|
||||
for (int i = 0; i <= K; ++i) {
|
||||
double x = i*Math::pi()/(2*K), sinx = sin(x), cosx = cos(x);
|
||||
cout << x << " " << f(x) << " "
|
||||
<< DST::eval(sinx, cosx, txa.data(), 2*N) << " "
|
||||
<< DST::integral(sinx, cosx, txa.data(), 2*N) << "\n";
|
||||
}
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// Example of using the GeographicLib::Ellipsoid class
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <exception>
|
||||
#include <GeographicLib/Ellipsoid.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace GeographicLib;
|
||||
|
||||
int main() {
|
||||
try {
|
||||
Ellipsoid wgs84(Constants::WGS84_a(), Constants::WGS84_f());
|
||||
// Alternatively: const Ellipsoid& wgs84 = Ellipsoid::WGS84();
|
||||
cout << "The latitude half way between the equator and the pole is "
|
||||
<< wgs84.InverseRectifyingLatitude(45) << "\n";
|
||||
cout << "Half the area of the ellipsoid lies between latitudes +/- "
|
||||
<< wgs84.InverseAuthalicLatitude(30) << "\n";
|
||||
cout << "The northernmost edge of a square Mercator map is at latitude "
|
||||
<< wgs84.InverseIsometricLatitude(180) << "\n";
|
||||
cout << "Table phi(deg) beta-phi xi-phi mu-phi chi-phi theta-phi (mins)\n"
|
||||
<< fixed << setprecision(2);
|
||||
for (int i = 0; i <= 90; i += 15) {
|
||||
double phi = i,
|
||||
bet = wgs84.ParametricLatitude(phi),
|
||||
xi = wgs84.AuthalicLatitude(phi),
|
||||
mu = wgs84.RectifyingLatitude(phi),
|
||||
chi = wgs84.ConformalLatitude(phi),
|
||||
theta = wgs84.GeocentricLatitude(phi);
|
||||
cout << i << " "
|
||||
<< (bet-phi)*60 << " "
|
||||
<< (xi-phi)*60 << " "
|
||||
<< (mu-phi)*60 << " "
|
||||
<< (chi-phi)*60 << " "
|
||||
<< (theta-phi)*60 << "\n";
|
||||
}
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// Example of using the Triaxial::Ellipsoid3 class.
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <exception>
|
||||
#include <GeographicLib/Triaxial/Ellipsoid3.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace GeographicLib;
|
||||
|
||||
int main() {
|
||||
try {
|
||||
using Triaxial::Ellipsoid3;
|
||||
using ang = Angle;
|
||||
using vec3 = Ellipsoid3::vec3;
|
||||
Ellipsoid3 t(3, 2, 1);
|
||||
ang bet{40}, omg{20}, alp{80};
|
||||
vec3 r, v;
|
||||
t.elliptocart2(bet, omg, alp, r, v);
|
||||
cout << fixed << setprecision(3)
|
||||
<< "[bet, omg, alp] = ["
|
||||
<< double(bet) << " " << double(omg) << " " << double(alp)
|
||||
<< "]\n => r = [" << r[0] << " " << r[1] << " " << r[2]
|
||||
<< "], v = [" << v[0] << " " << v[1] << " " << v[2] << "]\n";
|
||||
ang betn, omgn, alpn;
|
||||
t.cart2toellip(r, v, betn, omgn, alpn);
|
||||
cout << " => [bet, omg, alp] = ["
|
||||
<< double(betn) << " " << double(omgn) << " " << double(alpn) << "]\n";
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// Example of using the GeographicLib::EllipticFunction class
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <exception>
|
||||
#include <cmath>
|
||||
#include <GeographicLib/Math.hpp>
|
||||
#include <GeographicLib/EllipticFunction.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace GeographicLib;
|
||||
|
||||
int main() {
|
||||
try {
|
||||
EllipticFunction ell(0.1); // parameter m = 0.1
|
||||
// See Abramowitz and Stegun, table 17.1
|
||||
cout << ell.K() << " " << ell.E() << "\n";
|
||||
double phi = 20, sn, cn;
|
||||
Math::sincosd(phi, sn ,cn);
|
||||
// See Abramowitz and Stegun, table 17.6 with
|
||||
// alpha = asin(sqrt(m)) = 18.43 deg and phi = 20 deg
|
||||
cout << ell.E(phi * Math::degree()) << " "
|
||||
<< ell.E(sn, cn, ell.Delta(sn, cn))
|
||||
<< "\n";
|
||||
// See Carlson 1995, Sec 3.
|
||||
cout << fixed << setprecision(16)
|
||||
<< "RF(1,2,0) = " << EllipticFunction::RF(1,2) << "\n"
|
||||
<< "RF(2,3,4) = " << EllipticFunction::RF(2,3,4) << "\n"
|
||||
<< "RC(0,1/4) = " << EllipticFunction::RC(0,0.25) << "\n"
|
||||
<< "RC(9/4,2) = " << EllipticFunction::RC(2.25,2) << "\n"
|
||||
<< "RC(1/4,-2) = " << EllipticFunction::RC(0.25,-2) << "\n"
|
||||
<< "RJ(0,1,2,3) = " << EllipticFunction::RJ(0,1,2,3) << "\n"
|
||||
<< "RJ(2,3,4,5) = " << EllipticFunction::RJ(2,3,4,5) << "\n"
|
||||
<< "RD(0,2,1) = " << EllipticFunction::RD(0,2,1) << "\n"
|
||||
<< "RD(2,3,4) = " << EllipticFunction::RD(2,3,4) << "\n"
|
||||
<< "RG(0,16,16) = " << EllipticFunction::RG(16,16) << "\n"
|
||||
<< "RG(2,3,4) = " << EllipticFunction::RG(2,3,4) << "\n"
|
||||
<< "RG(0,0.0796,4) = " << EllipticFunction::RG(0.0796,4) << "\n";
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cout << "Caught exception: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// Example of using the GeographicLib::GARS class
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <GeographicLib/GARS.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace GeographicLib;
|
||||
|
||||
int main() {
|
||||
try {
|
||||
{
|
||||
// Sample forward calculation
|
||||
double lat = 57.64911, lon = 10.40744; // Jutland
|
||||
string gars;
|
||||
for (int prec = 0; prec <= 2; ++prec) {
|
||||
GARS::Forward(lat, lon, prec, gars);
|
||||
cout << prec << " " << gars << "\n";
|
||||
}
|
||||
}
|
||||
{
|
||||
// Sample reverse calculation
|
||||
string gars = "381NH45";
|
||||
double lat, lon;
|
||||
cout << fixed;
|
||||
for (int len = 5; len <= int(gars.size()); ++len) {
|
||||
int prec;
|
||||
GARS::Reverse(gars.substr(0, len), lat, lon, prec);
|
||||
cout << prec << " " << lat << " " << lon << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "Caught exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 2395 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user