add GeographicLib

This commit is contained in:
Sven Czarnian
2021-11-22 16:16:36 +01:00
parent b1b6b5c790
commit 09e29afe7b
46 changed files with 16958 additions and 0 deletions

View File

@@ -0,0 +1,198 @@
/**
* \file Accumulator.hpp
* \brief Header for GeographicLib::Accumulator class
*
* Copyright (c) Charles Karney (2010-2020) <charles@karney.com> 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 &times; \e T is exactly representable as a \e T
* (i.e., &plusmn; powers of two). Use \e n = &minus;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

View File

@@ -0,0 +1,321 @@
/**
* \file AlbersEqualArea.hpp
* \brief Header for GeographicLib::AlbersEqualArea class
*
* Copyright (c) Charles Karney (2010-2021) <charles@karney.com> 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="http://pubs.er.usgs.gov/usgspubs/pp/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, &gamma;, 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 &gamma; degrees
* counter-clockwise 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; using std::abs; using 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(3), 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;
friend class Ellipsoid; // For access to txif, tphif, etc.
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 &minus; \e f) \e a, or \e k0 is
* not positive.
* @exception GeographicErr if \e stdlat is not in [&minus;90&deg;,
* 90&deg;].
**********************************************************************/
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 &minus; \e f) \e a, or \e k1 is
* not positive.
* @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in
* [&minus;90&deg;, 90&deg;], 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 &minus; \e f) \e a, or \e k1 is
* not positive.
* @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in
* [&minus;90&deg;, 90&deg;], 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 &minus; \e lat1) &le; 160&deg;,
* then the error in the latitude of origin is less than 4.5 &times;
* 10<sup>&minus;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 (&minus;90&deg;,
* 90&deg;).
*
* 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
* [&minus;90&deg;, 90&deg;]. 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 [&minus;180&deg;, 180&deg;]. The value of \e lat returned is
* in the range [&minus;90&deg;, 90&deg;]. 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; }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
/**
* 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&deg;, 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 = &minus;90&deg;, and \e k0 = 1. This degenerates to the
* Lambert azimuthal equal area projection.
**********************************************************************/
static const AlbersEqualArea& AzimuthalEqualAreaSouth();
};
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_ALBERSEQUALAREA_HPP

View File

@@ -0,0 +1,145 @@
/**
* \file AzimuthalEquidistant.hpp
* \brief Header for GeographicLib::AzimuthalEquidistant class
*
* Copyright (c) Charles Karney (2009-2020) <charles@karney.com> 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 [&minus;90&deg;, 90&deg;].
* 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 [&minus;90&deg;, 90&deg;]. \e lat will
* be in the range [&minus;90&deg;, 90&deg;] and \e lon will be in the
* range [&minus;180&deg;, 180&deg;]. 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(); }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
};
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_AZIMUTHALEQUIDISTANT_HPP

View File

@@ -0,0 +1,210 @@
/**
* \file CassiniSoldner.hpp
* \brief Header for GeographicLib::CassiniSoldner class
*
* Copyright (c) Charles Karney (2009-2020) <charles@karney.com> 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&deg; 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&deg;. \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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 [&minus;90&deg;, 90&deg;]. 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(); }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
};
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_CASSINISOLDNER_HPP

View File

@@ -0,0 +1,195 @@
/**
* \file CircularEngine.hpp
* \brief Header for GeographicLib::CircularEngine class
*
* Copyright (c) Charles Karney (2011-2015) <charles@karney.com> 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 _M;
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)
: _M(M)
, _gradp(gradp)
, _norm(norm)
, _a(a)
, _r(r)
, _u(u)
, _t(t)
, _wc(std::vector<real>(_M + 1, 0))
, _ws(std::vector<real>(_M + 1, 0))
, _wrc(std::vector<real>(_gradp ? _M + 1 : 0, 0))
, _wrs(std::vector<real>(_gradp ? _M + 1 : 0, 0))
, _wtc(std::vector<real>(_gradp ? _M + 1 : 0, 0))
, _wts(std::vector<real>(_gradp ? _M + 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()
: _M(-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

25
external/include/GeographicLib/Config.h vendored Normal file
View File

@@ -0,0 +1,25 @@
#define GEOGRAPHICLIB_VERSION_STRING "1.52"
#define GEOGRAPHICLIB_VERSION_MAJOR 1
#define GEOGRAPHICLIB_VERSION_MINOR 52
#define GEOGRAPHICLIB_VERSION_PATCH 0
#define GEOGRAPHICLIB_DATA "C:/Users/sven/Documents/Visual Studio 2019/Projects/TST/install"
// These are macros which affect the building of the library
#define GEOGRAPHICLIB_HAVE_LONG_DOUBLE 0
#define GEOGRAPHICLIB_WORDS_BIGENDIAN 0
#define GEOGRAPHICLIB_PRECISION 1
// 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 0
#endif

View File

@@ -0,0 +1,329 @@
/**
* \file Constants.hpp
* \brief Header for GeographicLib::Constants class
*
* Copyright (c) Charles Karney (2008-2020) <charles@karney.com> 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
// 1100 vc5
// 1200 vc6
// 1300 vc7
// 1310 vc7.1 (2003)
// 1400 vc8 (2005)
// 1500 vc9 (2008)
// 1600 vc10 (2010)
// 1700 vc11 (2012)
// 1800 vc12 (2013)
// 1900 vc14 (2015) First version of VS to include enough C++11 support
// 191[0-9] vc15 (2017)
// 192[0-9] vc16 (2019)
#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
// Use GEOGRAPHICLIB_DEPRECATED to mark functions, types or variables as
// deprecated. Code inspired by Apache Subversion's svn_types.h file (via
// MPFR).
#if defined(__GNUC__)
# if __GNUC__ > 4
# define GEOGRAPHICLIB_DEPRECATED(msg) __attribute__((deprecated(msg)))
# else
# define GEOGRAPHICLIB_DEPRECATED(msg) __attribute__((deprecated))
# endif
#elif defined(_MSC_VER) && _MSC_VER >= 1300
# define GEOGRAPHICLIB_DEPRECATED(msg) __declspec(deprecated(msg))
#else
# define GEOGRAPHICLIB_DEPRECATED(msg)
#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(); // 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() / 60; }
/**
* @return the number of radians in an arcsecond.
**********************************************************************/
static Math::real arcsecond()
{ return Math::degree() / 3600; }
/** \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>&minus;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, &omega;, in rad
* s<sup>&minus;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>&minus;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, &omega;, in rad
* s<sup>&minus;1</sup>.
*
* This is about 2 &pi; 366.25 / (365.25 &times; 24 &times; 3600) rad
* s<sup>&minus;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 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

405
external/include/GeographicLib/DMS.hpp vendored Normal file
View File

@@ -0,0 +1,405 @@
/**
* \file DMS.hpp
* \brief Header for GeographicLib::DMS class
*
* Copyright (c) Charles Karney (2008-2020) <charles@karney.com> 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 dll vs vector and constant conditional expressions
# pragma warning (push)
# pragma warning (disable: 4251 4127)
#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) {
std::string::size_type p = 0;
int count = c ? 1 : 0;
while (true) {
p = s.find(pat, p);
if (p == std::string::npos)
break;
s.replace(p, pat.length(), count, 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(); // 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), &quot; (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 &deg; 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 &quot;. 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 &quot; 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&quot; 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. 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&quot;S, -20&deg;30'40.5, -20d30.675,
* N-20d30'40.5&quot;, -20:30:40.5
* - 4d0'9, 4d9&quot;, 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&quot;4', 4::5, 4:5:, :4:5, 4d4.5'4&quot;, -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 (&deg;)
* - U+00ba masculine ordinal indicator (&ordm;)
* - 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 (&prime;)
* - U+2035 back prime ()
* - U+00b4 acute accent (&acute;)
* - U+2018 left single quote (&lsquo;)
* - U+2019 right single quote (&rsquo;)
* - U+201b reversed-9 single quote ()
* - U+02b9 modifier letter prime (ʹ)
* - U+02ca modifier letter acute accent (ˊ)
* - U+02cb modifier letter grave accent (ˋ)
* - seconds:
* - &quot; quotation mark
* - U+2033 double prime (&Prime;)
* - U+2036 reversed double prime (‶)
* + U+02dd double acute accent (˝)
* - U+201c left double quote (&ldquo;)
* - U+201d right double quote (&rdquo;)
* - U+201f reversed-9 double quote (‟)
* - U+02ba modifier letter double prime (ʺ)
* - '&nbsp;' 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 (&ndash;)
* - U+2014 em dash (&mdash;)
* - U+2212 minus sign (&minus;)
* - U+2796 heavy minus ()
* - ignored spaces:
* - U+00a0 non-breaking space
* - U+2007 figure space (||)
* - U+2009 thin space (|&thinsp;|)
* - 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 / 60) / 60; }
/**
* 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 [&minus;90&deg;,
* 90&deg;].
*
* 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 [&minus;180&deg;,
* 180&deg;].
*
* A hemisphere designator E/W can be used; the result is multiplied by
* &minus;1 if W is present.
**********************************************************************/
static Math::real DecodeAzimuth(const std::string& azistr);
/**
* Convert angle (in degrees) into a DMS string (using d, ', and &quot;).
*
* @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, ', &quot; 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&deg;), 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 &quot;) 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, ', &quot; 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&quot;. \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 = 60 * (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 = 60 * (ang - d);
m = int(ang); s = 60 * (ang - m);
}
};
} // namespace GeographicLib
#if defined(_MSC_VER)
# pragma warning (pop)
#endif
#endif // GEOGRAPHICLIB_DMS_HPP

View File

@@ -0,0 +1,542 @@
/**
* \file Ellipsoid.hpp
* \brief Header for GeographicLib::Ellipsoid class
*
* Copyright (c) Charles Karney (2012-2020) <charles@karney.com> 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/TransverseMercator.hpp>
#include <GeographicLib/EllipticFunction.hpp>
#include <GeographicLib/AlbersEqualArea.hpp>
namespace GeographicLib {
/**
* \brief Properties of an ellipsoid
*
* This class returns various properties of the ellipsoid and converts
* between various types of latitudes. The latitude conversions are also
* possible using the various projections supported by %GeographicLib; but
* Ellipsoid provides more direct access (sometimes using private functions
* of the projection classes). Ellipsoid::RectifyingLatitude,
* Ellipsoid::InverseRectifyingLatitude, and Ellipsoid::MeridianDistance
* provide functionality which can be provided by the Geodesic class.
* However Geodesic uses a series approximation (valid for abs \e f < 1/150),
* whereas Ellipsoid computes these quantities using EllipticFunction which
* provides accurate results even when \e f is large. Use of this class
* should be limited to &minus;3 < \e f < 3/4 (i.e., 1/4 < b/a < 4).
*
* 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, _f1, _f12, _e2, _es, _e12, _n, _b;
TransverseMercator _tm;
EllipticFunction _ell;
AlbersEqualArea _au;
// These are the alpha and beta coefficients in the Krueger series from
// TransverseMercator. Thy are used by RhumbSolve to compute
// (psi2-psi1)/(mu2-mu1).
const Math::real* ConformalToRectifyingCoeffs() const { return _tm._alp; }
const Math::real* RectifyingToConformalCoeffs() const { return _tm._bet; }
friend class Rhumb; friend class RhumbLine;
public:
/** \name Constructor
**********************************************************************/
///@{
/**
* Constructor for a 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 &minus; \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 semi-axis (meters).
**********************************************************************/
Math::real MinorRadius() const { return _b; }
/**
* @return \e L the distance between the equator and a pole along a
* meridian (meters). For a sphere \e L = (&pi;/2) \e a. The radius
* of a sphere with the same meridian length is \e L / (&pi;/2).
**********************************************************************/
Math::real QuarterMeridian() const;
/**
* @return \e A the total area of the ellipsoid (meters<sup>2</sup>). For
* a sphere \e A = 4&pi; <i>a</i><sup>2</sup>. The radius of a sphere
* with the same area is sqrt(\e A / (4&pi;)).
**********************************************************************/
Math::real Area() const;
/**
* @return \e V the total volume of the ellipsoid (meters<sup>3</sup>).
* For a sphere \e V = (4&pi; / 3) <i>a</i><sup>3</sup>. The radius of
* a sphere with the same volume is cbrt(\e V / (4&pi;/3)).
**********************************************************************/
Math::real Volume() const
{ return (4 * Math::pi()) * Math::sq(_a) * _b / 3; }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
/** \name %Ellipsoid shape
**********************************************************************/
///@{
/**
* @return \e f = (\e a &minus; \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 &minus; \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 &minus; \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> &minus;
* <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> &minus;
* <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> &minus;
* <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 &beta; the parametric latitude (degrees).
*
* The geographic latitude, &phi;, 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), &beta;,
* allows the cartesian coordinated of a meridian to be expressed
* conveniently in parametric form as
* - \e R = \e a cos &beta;
* - \e Z = \e b sin &beta;
* .
* where \e a and \e b are the equatorial radius and the polar semi-axis.
* For a sphere &beta; = &phi;.
*
* &phi; must lie in the range [&minus;90&deg;, 90&deg;]; the
* result is undefined if this condition does not hold. The returned value
* &beta; lies in [&minus;90&deg;, 90&deg;].
**********************************************************************/
Math::real ParametricLatitude(real phi) const;
/**
* @param[in] beta the parametric latitude (degrees).
* @return &phi; the geographic latitude (degrees).
*
* &beta; must lie in the range [&minus;90&deg;, 90&deg;]; the
* result is undefined if this condition does not hold. The returned value
* &phi; lies in [&minus;90&deg;, 90&deg;].
**********************************************************************/
Math::real InverseParametricLatitude(real beta) const;
/**
* @param[in] phi the geographic latitude (degrees).
* @return &theta; the geocentric latitude (degrees).
*
* The geocentric latitude, &theta;, 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 &theta; = &phi;.
*
* &phi; must lie in the range [&minus;90&deg;, 90&deg;]; the
* result is undefined if this condition does not hold. The returned value
* &theta; lies in [&minus;90&deg;, 90&deg;].
**********************************************************************/
Math::real GeocentricLatitude(real phi) const;
/**
* @param[in] theta the geocentric latitude (degrees).
* @return &phi; the geographic latitude (degrees).
*
* &theta; must lie in the range [&minus;90&deg;, 90&deg;]; the
* result is undefined if this condition does not hold. The returned value
* &phi; lies in [&minus;90&deg;, 90&deg;].
**********************************************************************/
Math::real InverseGeocentricLatitude(real theta) const;
/**
* @param[in] phi the geographic latitude (degrees).
* @return &mu; the rectifying latitude (degrees).
*
* The rectifying latitude, &mu;, has the property that the distance along
* a meridian of the ellipsoid between two points with rectifying latitudes
* &mu;<sub>1</sub> and &mu;<sub>2</sub> is equal to
* (&mu;<sub>2</sub> - &mu;<sub>1</sub>) \e L / 90&deg;,
* where \e L = QuarterMeridian(). For a sphere &mu; = &phi;.
*
* &phi; must lie in the range [&minus;90&deg;, 90&deg;]; the
* result is undefined if this condition does not hold. The returned value
* &mu; lies in [&minus;90&deg;, 90&deg;].
**********************************************************************/
Math::real RectifyingLatitude(real phi) const;
/**
* @param[in] mu the rectifying latitude (degrees).
* @return &phi; the geographic latitude (degrees).
*
* &mu; must lie in the range [&minus;90&deg;, 90&deg;]; the
* result is undefined if this condition does not hold. The returned value
* &phi; lies in [&minus;90&deg;, 90&deg;].
**********************************************************************/
Math::real InverseRectifyingLatitude(real mu) const;
/**
* @param[in] phi the geographic latitude (degrees).
* @return &xi; the authalic latitude (degrees).
*
* The authalic latitude, &xi;, has the property that the area of the
* ellipsoid between two circles with authalic latitudes
* &xi;<sub>1</sub> and &xi;<sub>2</sub> is equal to (sin
* &xi;<sub>2</sub> - sin &xi;<sub>1</sub>) \e A / 2, where \e A
* = Area(). For a sphere &xi; = &phi;.
*
* &phi; must lie in the range [&minus;90&deg;, 90&deg;]; the
* result is undefined if this condition does not hold. The returned value
* &xi; lies in [&minus;90&deg;, 90&deg;].
**********************************************************************/
Math::real AuthalicLatitude(real phi) const;
/**
* @param[in] xi the authalic latitude (degrees).
* @return &phi; the geographic latitude (degrees).
*
* &xi; must lie in the range [&minus;90&deg;, 90&deg;]; the
* result is undefined if this condition does not hold. The returned value
* &phi; lies in [&minus;90&deg;, 90&deg;].
**********************************************************************/
Math::real InverseAuthalicLatitude(real xi) const;
/**
* @param[in] phi the geographic latitude (degrees).
* @return &chi; the conformal latitude (degrees).
*
* The conformal latitude, &chi;, 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 &chi; = &phi;.
*
* &phi; must lie in the range [&minus;90&deg;, 90&deg;]; the
* result is undefined if this condition does not hold. The returned value
* &chi; lies in [&minus;90&deg;, 90&deg;].
**********************************************************************/
Math::real ConformalLatitude(real phi) const;
/**
* @param[in] chi the conformal latitude (degrees).
* @return &phi; the geographic latitude (degrees).
*
* &chi; must lie in the range [&minus;90&deg;, 90&deg;]; the
* result is undefined if this condition does not hold. The returned value
* &phi; lies in [&minus;90&deg;, 90&deg;].
**********************************************************************/
Math::real InverseConformalLatitude(real chi) const;
/**
* @param[in] phi the geographic latitude (degrees).
* @return &psi; 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 &psi; =
* sinh<sup>&minus;1</sup> tan &phi;.
*
* &phi; must lie in the range [&minus;90&deg;, 90&deg;]; the result is
* undefined if this condition does not hold. The value returned for &phi;
* = &plusmn;90&deg; is some (positive or negative) large but finite value,
* such that InverseIsometricLatitude returns the original value of &phi;.
**********************************************************************/
Math::real IsometricLatitude(real phi) const;
/**
* @param[in] psi the isometric latitude (degrees).
* @return &phi; the geographic latitude (degrees).
*
* The returned value &phi; lies in [&minus;90&deg;, 90&deg;]. For a
* sphere &phi; = tan<sup>&minus;1</sup> sinh &psi;.
**********************************************************************/
Math::real InverseIsometricLatitude(real psi) const;
///@}
/** \name Other quantities.
**********************************************************************/
///@{
/**
* @param[in] phi the geographic latitude (degrees).
* @return \e R = \e a cos &beta; the radius of a circle of latitude
* &phi; (meters). \e R (&pi;/180&deg;) gives meters per degree
* longitude measured along a circle of latitude.
*
* &phi; must lie in the range [&minus;90&deg;, 90&deg;]; 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 &beta; the distance of a circle of latitude
* &phi; from the equator measured parallel to the ellipsoid axis
* (meters).
*
* &phi; must lie in the range [&minus;90&deg;, 90&deg;]; 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 &phi; (meters). \e s is
* given by \e s = &mu; \e L / 90&deg;, where \e L =
* QuarterMeridian()).
*
* &phi; must lie in the range [&minus;90&deg;, 90&deg;]; the
* result is undefined if this condition does not hold.
**********************************************************************/
Math::real MeridianDistance(real phi) const;
/**
* @param[in] phi the geographic latitude (degrees).
* @return &rho; the meridional radius of curvature of the ellipsoid at
* latitude &phi; (meters); this is the curvature of the meridian. \e
* rho is given by &rho; = (180&deg;/&pi;) d\e s / d&phi;,
* where \e s = MeridianDistance(); thus &rho; (&pi;/180&deg;)
* gives meters per degree latitude measured along a meridian.
*
* &phi; must lie in the range [&minus;90&deg;, 90&deg;]; the
* result is undefined if this condition does not hold.
**********************************************************************/
Math::real MeridionalCurvatureRadius(real phi) const;
/**
* @param[in] phi the geographic latitude (degrees).
* @return &nu; the transverse radius of curvature of the ellipsoid at
* latitude &phi; (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. &nu; is related to \e R = CircleRadius() by \e
* R = &nu; cos &phi;.
*
* &phi; must lie in the range [&minus;90&deg;, 90&deg;]; 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 &phi; inclined at an angle \e azi to the
* meridian (meters).
*
* &phi; must lie in the range [&minus;90&deg;, 90&deg;]; 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 &minus; \e b) / \e b, the second
* flattening.
* @return \e f = (\e a &minus; \e b) / \e a, the flattening.
*
* \e f ' should lie in (&minus;1, &infin;).
* The returned value \e f lies in (&minus;&infin;, 1).
**********************************************************************/
static Math::real SecondFlatteningToFlattening(real fp)
{ return fp / (1 + fp); }
/**
* @param[in] f = (\e a &minus; \e b) / \e a, the flattening.
* @return \e f ' = (\e a &minus; \e b) / \e b, the second flattening.
*
* \e f should lie in (&minus;&infin;, 1).
* The returned value \e f ' lies in (&minus;1, &infin;).
**********************************************************************/
static Math::real FlatteningToSecondFlattening(real f)
{ return f / (1 - f); }
/**
* @param[in] n = (\e a &minus; \e b) / (\e a + \e b), the third
* flattening.
* @return \e f = (\e a &minus; \e b) / \e a, the flattening.
*
* \e n should lie in (&minus;1, 1).
* The returned value \e f lies in (&minus;&infin;, 1).
**********************************************************************/
static Math::real ThirdFlatteningToFlattening(real n)
{ return 2 * n / (1 + n); }
/**
* @param[in] f = (\e a &minus; \e b) / \e a, the flattening.
* @return \e n = (\e a &minus; \e b) / (\e a + \e b), the third
* flattening.
*
* \e f should lie in (&minus;&infin;, 1).
* The returned value \e n lies in (&minus;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> &minus;
* <i>b</i><sup>2</sup>) / <i>a</i><sup>2</sup>, the eccentricity
* squared.
* @return \e f = (\e a &minus; \e b) / \e a, the flattening.
*
* <i>e</i><sup>2</sup> should lie in (&minus;&infin;, 1).
* The returned value \e f lies in (&minus;&infin;, 1).
**********************************************************************/
static Math::real EccentricitySqToFlattening(real e2)
{ using std::sqrt; return e2 / (sqrt(1 - e2) + 1); }
/**
* @param[in] f = (\e a &minus; \e b) / \e a, the flattening.
* @return <i>e</i><sup>2</sup> = (<i>a</i><sup>2</sup> &minus;
* <i>b</i><sup>2</sup>) / <i>a</i><sup>2</sup>, the eccentricity
* squared.
*
* \e f should lie in (&minus;&infin;, 1).
* The returned value <i>e</i><sup>2</sup> lies in (&minus;&infin;, 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> &minus;
* <i>b</i><sup>2</sup>) / <i>b</i><sup>2</sup>, the second eccentricity
* squared.
* @return \e f = (\e a &minus; \e b) / \e a, the flattening.
*
* <i>e'</i> <sup>2</sup> should lie in (&minus;1, &infin;).
* The returned value \e f lies in (&minus;&infin;, 1).
**********************************************************************/
static Math::real SecondEccentricitySqToFlattening(real ep2)
{ using std::sqrt; return ep2 / (sqrt(1 + ep2) + 1 + ep2); }
/**
* @param[in] f = (\e a &minus; \e b) / \e a, the flattening.
* @return <i>e'</i> <sup>2</sup> = (<i>a</i><sup>2</sup> &minus;
* <i>b</i><sup>2</sup>) / <i>b</i><sup>2</sup>, the second eccentricity
* squared.
*
* \e f should lie in (&minus;&infin;, 1).
* The returned value <i>e'</i> <sup>2</sup> lies in (&minus;1, &infin;).
**********************************************************************/
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>
* &minus; <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 &minus; \e b) / \e a, the flattening.
*
* <i>e''</i> <sup>2</sup> should lie in (&minus;1, 1).
* The returned value \e f lies in (&minus;&infin;, 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 &minus; \e b) / \e a, the flattening.
* @return <i>e''</i> <sup>2</sup> = (<i>a</i><sup>2</sup> &minus;
* <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 (&minus;&infin;, 1).
* The returned value <i>e''</i> <sup>2</sup> lies in (&minus;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

View File

@@ -0,0 +1,702 @@
/**
* \file EllipticFunction.hpp
* \brief Header for GeographicLib::EllipticFunction class
*
* Copyright (c) Charles Karney (2008-2021) <charles@karney.com> 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 &alpha;<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> &lt; 0). (Confusingly, Abramowitz and
* Stegun call \e m = <i>k</i><sup>2</sup> the "parameter" and \e n =
* &alpha;<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 &delta;\e E(&phi;, \e k) is an odd periodic function with period
* &pi;.
*
* 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)
* .
* 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_ = 13 }; // Max depth required for sncndn; probably 5 is enough.
real _k2, _kp2, _alpha2, _alphap2, _eps;
real _Kc, _Ec, _Dc, _Pic, _Gc, _Hc;
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 (&minus;&infin;, 1].
* @param[in] alpha2 the parameter &alpha;<sup>2</sup>.
* &alpha;<sup>2</sup> must lie in (&minus;&infin;, 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 &alpha;<sup>2</sup> = 0 (the default value); in this case, we
* have &Pi;(&phi;, 0, \e k) = \e F(&phi;, \e k), \e G(&phi;, 0, \e k) = \e
* E(&phi;, \e k), and \e H(&phi;, 0, \e k) = \e F(&phi;, \e k) - \e
* D(&phi;, \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 (&minus;&infin;, 1].
* @param[in] alpha2 the parameter &alpha;<sup>2</sup>.
* &alpha;<sup>2</sup> must lie in (&minus;&infin;, 1].
* @param[in] kp2 the complementary modulus squared <i>k'</i><sup>2</sup> =
* 1 &minus; <i>k</i><sup>2</sup>. This must lie in [0, &infin;).
* @param[in] alphap2 the complementary parameter &alpha;'<sup>2</sup> = 1
* &minus; &alpha;<sup>2</sup>. This must lie in [0, &infin;).
* @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 (&minus;&infin;, ].
* done.)
* @param[in] alpha2 the new value of parameter &alpha;<sup>2</sup>.
* &alpha;<sup>2</sup> must lie in (&minus;&infin;, 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 (&minus;&infin;, 1].
* @param[in] alpha2 the parameter &alpha;<sup>2</sup>.
* &alpha;<sup>2</sup> must lie in (&minus;&infin;, 1].
* @param[in] kp2 the complementary modulus squared <i>k'</i><sup>2</sup> =
* 1 &minus; <i>k</i><sup>2</sup>. This must lie in [0, &infin;).
* @param[in] alphap2 the complementary parameter &alpha;'<sup>2</sup> = 1
* &minus; &alpha;<sup>2</sup>. This must lie in [0, &infin;).
* @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 &minus; <i>k</i><sup>2</sup>.
**********************************************************************/
Math::real kp2() const { return _kp2; }
/**
* @return the parameter &alpha;<sup>2</sup>.
**********************************************************************/
Math::real alpha2() const { return _alpha2; }
/**
* @return the complementary parameter &alpha;'<sup>2</sup> = 1 &minus;
* &alpha;<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 _Kc; }
/**
* 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 _Ec; }
/**
* 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 _Dc; }
/**
* The difference between the complete integrals of the first and second
* kinds.
*
* @return \e K(\e k) &minus; \e E(\e k).
**********************************************************************/
Math::real KE() const { return _k2 * _Dc; }
/**
* The complete integral of the third kind.
*
* @return &Pi;(&alpha;<sup>2</sup>, \e k).
*
* &Pi;(&alpha;<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 _Pic; }
/**
* Legendre's complete geodesic longitude integral.
*
* @return \e G(&alpha;<sup>2</sup>, \e k).
*
* \e G(&alpha;<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 _Gc; }
/**
* Cayley's complete geodesic longitude difference integral.
*
* @return \e H(&alpha;<sup>2</sup>, \e k).
*
* \e H(&alpha;<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 _Hc; }
///@}
/** \name Incomplete elliptic integrals.
**********************************************************************/
///@{
/**
* The incomplete integral of the first kind.
*
* @param[in] phi
* @return \e F(&phi;, \e k).
*
* \e F(&phi;, \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(&phi;, \e k).
*
* \e E(&phi;, \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(&pi; <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 &phi; = <i>E</i><sup>&minus;1</sup>(\e x, \e k); i.e., the
* solution of such that \e E(&phi;, \e k) = \e x.
**********************************************************************/
Math::real Einv(real x) const;
/**
* The incomplete integral of the third kind.
*
* @param[in] phi
* @return &Pi;(&phi;, &alpha;<sup>2</sup>, \e k).
*
* &Pi;(&phi;, &alpha;<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(&phi;, \e k).
*
* \e D(&phi;, \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(&phi;, &alpha;<sup>2</sup>, \e k).
*
* \e G(&phi;, &alpha;<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&eacute;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(&phi;, &alpha;<sup>2</sup>, \e k).
*
* \e H(&phi;, &alpha;<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&phi;.
* @param[in] cn = cos&phi;.
* @param[in] dn = sqrt(1 &minus; <i>k</i><sup>2</sup>
* sin<sup>2</sup>&phi;).
* @return \e F(&phi;, \e k) as though &phi; &isin; (&minus;&pi;, &pi;].
**********************************************************************/
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&phi;.
* @param[in] cn = cos&phi;.
* @param[in] dn = sqrt(1 &minus; <i>k</i><sup>2</sup>
* sin<sup>2</sup>&phi;).
* @return \e E(&phi;, \e k) as though &phi; &isin; (&minus;&pi;, &pi;].
**********************************************************************/
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&phi;.
* @param[in] cn = cos&phi;.
* @param[in] dn = sqrt(1 &minus; <i>k</i><sup>2</sup>
* sin<sup>2</sup>&phi;).
* @return &Pi;(&phi;, &alpha;<sup>2</sup>, \e k) as though &phi; &isin;
* (&minus;&pi;, &pi;].
**********************************************************************/
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&phi;.
* @param[in] cn = cos&phi;.
* @param[in] dn = sqrt(1 &minus; <i>k</i><sup>2</sup>
* sin<sup>2</sup>&phi;).
* @return \e D(&phi;, \e k) as though &phi; &isin; (&minus;&pi;, &pi;].
**********************************************************************/
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&phi;.
* @param[in] cn = cos&phi;.
* @param[in] dn = sqrt(1 &minus; <i>k</i><sup>2</sup>
* sin<sup>2</sup>&phi;).
* @return \e G(&phi;, &alpha;<sup>2</sup>, \e k) as though &phi; &isin;
* (&minus;&pi;, &pi;].
**********************************************************************/
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&phi;.
* @param[in] cn = cos&phi;.
* @param[in] dn = sqrt(1 &minus; <i>k</i><sup>2</sup>
* sin<sup>2</sup>&phi;).
* @return \e H(&phi;, &alpha;<sup>2</sup>, \e k) as though &phi; &isin;
* (&minus;&pi;, &pi;].
**********************************************************************/
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&phi;.
* @param[in] cn = cos&phi;.
* @param[in] dn = sqrt(1 &minus; <i>k</i><sup>2</sup>
* sin<sup>2</sup>&phi;).
* @return the periodic function &pi; \e F(&phi;, \e k) / (2 \e K(\e k)) -
* &phi;.
**********************************************************************/
Math::real deltaF(real sn, real cn, real dn) const;
/**
* The periodic incomplete integral of the second kind.
*
* @param[in] sn = sin&phi;.
* @param[in] cn = cos&phi;.
* @param[in] dn = sqrt(1 &minus; <i>k</i><sup>2</sup>
* sin<sup>2</sup>&phi;).
* @return the periodic function &pi; \e E(&phi;, \e k) / (2 \e E(\e k)) -
* &phi;.
**********************************************************************/
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&tau;.
* @param[in] ctau = sin&tau;.
* @return the periodic function <i>E</i><sup>&minus;1</sup>(&tau; (2 \e
* E(\e k)/&pi;), \e k) - &tau;.
**********************************************************************/
Math::real deltaEinv(real stau, real ctau) const;
/**
* The periodic incomplete integral of the third kind.
*
* @param[in] sn = sin&phi;.
* @param[in] cn = cos&phi;.
* @param[in] dn = sqrt(1 &minus; <i>k</i><sup>2</sup>
* sin<sup>2</sup>&phi;).
* @return the periodic function &pi; &Pi;(&phi;, &alpha;<sup>2</sup>,
* \e k) / (2 &Pi;(&alpha;<sup>2</sup>, \e k)) - &phi;.
**********************************************************************/
Math::real deltaPi(real sn, real cn, real dn) const;
/**
* The periodic Jahnke's incomplete elliptic integral.
*
* @param[in] sn = sin&phi;.
* @param[in] cn = cos&phi;.
* @param[in] dn = sqrt(1 &minus; <i>k</i><sup>2</sup>
* sin<sup>2</sup>&phi;).
* @return the periodic function &pi; \e D(&phi;, \e k) / (2 \e D(\e k)) -
* &phi;.
**********************************************************************/
Math::real deltaD(real sn, real cn, real dn) const;
/**
* Legendre's periodic geodesic longitude integral.
*
* @param[in] sn = sin&phi;.
* @param[in] cn = cos&phi;.
* @param[in] dn = sqrt(1 &minus; <i>k</i><sup>2</sup>
* sin<sup>2</sup>&phi;).
* @return the periodic function &pi; \e G(&phi;, \e k) / (2 \e G(\e k)) -
* &phi;.
**********************************************************************/
Math::real deltaG(real sn, real cn, real dn) const;
/**
* Cayley's periodic geodesic longitude difference integral.
*
* @param[in] sn = sin&phi;.
* @param[in] cn = cos&phi;.
* @param[in] dn = sqrt(1 &minus; <i>k</i><sup>2</sup>
* sin<sup>2</sup>&phi;).
* @return the periodic function &pi; \e H(&phi;, \e k) / (2 \e H(\e k)) -
* &phi;.
**********************************************************************/
Math::real deltaH(real sn, real cn, real dn) const;
///@}
/** \name Elliptic functions.
**********************************************************************/
///@{
/**
* 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).
**********************************************************************/
void sncndn(real x, real& sn, real& cn, real& dn) const;
/**
* The &Delta; amplitude function.
*
* @param[in] sn sin&phi;.
* @param[in] cn cos&phi;.
* @return &Delta; = sqrt(1 &minus; <i>k</i><sup>2</sup>
* sin<sup>2</sup>&phi;).
**********************************************************************/
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]
* 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).
**********************************************************************/
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]
**********************************************************************/
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]
* See also https://dlmf.nist.gov/19.16.E3.
* 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).
**********************************************************************/
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]
**********************************************************************/
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]
**********************************************************************/
static real RD(real x, real y, real z);
///@}
};
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_ELLIPTICFUNCTION_HPP

143
external/include/GeographicLib/GARS.hpp vendored Normal file
View File

@@ -0,0 +1,143 @@
/**
* \file GARS.hpp
* \brief Header for GeographicLib::GARS class
*
* Copyright (c) Charles Karney (2015-2021) <charles@karney.com> 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>
#if defined(_MSC_VER)
// Squelch warnings about dll vs string
# pragma warning (push)
# pragma warning (disable: 4251)
#endif
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_;
enum {
lonorig_ = -180, // Origin for longitude
latorig_ = -90, // Origin for latitude
baselon_ = 10, // Base for longitude tiles
baselat_ = 24, // Base for latitude tiles
lonlen_ = 3,
latlen_ = 2,
baselen_ = lonlen_ + latlen_,
mult1_ = 2, // base precision = 1/2 degree
mult2_ = 2, // 6th char gives 2x more precision
mult3_ = 3, // 7th char gives 3x more precision
m_ = mult1_ * mult2_ * mult3_,
maxprec_ = 2,
maxlen_ = baselen_ + maxprec_,
};
GARS(); // 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 [&minus;90&deg;,
* 90&deg;].
* @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::abs; res = abs(res);
for (int prec = 0; prec < maxprec_; ++prec)
if (Resolution(prec) <= res)
return prec;
return maxprec_;
}
};
} // namespace GeographicLib
#if defined(_MSC_VER)
# pragma warning (pop)
#endif
#endif // GEOGRAPHICLIB_GARS_HPP

View File

@@ -0,0 +1,553 @@
/**
* \file GeoCoords.hpp
* \brief Header for GeographicLib::GeoCoords class
*
* Copyright (c) Charles Karney (2008-2020) <charles@karney.com> 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&quot;E 33d26'24&quot;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 &quot; 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&quot;
* - 40d30'30
* - 40&deg;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) = &minus;1.5. However, note
* that -1:30-0:0:15 is parsed as (-1:30) + (-0:0:15) = &minus;(1+30/60)
* &minus; (15/3600). Latitudes must be in the range [&minus;90&deg;,
* 90&deg;]. Internally longitudes are reduced to the range
* [&minus;180&deg;, 180&deg;].
*
* <b>UTM/UPS parsing</b>: For UTM zones (&minus;80&deg; &le; Lat <
* 84&deg;), 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 [&minus;90&deg;,
* 90&deg;].
* @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 [&minus;90&deg;,
* 90&deg;].
* @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 = longitude;
if (_long >= 180) _long -= 360;
else if (_long < -180) _long += 360;
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 = &minus;5 (min), 1&deg;
* - prec = 0, 10<sup>&minus;5</sup>&deg; (about 1m)
* - prec = 3, 10<sup>&minus;8</sup>&deg;
* - prec = 9 (max), 10<sup>&minus;14</sup>&deg;
**********************************************************************/
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, ', &quot; 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 = &minus;5 (min), 1&deg;
* - prec = &minus;4, 0.1&deg;
* - prec = &minus;3, 1'
* - prec = &minus;2, 0.1'
* - prec = &minus;1, 1&quot;
* - prec = 0, 0.1&quot; (about 3m)
* - prec = 1, 0.01&quot;
* - prec = 10 (max), 10<sup>&minus;11</sup>&quot;
**********************************************************************/
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 &minus;2 (100m) is 38SMB441847 and not
* 38SMB442848. \e prec specifies the precision of the MGRS string as
* follows:
* - prec = &minus;6 (min), only the grid zone is returned, e.g., 38S
* - prec = &minus;5, 100km, e.g., 38SMB
* - prec = &minus;4, 10km
* - prec = &minus;3, 1km
* - prec = &minus;2, 100m
* - prec = &minus;1, 10m
* - prec = 0, 1m
* - prec = 1, 0.1m
* - prec = 6 (max), 1&mu;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 = &minus;5 (min), 100km
* - prec = &minus;3, 1km
* - prec = 0, 1m
* - prec = 3, 1mm
* - prec = 6, 1&mu;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(); }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
};
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_GEOCOORDS_HPP

View File

@@ -0,0 +1,274 @@
/**
* \file Geocentric.hpp
* \brief Header for GeographicLib::Geocentric class
*
* Copyright (c) Charles Karney (2008-2020) <charles@karney.com> 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&deg;. 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 a 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 &minus; \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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 &sdot; \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
* &ge; &minus; \e a (1 &minus; <i>e</i><sup>2</sup>) / sqrt(1 &minus;
* <i>e</i><sup>2</sup> sin<sup>2</sup>\e lat). The value of \e lon
* returned is in the range [&minus;180&deg;, 180&deg;].
**********************************************************************/
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> &sdot; \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(); }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
/**
* A global instantiation of Geocentric with the parameters for the WGS84
* ellipsoid.
**********************************************************************/
static const Geocentric& WGS84();
};
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_GEOCENTRIC_HPP

View File

@@ -0,0 +1,977 @@
/**
* \file Geodesic.hpp
* \brief Header for GeographicLib::Geodesic class
*
* Copyright (c) Charles Karney (2009-2020) <charles@karney.com> 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>
#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 a 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 if
* labeled &phi;, longitude &lambda; (with &lambda;<sub>12</sub> =
* &lambda;<sub>2</sub> &minus; &lambda;<sub>1</sub>), and azimuth &alpha;.
*
* <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&deg; 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&deg;.
*
* 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&deg;. 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
* counter-clockwise, 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 &minus; (1 &minus; \e M12 \e M21) \e m23 / \e m12
* - \e M31 = \e M32 \e M21 &minus; (1 &minus; \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 = &minus;\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] &rarr; [\e
* azi2, \e azi1], [\e M12, \e M21] &rarr; [\e M21, \e M12], \e S12 &rarr;
* &minus;\e S12. (This occurs when the longitude difference is near
* &plusmn;180&deg; for oblate ellipsoids.)
* - \e lon2 = \e lon1 &plusmn; 180&deg; (with neither point at a pole). If
* \e azi1 = 0&deg; or &plusmn;180&deg;, the geodesic is unique. Otherwise
* there are two geodesics and the second one is obtained by setting [\e
* azi1, \e azi2] &rarr; [&minus;\e azi1, &minus;\e azi2], \e S12 &rarr;
* &minus;\e S12. (This occurs when \e lat2 is near &minus;\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] &rarr; [\e azi1, \e
* azi2] + [\e d, &minus;\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] &rarr;
* [\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. 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>| &lt; 0.02; however reasonably accurate results will be
* obtained for |<i>f</i>| &lt; 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, use GeodesicExact instead.
*
* 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-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_;
enum captype {
CAP_NONE = 0U,
CAP_C1 = 1U<<0,
CAP_C1p = 1U<<1,
CAP_C2 = 1U<<2,
CAP_C3 = 1U<<3,
CAP_C4 = 1U<<4,
CAP_ALL = 0x1FU,
CAP_MASK = CAP_ALL,
OUT_ALL = 0x7F80U,
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, _f1, _e2, _ep2, _n, _b, _c2, _etol2;
real _A3x[nA3x_], _C3x[nC3x_], _C4x[nC4x_];
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::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,
/**
* 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. (LONG_UNROLL is not
* included in this mask.)
* @hideinitializer
**********************************************************************/
ALL = OUT_ALL| CAP_ALL,
};
/** \name Constructor
**********************************************************************/
///@{
/**
* Constructor for a 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 &minus; \e f) \e a is not
* positive.
**********************************************************************/
Geodesic(real a, real f);
///@}
/** \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 [&minus;90&deg;, 90&deg;]. The values of
* \e lon2 and \e azi2 returned are in the range [&minus;180&deg;,
* 180&deg;].
*
* If either point is at a pole, the azimuth is defined by keeping the
* longitude fixed, writing \e lat = &plusmn;(90&deg; &minus; &epsilon;),
* and taking the limit &epsilon; &rarr; 0+. An arc length greater that
* 180&deg; 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&deg;.)
*
* 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 [&minus;90&deg;, 90&deg;]. The values of
* \e lon2 and \e azi2 returned are in the range [&minus;180&deg;,
* 180&deg;].
*
* If either point is at a pole, the azimuth is defined by keeping the
* longitude fixed, writing \e lat = &plusmn;(90&deg; &minus; &epsilon;),
* and taking the limit &epsilon; &rarr; 0+. An arc length greater that
* 180&deg; 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&deg;.)
*
* 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 [&minus;180&deg;, 180&deg;].
* .
* 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 &minus; \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 [&minus;90&deg;, 90&deg;].
* The values of \e azi1 and \e azi2 returned are in the range
* [&minus;180&deg;, 180&deg;].
*
* If either point is at a pole, the azimuth is defined by keeping the
* longitude fixed, writing \e lat = &plusmn;(90&deg; &minus; &epsilon;),
* and taking the limit &epsilon; &rarr; 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.
**********************************************************************/
///@{
/**
* 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 [&minus;90&deg;, 90&deg;].
*
* 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 = &plusmn;(90 &minus; &epsilon;), and taking the
* limit &epsilon; &rarr; 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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 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; }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
/**
* A global instantiation of Geodesic with the parameters for the WGS84
* ellipsoid.
**********************************************************************/
static const Geodesic& WGS84();
};
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_GEODESIC_HPP

View File

@@ -0,0 +1,869 @@
/**
* \file GeodesicExact.hpp
* \brief Header for GeographicLib::GeodesicExact class
*
* Copyright (c) Charles Karney (2012-2020) <charles@karney.com> 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>
#if !defined(GEOGRAPHICLIB_GEODESICEXACT_ORDER)
/**
* The order of the expansions used by GeodesicExact.
**********************************************************************/
# define GEOGRAPHICLIB_GEODESICEXACT_ORDER 30
#endif
namespace GeographicLib {
class GeodesicLineExact;
/**
* \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 &isin; [-0.01, 0.01]. The GeodesicExact class computes the
* ellitpic 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 &isin; [0.01, 100] or \e f &isin; [&minus;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
* &minus; \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 computation of the area in these classes is via a 30th order series.
* This gives accurate results for <i>b</i>/\e a &isin; [1/2, 2]; the
* accuracy is about 8 decimal digits for <i>b</i>/\e a &isin; [1/4, 4].
*
* See \ref geodellip for the formulation. See the documentation on the
* Geodesic class for additional information on the geodesic problems.
*
* 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;
static const int nC4_ = GEOGRAPHICLIB_GEODESICEXACT_ORDER;
static const int nC4x_ = (nC4_ * (nC4_ + 1)) / 2;
static const unsigned maxit1_ = 20;
unsigned maxit2_;
real tiny_, tol0_, tol1_, tol2_, tolb_, xthresh_;
enum captype {
CAP_NONE = 0U,
CAP_E = 1U<<0,
// Skip 1U<<1 for compatibility with Geodesic (not required)
CAP_D = 1U<<2,
CAP_H = 1U<<3,
CAP_C4 = 1U<<4,
CAP_ALL = 0x1FU,
CAP_MASK = CAP_ALL,
OUT_ALL = 0x7F80U,
OUT_MASK = 0xFF80U, // Includes LONG_UNROLL
};
static real CosSeries(real sinx, real cosx, const real c[], int n);
static real Astroid(real x, real y);
real _a, _f, _f1, _e2, _ep2, _n, _b, _c2, _etol2;
real _C4x[nC4x_];
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;
// These are Maxima generated functions to provide series approximations to
// the integrals for the area.
void C4coeff();
void C4f(real k2, real c[]) const;
// Large coefficients are split so that lo contains the low 52 bits and hi
// the rest. This choice avoids double rounding with doubles and higher
// precision types. float coefficients will suffer double rounding;
// however the accuracy is already lousy for floats.
static Math::real reale(long long hi, long long lo) {
using std::ldexp;
return ldexp(real(hi), 52) + lo;
}
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,
/**
* 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. (LONG_UNROLL is not
* included in this mask.)
* @hideinitializer
**********************************************************************/
ALL = OUT_ALL| CAP_ALL,
};
/** \name Constructor
**********************************************************************/
///@{
/**
* Constructor for a 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 &minus; \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 [&minus;90&deg;, 90&deg;]. The values of
* \e lon2 and \e azi2 returned are in the range [&minus;180&deg;,
* 180&deg;].
*
* If either point is at a pole, the azimuth is defined by keeping the
* longitude fixed, writing \e lat = &plusmn;(90&deg; &minus; &epsilon;),
* and taking the limit &epsilon; &rarr; 0+. An arc length greater that
* 180&deg; 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&deg;.)
*
* 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 [&minus;90&deg;, 90&deg;]. The values of
* \e lon2 and \e azi2 returned are in the range [&minus;180&deg;,
* 180&deg;].
*
* If either point is at a pole, the azimuth is defined by keeping the
* longitude fixed, writing \e lat = &plusmn;(90&deg; &minus; &epsilon;),
* and taking the limit &epsilon; &rarr; 0+. An arc length greater that
* 180&deg; 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&deg;.)
*
* 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 [&minus;180&deg;, 180&deg;].
* .
* 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
* &minus; \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 [&minus;90&deg;, 90&deg;].
* The values of \e azi1 and \e azi2 returned are in the range
* [&minus;180&deg;, 180&deg;].
*
* If either point is at a pole, the azimuth is defined by keeping the
* longitude fixed, writing \e lat = &plusmn;(90&deg; &minus; &epsilon;),
* and taking the limit &epsilon; &rarr; 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.
**********************************************************************/
///@{
/**
* 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 [&minus;90&deg;, 90&deg;].
*
* 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 = &plusmn;(90 &minus; &epsilon;), and taking the
* limit &epsilon; &rarr; 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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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; }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
/**
* A global instantiation of GeodesicExact with the parameters for the
* WGS84 ellipsoid.
**********************************************************************/
static const GeodesicExact& WGS84();
};
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_GEODESICEXACT_HPP

View File

@@ -0,0 +1,708 @@
/**
* \file GeodesicLine.hpp
* \brief Header for GeographicLib::GeodesicLine class
*
* Copyright (c) Charles Karney (2009-2020) <charles@karney.com> 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>
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. 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>| &lt; 0.02; however reasonably accurate results will be
* obtained for |<i>f</i>| &lt; 0.2. For very eccentric ellipsoids, use
* GeodesicLineExact instead.
*
* 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, _b, _c2, _f1, _salp0, _calp0, _k2,
_salp1, _calp1, _ssig1, _csig1, _dn1, _stau1, _ctau1, _somg1, _comg1,
_A1m1, _A2m1, _A3c, _B11, _B21, _B31, _A4, _B41;
real _a13, _s13;
// index zero elements of _C1a, _C1pa, _C2a, _C3a are unused
real _C1a[nC1_ + 1], _C1pa[nC1p_ + 1], _C2a[nC2_ + 1], _C3a[nC3_],
_C4a[nC4_]; // all the elements of _C4a are used
unsigned _caps;
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);
enum captype {
CAP_NONE = Geodesic::CAP_NONE,
CAP_C1 = Geodesic::CAP_C1,
CAP_C1p = Geodesic::CAP_C1p,
CAP_C2 = Geodesic::CAP_C2,
CAP_C3 = Geodesic::CAP_C3,
CAP_C4 = Geodesic::CAP_C4,
CAP_ALL = Geodesic::CAP_ALL,
CAP_MASK = Geodesic::CAP_MASK,
OUT_ALL = Geodesic::OUT_ALL,
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,
/**
* 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. (LONG_UNROLL is not
* included in this mask.)
* @hideinitializer
**********************************************************************/
ALL = Geodesic::ALL,
};
/** \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 [&minus;90&deg;, 90&deg;].
*
* 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 = &plusmn;(90&deg; &minus; &epsilon;), and taking
* the limit &epsilon; &rarr; 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
* [&minus;180&deg;, 180&deg;].
*
* 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
* [&minus;180&deg;, 180&deg;].
*
* 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 [&minus;180&deg;, 180&deg;].
* .
* 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 &minus;
* \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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 (&minus;180&deg;, 180&deg;].
**********************************************************************/
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 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); }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
};
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_GEODESICLINE_HPP

View File

@@ -0,0 +1,673 @@
/**
* \file GeodesicLineExact.hpp
* \brief Header for GeographicLib::GeodesicLineExact class
*
* Copyright (c) Charles Karney (2012-2020) <charles@karney.com> 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>
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;
static const int nC4_ = GeodesicExact::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,
_A4, _B41, _E0, _D0, _H0, _E1, _D1, _H1;
real _a13, _s13;
real _C4a[nC4_]; // all the elements of _C4a are used
EllipticFunction _E;
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);
enum captype {
CAP_NONE = GeodesicExact::CAP_NONE,
CAP_E = GeodesicExact::CAP_E,
CAP_D = GeodesicExact::CAP_D,
CAP_H = GeodesicExact::CAP_H,
CAP_C4 = GeodesicExact::CAP_C4,
CAP_ALL = GeodesicExact::CAP_ALL,
CAP_MASK = GeodesicExact::CAP_MASK,
OUT_ALL = GeodesicExact::OUT_ALL,
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,
/**
* 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,
};
/** \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 [&minus;90&deg;, 90&deg;].
*
* 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 = &plusmn;(90&deg; &minus; &epsilon;), and taking
* the limit &epsilon; &rarr; 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
* [&minus;180&deg;, 180&deg;].
*
* 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
* [&minus;180&deg;, 180&deg;].
*
* 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 [&minus;180&deg;, 180&deg;].
* .
* 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
* &minus; \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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 (&minus;180&deg;, 180&deg;].
**********************************************************************/
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); }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
};
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_GEODESICLINEEXACT_HPP

View File

@@ -0,0 +1,180 @@
/**
* \file Geohash.hpp
* \brief Header for GeographicLib::Geohash class
*
* Copyright (c) Charles Karney (2012-2017) <charles@karney.com> 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>
#if defined(_MSC_VER)
// Squelch warnings about dll vs string
# pragma warning (push)
# pragma warning (disable: 4251)
#endif
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(); // 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 [&minus;90&deg;,
* 90&deg;].
* @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&mu;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&mu;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(180), -(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(360), -(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::abs; res = abs(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::abs;
latres = abs(latres);
lonres = abs(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 [&minus;2, 12].
**********************************************************************/
static int DecimalPrecision(int len) {
using std::floor; using std::log;
return -int(floor(log(LatitudeResolution(len))/log(Math::real(10))));
}
};
} // namespace GeographicLib
#if defined(_MSC_VER)
# pragma warning (pop)
#endif
#endif // GEOGRAPHICLIB_GEOHASH_HPP

478
external/include/GeographicLib/Geoid.hpp vendored Normal file
View File

@@ -0,0 +1,478 @@
/**
* \file Geoid.hpp
* \brief Header for GeographicLib::Geoid class
*
* Copyright (c) Charles Karney (2009-2021) <charles@karney.com> 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>&minus;1. Setting it
* to 4 uses a maxval of 2<sup>32</sup>&minus;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
*
* &nbsp;&nbsp;&nbsp;\e h = \e N + \e H;
* &nbsp;&nbsp;\e H = &minus;\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 (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 geoid to heights
* above the ellipsoid.
**********************************************************************/
ELLIPSOIDTOGEOID = -1,
/**
* No conversion.
**********************************************************************/
NONE = 0,
/**
* The multiplier for converting from heights above the ellipsoid to
* heights above the geoid.
**********************************************************************/
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&deg; to its value. \e south and \e north should be in
* the range [&minus;90&deg;, 90&deg;].
**********************************************************************/
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(-90), real(0),
real(90), real(360)); }
/**
* 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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 &minus;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 &minus;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 ? 90 - (_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 ? 90 - ( _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(); }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
/**
* @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

View File

@@ -0,0 +1,161 @@
/**
* \file Georef.hpp
* \brief Header for GeographicLib::Georef class
*
* Copyright (c) Charles Karney (2015-2021) <charles@karney.com> 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>
#if defined(_MSC_VER)
// Squelch warnings about dll vs string
# pragma warning (push)
# pragma warning (disable: 4251)
#endif
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_;
enum {
tile_ = 15, // The size of tile in degrees
lonorig_ = -180, // Origin for longitude
latorig_ = -90, // Origin for latitude
base_ = 10, // Base for minutes
baselen_ = 4,
maxprec_ = 11, // approximately equivalent to MGRS class
maxlen_ = baselen_ + 2 * maxprec_,
};
Georef(); // 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 [&minus;90&deg;,
* 90&deg;].
* @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 = &minus;1 (min), 15&deg;
* - \e prec = 0, 1&deg;
* - \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'
* - &hellip;
* - \e prec = 11 (max), 10<sup>&minus;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 [&minus;1, 11] and gives the precision of \e georef as follows:
* - \e prec = &minus;1 (min), 15&deg;
* - \e prec = 0, 1&deg;
* - \e prec = 1, not returned
* - \e prec = 2, 1'
* - \e prec = 3, 0.1'
* - \e prec = 4, 0.01'
* - \e prec = 5, 0.001'
* - &hellip;
* - \e prec = 11 (max), 10<sup>&minus;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 [&minus;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::abs; res = abs(res);
for (int prec = 0; prec < maxprec_; ++prec) {
if (prec == 1)
continue;
if (Resolution(prec) <= res)
return prec;
}
return maxprec_;
}
};
} // namespace GeographicLib
#if defined(_MSC_VER)
# pragma warning (pop)
#endif
#endif // GEOGRAPHICLIB_GEOREF_HPP

View File

@@ -0,0 +1,221 @@
/**
* \file Gnomonic.hpp
* \brief Header for GeographicLib::Gnomonic class
*
* Copyright (c) Charles Karney (2010-2020) <charles@karney.com> 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 &rho; = <i>m12</i>/\e M12; finally \e x = &rho; sin \e azi1; \e
* y = &rho; 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, &rho; 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&deg;, when endpoints are a distance \e r away, and when their
* azimuths from the center are &plusmn; 45&deg; or &plusmn; 135&deg;.
* 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 [&minus;90&deg;, 90&deg;].
* 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 &le; 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 [&minus;90&deg;, 90&deg;]. \e lat will
* be in the range [&minus;90&deg;, 90&deg;] and \e lon will be in the
* range [&minus;180&deg;, 180&deg;]. 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(); }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
};
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_GNOMONIC_HPP

View File

@@ -0,0 +1,287 @@
/**
* \file GravityCircle.hpp
* \brief Header for GeographicLib::GravityCircle class
*
* Copyright (c) Charles Karney (2011-2020) <charles@karney.com> 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, _Z, _Px, _invR, _cpsi, _spsi,
_cphi, _sphi, _amodel, _GMmodel, _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>&minus;2</sup>).
* @param[out] gy the northerly component of the acceleration
* (m s<sup>&minus;2</sup>).
* @param[out] gz the upward component of the acceleration
* (m s<sup>&minus;2</sup>); this is usually negative.
* @return \e W the sum of the gravitational and centrifugal potentials
* (m<sup>2</sup> s<sup>&minus;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>&minus;2</sup>).
* @param[out] deltay the northerly component of the disturbance vector
* (m s<sup>&minus;2</sup>).
* @param[out] deltaz the upward component of the disturbance vector
* (m s<sup>&minus;2</sup>).
* @return \e T the corresponding disturbing potential
* (m<sup>2</sup> s<sup>&minus;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>&minus;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>&minus;2</sup>).
* @param[out] gY the \e Y component of the acceleration
* (m s<sup>&minus;2</sup>).
* @param[out] gZ the \e Z component of the acceleration
* (m s<sup>&minus;2</sup>).
* @return \e W = \e V + &Phi; the sum of the gravitational and
* centrifugal potentials (m<sup>2</sup> s<sup>&minus;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>&minus;2</sup>).
* @param[out] GY the \e Y component of the acceleration
* (m s<sup>&minus;2</sup>).
* @param[out] GZ the \e Z component of the acceleration
* (m s<sup>&minus;2</sup>).
* @return \e V = \e W - &Phi; the gravitational potential
* (m<sup>2</sup> s<sup>&minus;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>&minus;2</sup>).
* @param[out] deltaY the \e Y component of the gravity disturbance
* (m s<sup>&minus;2</sup>).
* @param[out] deltaZ the \e Z component of the gravity disturbance
* (m s<sup>&minus;2</sup>).
* @return \e T = \e W - \e U the disturbing potential (also called the
* anomalous potential) (m<sup>2</sup> s<sup>&minus;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>&minus;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;
}
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
};
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_GRAVITYCIRCLE_HPP

View File

@@ -0,0 +1,549 @@
/**
* \file GravityModel.hpp
* \brief Header for GeographicLib::GravityModel class
*
* Copyright (c) Charles Karney (2011-2021) <charles@karney.com> 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;
* - &Phi; = rotational potential;
* - \e W = \e V + &Phi; = \e T + \e U = total potential;
* - <i>V</i><sub>0</sub> = normal gravitation potential;
* - \e U = <i>V</i><sub>0</sub> + &Phi; = total normal potential;
* - \e T = \e W &minus; \e U = \e V &minus; <i>V</i><sub>0</sub> = anomalous
* or disturbing potential;
* - <b>g</b> = &nabla;\e W = <b>&gamma;</b> + <b>&delta;</b>;
* - <b>f</b> = &nabla;&Phi;;
* - <b>&Gamma;</b> = &nabla;<i>V</i><sub>0</sub>;
* - <b>&gamma;</b> = &nabla;\e U;
* - <b>&delta;</b> = &nabla;\e T = gravity disturbance vector
* = <b>g</b><sub><i>P</i></sub> &minus; <b>&gamma;</b><sub><i>P</i></sub>;
* - &delta;\e g = gravity disturbance = <i>g</i><sub><i>P</i></sub> &minus;
* &gamma;<sub><i>P</i></sub>;
* - &Delta;<b>g</b> = gravity anomaly vector = <b>g</b><sub><i>P</i></sub>
* &minus; <b>&gamma;</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;
* - &Delta;\e g = gravity anomaly = <i>g</i><sub><i>P</i></sub> &minus;
* &gamma;<sub><i>Q</i></sub>;
* - (&xi;, &eta;) deflection of the vertical, the difference in
* directions of <b>g</b><sub><i>P</i></sub> and
* <b>&gamma;</b><sub><i>Q</i></sub>, &xi; = NS, &eta; = 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, _GMmodel, _zeta0, _corrmult;
int _nmx, _mmx;
SphericalHarmonic::normalization _norm;
NormalGravity _earth;
std::vector<real> _Cx, _Sx, _CC, _CS, _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,
};
/** \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 non-negative, truncate the degree of the
* model this value.
* @param[in] Mmax (optional) if non-negative, 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 &ge; 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>&minus;2</sup>).
* @param[out] gy the northerly component of the acceleration
* (m s<sup>&minus;2</sup>).
* @param[out] gz the upward component of the acceleration
* (m s<sup>&minus;2</sup>); this is usually negative.
* @return \e W the sum of the gravitational and centrifugal potentials
* (m<sup>2</sup> s<sup>&minus;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>&minus;2</sup>).
* @param[out] deltay the northerly component of the disturbance vector
* (m s<sup>&minus;2</sup>).
* @param[out] deltaz the upward component of the disturbance vector
* (m s<sup>&minus;2</sup>).
* @return \e T the corresponding disturbing potential
* (m<sup>2</sup> s<sup>&minus;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>&minus;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>&minus;2</sup>).
* @param[out] gY the \e Y component of the acceleration
* (m s<sup>&minus;2</sup>).
* @param[out] gZ the \e Z component of the acceleration
* (m s<sup>&minus;2</sup>).
* @return \e W = \e V + &Phi; the sum of the gravitational and
* centrifugal potentials (m<sup>2</sup> s<sup>&minus;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>&minus;2</sup>).
* @param[out] GY the \e Y component of the acceleration
* (m s<sup>&minus;2</sup>).
* @param[out] GZ the \e Z component of the acceleration
* (m s<sup>&minus;2</sup>).
* @return \e V = \e W - &Phi; the gravitational potential
* (m<sup>2</sup> s<sup>&minus;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>&minus;2</sup>).
* @param[out] deltaY the \e Y component of the gravity disturbance
* (m s<sup>&minus;2</sup>).
* @param[out] deltaZ the \e Z component of the gravity disturbance
* (m s<sup>&minus;2</sup>).
* @return \e T = \e W - \e U the disturbing potential (also called the
* anomalous potential) (m<sup>2</sup> s<sup>&minus;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>&minus;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>&minus;2</sup>).
* @param[out] gammaY the \e Y component of the normal acceleration
* (m s<sup>&minus;2</sup>).
* @param[out] gammaZ the \e Z component of the normal acceleration
* (m s<sup>&minus;2</sup>).
* @return \e U = <i>V</i><sub>0</sub> + &Phi; the sum of the
* normal gravitational and centrifugal potentials
* (m<sup>2</sup> s<sup>&minus;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>&minus;2</sup>).
* @param[out] fY the \e Y component of the centrifugal acceleration
* (m s<sup>&minus;2</sup>).
* @return &Phi; the centrifugal potential (m<sup>2</sup>
* s<sup>&minus;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>&minus;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 _GMmodel; }
/**
* @return \e GM the mass constant of the ReferenceEllipsoid()
* (m<sup>3</sup> s<sup>&minus;2</sup>).
**********************************************************************/
Math::real ReferenceMassConstant() const
{ return _earth.MassConstant(); }
/**
* @return &omega; the angular velocity of the model and the
* ReferenceEllipsoid() (rad s<sup>&minus;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; }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
/**
* @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

View File

@@ -0,0 +1,330 @@
/**
* \file LambertConformalConic.hpp
* \brief Header for GeographicLib::LambertConformalConic class
*
* Copyright (c) Charles Karney (2010-2020) <charles@karney.com> 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="http://pubs.er.usgs.gov/usgspubs/pp/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(3), 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; using 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; using 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 &minus; \e f) \e a, or \e k0 is
* not positive.
* @exception GeographicErr if \e stdlat is not in [&minus;90&deg;,
* 90&deg;].
**********************************************************************/
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 &minus; \e f) \e a, or \e k1 is
* not positive.
* @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in
* [&minus;90&deg;, 90&deg;], 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 &minus; \e f) \e a, or \e k1 is
* not positive.
* @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in
* [&minus;90&deg;, 90&deg;], 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 &minus;
* \e lat1) &le; 160&deg; and max(abs(\e lat1), abs(\e lat2)) &le; 90
* &minus; min(0.0002, 2.2 &times; 10<sup>&minus;6</sup>(180 &minus; \e
* dlat), 6 &times 10<sup>&minus;8</sup> <i>dlat</i><sup>2</sup>) (in
* degrees), then the error in the latitude of origin is less than 4.5
* &times; 10<sup>&minus;14</sup>d and the relative error in the scale is
* less than 7 &times; 10<sup>&minus;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 [&minus;90&deg;,
* 90&deg;].
**********************************************************************/
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
* [&minus;90&deg;, 90&deg;]. 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 [&minus;180&deg;, 180&deg;]. 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; }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
/**
* 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

View File

@@ -0,0 +1,244 @@
/**
* \file LocalCartesian.hpp
* \brief Header for GeographicLib::LocalCartesian class
*
* Copyright (c) Charles Karney (2008-2020) <charles@karney.com> 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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 [&minus;90&deg;, 90&deg;].
*
* 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 &sdot; \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 [&minus;180&deg;, 180&deg;].
**********************************************************************/
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> &sdot; \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(); }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
};
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_LOCALCARTESIAN_HPP

361
external/include/GeographicLib/MGRS.hpp vendored Normal file
View File

@@ -0,0 +1,361 @@
/**
* \file MGRS.hpp
* \brief Header for GeographicLib::MGRS class
*
* Copyright (c) Charles Karney (2008-2020) <charles@karney.com> 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>
#if defined(_MSC_VER)
// Squelch warnings about dll vs string
# pragma warning (push)
# pragma warning (disable: 4251)
#endif
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 &lfloor;10<sup>6</sup> <i>x</i>&rfloor; 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 int mineasting_[4];
static const int maxeasting_[4];
static const int minnorthing_[4];
static const int maxnorthing_[4];
enum {
base_ = 10,
// Top-level tiles are 10^5 m = 100 km on a side
tilelevel_ = 5,
// Period of UTM row letters
utmrowperiod_ = 20,
// Row letters are shifted by 5 for even zones
utmevenrowshift_ = 5,
// Maximum precision is um
maxprec_ = 5 + 6,
// For generating digits at maxprec
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; using std::abs;
real ya = floor( (std::min)(real(88), abs(y/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 access these enums
enum {
tile_ = 100000, // Size MGRS blocks
minutmcol_ = 1,
maxutmcol_ = 9,
minutmSrow_ = 10,
maxutmSrow_ = 100, // Also used for UTM S false northing
minutmNrow_ = 0, // Also used for UTM N false northing
maxutmNrow_ = 95,
minupsSind_ = 8, // These 4 ind's apply to easting and northing
maxupsSind_ = 32,
minupsNind_ = 13,
maxupsNind_ = 27,
upseasting_ = 20, // Also used for UPS false northing
utmeasting_ = 5, // UTM false easting
// Difference between S hemisphere northing and N hemisphere northing
utmNshift_ = (maxutmSrow_ - minutmNrow_) * tile_
};
MGRS(); // 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 = &minus;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
* - &hellip;
* - \e prec = 11 (max), 1 &mu;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 [&minus;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 = &minus;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 &minus;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
* &minus;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);
/** \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(); }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
static Math::real MajorRadius() { return EquatorialRadius(); }
///@}
/**
* 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
#if defined(_MSC_VER)
# pragma warning (pop)
#endif
#endif // GEOGRAPHICLIB_MGRS_HPP

View File

@@ -0,0 +1,204 @@
/**
* \file MagneticCircle.hpp
* \brief Header for GeographicLib::MagneticCircle class
*
* Copyright (c) Charles Karney (2011-2021) <charles@karney.com> 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(); }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
};
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_MAGNETICCIRCLE_HPP

View File

@@ -0,0 +1,406 @@
/**
* \file MagneticModel.hpp
* \brief Header for GeographicLib::MagneticModel class
*
* Copyright (c) Charles Karney (2011-2021) <charles@karney.com> 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
* - 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 _Nmodels, _Nconstants, _nmx, _mmx;
SphericalHarmonic::normalization _norm;
Geocentric _earth;
std::vector< std::vector<real> > _G;
std::vector< std::vector<real> > _H;
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:
/** \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 non-negative, truncate the degree of the
* model this value.
* @param[in] Mmax (optional) if non-negative, 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 &ge; 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 (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).
**********************************************************************/
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 (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).
**********************************************************************/
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 (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.
**********************************************************************/
MagneticCircle Circle(real t, real lat, real h) const;
/**
* Compute the magnetic field in geocentric coordinate.
*
* @param[in] t the time (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).
**********************************************************************/
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; }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
/**
* @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 "wmm2020". 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

684
external/include/GeographicLib/Math.hpp vendored Normal file
View File

@@ -0,0 +1,684 @@
/**
* \file Math.hpp
* \brief Header for GeographicLib::Math class
*
* Copyright (c) Charles Karney (2008-2020) <charles@karney.com> 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 <boost/version.hpp>
#include <boost/multiprecision/float128.hpp>
#include <boost/math/special_functions.hpp>
#elif GEOGRAPHICLIB_PRECISION == 5
#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 \
(throw GeographicLib::GeographicErr("Convergence failure"), false)
#else
#define GEOGRAPHICLIB_VOLATILE volatile
// Ignore convergence failures with standard floating points types by allowing
// loop to exit cleanly.
#define GEOGRAPHICLIB_PANIC 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(); // 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
/**
* @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.
**********************************************************************/
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 &pi;.
**********************************************************************/
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>() / 180;
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; }
/**
* The hypotenuse function avoiding underflow and overflow.
*
* @tparam T the type of the arguments and the returned value.
* @param[in] x
* @param[in] y
* @return sqrt(<i>x</i><sup>2</sup> + <i>y</i><sup>2</sup>).
*
* \deprecated Use std::hypot(x, y).
**********************************************************************/
template<typename T>
GEOGRAPHICLIB_DEPRECATED("Use std::hypot(x, y)")
static T hypot(T x, T y);
/**
* exp(\e x) &minus; 1 accurate near \e x = 0.
*
* @tparam T the type of the argument and the returned value.
* @param[in] x
* @return exp(\e x) &minus; 1.
*
* \deprecated Use std::expm1(x).
**********************************************************************/
template<typename T>
GEOGRAPHICLIB_DEPRECATED("Use std::expm1(x)")
static T expm1(T x);
/**
* log(1 + \e x) accurate near \e x = 0.
*
* @tparam T the type of the argument and the returned value.
* @param[in] x
* @return log(1 + \e x).
*
* \deprecated Use std::log1p(x).
**********************************************************************/
template<typename T>
GEOGRAPHICLIB_DEPRECATED("Use std::log1p(x)")
static T log1p(T x);
/**
* The inverse hyperbolic sine function.
*
* @tparam T the type of the argument and the returned value.
* @param[in] x
* @return asinh(\e x).
*
* \deprecated Use std::asinh(x).
**********************************************************************/
template<typename T>
GEOGRAPHICLIB_DEPRECATED("Use std::asinh(x)")
static T asinh(T x);
/**
* The inverse hyperbolic tangent function.
*
* @tparam T the type of the argument and the returned value.
* @param[in] x
* @return atanh(\e x).
*
* \deprecated Use std::atanh(x).
**********************************************************************/
template<typename T>
GEOGRAPHICLIB_DEPRECATED("Use std::atanh(x)")
static T atanh(T x);
/**
* Copy the sign.
*
* @tparam T the type of the argument.
* @param[in] x gives the magitude of the result.
* @param[in] y gives the sign of the result.
* @return value with the magnitude of \e x and with the sign of \e y.
*
* This routine correctly handles the case \e y = &minus;0, returning
* &minus|<i>x</i>|.
*
* \deprecated Use std::copysign(x, y).
**********************************************************************/
template<typename T>
GEOGRAPHICLIB_DEPRECATED("Use std::copysign(x, y)")
static T copysign(T x, T y);
/**
* The cube root function.
*
* @tparam T the type of the argument and the returned value.
* @param[in] x
* @return the real cube root of \e x.
*
* \deprecated Use std::cbrt(x).
**********************************************************************/
template<typename T>
GEOGRAPHICLIB_DEPRECATED("Use std::cbrt(x)")
static T cbrt(T x);
/**
* The remainder function.
*
* @tparam T the type of the arguments and the returned value.
* @param[in] x
* @param[in] y
* @return the remainder of \e x/\e y in the range [&minus;\e y/2, \e y/2].
*
* \deprecated Use std::remainder(x).
**********************************************************************/
template<typename T>
GEOGRAPHICLIB_DEPRECATED("Use std::remainder(x)")
static T remainder(T x, T y);
/**
* The remquo function.
*
* @tparam T the type of the arguments and the returned value.
* @param[in] x
* @param[in] y
* @param[out] n the low 3 bits of the quotient
* @return the remainder of \e x/\e y in the range [&minus;\e y/2, \e y/2].
*
* \deprecated Use std::remquo(x, y, n).
**********************************************************************/
template<typename T>
GEOGRAPHICLIB_DEPRECATED("Use std::remquo(x, y, n)")
static T remquo(T x, T y, int* n);
/**
* The round function.
*
* @tparam T the type of the argument and the returned value.
* @param[in] x
* @return \e x round to the nearest integer (ties round away from 0).
*
* \deprecated Use std::round(x).
**********************************************************************/
template<typename T>
GEOGRAPHICLIB_DEPRECATED("Use std::round(x)")
static T round(T x);
/**
* The lround function.
*
* @tparam T the type of the argument.
* @param[in] x
* @return \e x round to the nearest integer as a long int (ties round away
* from 0).
*
* If the result does not fit in a long int, the return value is undefined.
*
* \deprecated Use std::lround(x).
**********************************************************************/
template<typename T>
GEOGRAPHICLIB_DEPRECATED("Use std::lround(x)")
static long lround(T x);
/**
* Fused multiply and add.
*
* @tparam T the type of the arguments and the returned value.
* @param[in] x
* @param[in] y
* @param[in] z
* @return <i>xy</i> + <i>z</i>, correctly rounded (on those platforms with
* support for the <code>fma</code> instruction).
*
* On platforms without the <code>fma</code> instruction, no attempt is
* made to improve on the result of a rounded multiplication followed by a
* rounded addition.
*
* \deprecated Use std::fma(x, y, z).
**********************************************************************/
template<typename T>
GEOGRAPHICLIB_DEPRECATED("Use std::fma(x, y, z)")
static T fma(T x, T y, T z);
/**
* 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) && 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
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 that \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).
* @param[in] x the variable.
* @return the value of the polynomial.
*
* Evaluate <i>y</i> = &sum;<sub><i>n</i>=0..<i>N</i></sub>
* <i>p</i><sub><i>n</i></sub> <i>x</i><sup><i>N</i>&minus;<i>n</i></sup>.
* Return 0 if \e N &lt; 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 y = N < 0 ? 0 : *p++;
while (--N >= 0) y = y * x + *p++;
return y;
}
/**
* 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 (&minus;180&deg;, 180&deg;].
*
* The range of \e x is unrestricted.
**********************************************************************/
template<typename T> static T AngNormalize(T x) {
using std::remainder;
x = remainder(x, T(360)); return x != -180 ? x : 180;
}
/**
* 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 [&minus;90&deg;, 90&deg;], otherwise
* return NaN.
**********************************************************************/
template<typename T> static T LatFix(T x)
{ using std::abs; return abs(x) > 90 ? NaN<T>() : x; }
/**
* The exact difference of two angles reduced to
* (&minus;180&deg;, 180&deg;].
*
* @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 &minus; \e x.
*
* This computes \e z = \e y &minus; \e x exactly, reduced to
* (&minus;180&deg;, 180&deg;]; 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 d = &minus;180, then \e e &gt; 0; If \e d = 180, then \e e
* &le; 0.
**********************************************************************/
template<typename T> static T AngDiff(T x, T y, T& e) {
using std::remainder;
T t, d = AngNormalize(sum(remainder(-x, T(360)),
remainder( y, T(360)), t));
// Here y - x = d + t (mod 360), exactly, where d is in (-180,180] and
// abs(t) <= eps (eps = 2^-45 for doubles). The only case where the
// addition of t takes the result outside the range (-180,180] is d = 180
// and t > 0. The case, d = -180 + eps, t = -eps, can't happen, since
// sum would have returned the exact result in such a case (i.e., given t
// = 0).
return sum(d == 180 && t > 0 ? -180 : d, t, e);
}
/**
* Difference of two angles reduced to [&minus;180&deg;, 180&deg;]
*
* @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 &minus; \e x, reduced to the range [&minus;180&deg;,
* 180&deg;].
*
* The result is equivalent to computing the difference exactly, reducing
* it to (&minus;180&deg;, 180&deg;] and rounding the result. Note that
* this prescription allows &minus;180&deg; to be returned (e.g., if \e x
* is tiny and negative and \e y = 180&deg;).
**********************************************************************/
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 &minus; nextafter(1/16, 0) =
* 1/2<sup>57</sup> for reals = 0.7 pm on the earth if \e x is an angle in
* degrees. (This is about 1000 times more resolution than we get with
* angles around 90&deg;.) We use this to avoid having to deal with near
* singular cases when \e x is non-zero but tiny (e.g.,
* 10<sup>&minus;200</sup>). This converts &minus;0 to +0; however tiny
* negative numbers get converted to &minus;0.
**********************************************************************/
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&deg; = cos 81&deg; = &minus; sin 123456789&deg;.
* If x = &minus;0, then \e sinx = &minus;0; this is the only case where
* &minus;0 is returned.
**********************************************************************/
template<typename T> static void sincosd(T x, 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>).
**********************************************************************/
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>).
**********************************************************************/
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 = &plusmn;90&deg;, 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 (&minus;180&deg; 180&deg;]. N.B.,
* atan2d(&plusmn;0, &minus;1) = +180&deg;; atan2d(&minus;&epsilon;,
* &minus;1) = &minus;180&deg;, for &epsilon; positive and tiny;
* atan2d(&plusmn;0, +1) = &plusmn;0&deg;.
**********************************************************************/
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&chi; in terms of tan&phi;
*
* @tparam T the type of the argument and the returned value.
* @param[in] tau &tau; = tan&phi;
* @param[in] es the signed eccentricity = sign(<i>e</i><sup>2</sup>)
* sqrt(|<i>e</i><sup>2</sup>|)
* @return &tau;&prime; = tan&chi;
*
* 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&phi; in terms of tan&chi;
*
* @tparam T the type of the argument and the returned value.
* @param[in] taup &tau;&prime; = tan&chi;
* @param[in] es the signed eccentricity = sign(<i>e</i><sup>2</sup>)
* sqrt(|<i>e</i><sup>2</sup>|)
* @return &tau; = tan&phi;
*
* 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);
/**
* Test for finiteness.
*
* @tparam T the type of the argument.
* @param[in] x
* @return true if number is finite, false if NaN or infinite.
*
* \deprecated Use std::isfinite(x).
**********************************************************************/
template<typename T>
GEOGRAPHICLIB_DEPRECATED("Use std::isfinite(x)")
static bool isfinite(T x);
/**
* 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();
/**
* Test for NaN.
*
* @tparam T the type of the argument.
* @param[in] x
* @return true if argument is a NaN.
*
* \deprecated Use std::isnan(x).
**********************************************************************/
template<typename T>
GEOGRAPHICLIB_DEPRECATED("Use std::isnan(x)")
static bool isnan(T x);
/**
* 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

View File

@@ -0,0 +1,837 @@
/**
* \file NearestNeighbor.hpp
* \brief Header for GeographicLib::NearestNeighbor class
*
* Copyright (c) Charles Karney (2016-2020) <charles@karney.com> 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 <iostream>
#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
#if defined(_MSC_VER)
// Squelch warnings about constant conditional expressions
# pragma warning (push)
# pragma warning (disable: 4127)
#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 bundled 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 compile-time 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 = &minus;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 (&minus;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 &minus;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 &minus;
* \e tol are correct; all others are suspect &mdash; there may be other
* closer results with distances greater or equal to \e dk &minus; \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
#if defined(_MSC_VER)
# pragma warning (pop)
#endif
#endif // GEOGRAPHICLIB_NEARESTNEIGHBOR_HPP

View File

@@ -0,0 +1,400 @@
/**
* \file NormalGravity.hpp
* \brief Header for GeographicLib::NormalGravity class
*
* Copyright (c) Charles Karney (2011-2020) <charles@karney.com> 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 ellipoid", 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> &minus; <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
* semi-axis. In the case of an oblate ellipsoid, the mass is concentrated
* on a "focal rod" of length 2(<i>b</i><sup>2</sup> &minus;
* <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 &isin; [0.01, 100] or \e f &isin; [&minus;99, 0.99].
*
* Definitions:
* - <i>V</i><sub>0</sub>, the gravitational contribution to the normal
* potential;
* - &Phi;, the rotational contribution to the normal potential;
* - \e U = <i>V</i><sub>0</sub> + &Phi;, the total potential;
* - <b>&Gamma;</b> = &nabla;<i>V</i><sub>0</sub>, the acceleration due to
* mass of the earth;
* - <b>f</b> = &nabla;&Phi;, the centrifugal acceleration;
* - <b>&gamma;</b> = &nabla;\e U = <b>&Gamma;</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).
* - 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, _GM, _omega, _f, _J2, _omega2, _aomega2;
real _e2, _ep2, _b, _E, _U0, _gammae, _gammap, _Q0, _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; using std::abs; using std::atan; using std::asin;
using std::asinh; using std::atanh;
real z = sqrt(abs(x));
return x == 0 ? 1 :
(alt ?
(!(x < 0) ? asinh(z) : asin(z)) / sqrt(abs(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>&minus;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 &minus; \e b) / \e a, where \e a and \e b are
* the equatorial radius and the polar semi-axis. The parameters should
* obey \e a &gt; 0, \e f &lt; 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 &minus; \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 &gt; 0, \e GM &gt; 0 and \e J2 &lt; 1/3
* &minus; (<i>omega</i><sup>2</sup><i>a</i><sup>3</sup>/<i>GM</i>)
* 8/(45&pi;). 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 &gamma; the acceleration due to gravity, positive downwards
* (m s<sup>&minus;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>&minus;2</sup>).
* @param[out] gammaz the upward component of the acceleration
* (m s<sup>&minus;2</sup>); this is usually negative.
* @return \e U the corresponding normal potential
* (m<sup>2</sup> s<sup>&minus;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>&minus;2</sup>).
* @param[out] gammaY the \e Y component of the acceleration
* (m s<sup>&minus;2</sup>).
* @param[out] gammaZ the \e Z component of the acceleration
* (m s<sup>&minus;2</sup>).
* @return \e U = <i>V</i><sub>0</sub> + &Phi; the sum of the
* gravitational and centrifugal potentials
* (m<sup>2</sup> s<sup>&minus;2</sup>).
*
* The acceleration given by <b>&gamma;</b> = &nabla;\e U =
* &nabla;<i>V</i><sub>0</sub> + &nabla;&Phi; = <b>&Gamma;</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>&minus;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>&minus;2</sup>).
* @return <i>V</i><sub>0</sub> the gravitational potential
* (m<sup>2</sup> s<sup>&minus;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>&minus;2</sup>).
* @param[out] fY the \e Y component of the centrifugal acceleration
* (m s<sup>&minus;2</sup>).
* @return &Phi; the centrifugal potential (m<sup>2</sup>
* s<sup>&minus;2</sup>).
*
* &Phi; 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>&minus;2</sup>). This is the value used in the
* constructor.
**********************************************************************/
Math::real MassConstant() const
{ return Init() ? _GM : 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> =
* &minus;<i>J</i><sub><i>n</i></sub> / sqrt(2 \e n + 1).
**********************************************************************/
Math::real DynamicalFormFactor(int n = 2) const
{ return Init() ? ( n == 2 ? _J2 : Jn(n)) : Math::NaN(); }
/**
* @return &omega; the angular velocity of the ellipsoid (rad
* s<sup>&minus;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 &minus; \e b)/\e
* a.
**********************************************************************/
Math::real Flattening() const
{ return Init() ? _f : Math::NaN(); }
/**
* @return &gamma;<sub>e</sub> the normal gravity at equator (m
* s<sup>&minus;2</sup>).
**********************************************************************/
Math::real EquatorialGravity() const
{ return Init() ? _gammae : Math::NaN(); }
/**
* @return &gamma;<sub>p</sub> the normal gravity at poles (m
* s<sup>&minus;2</sup>).
**********************************************************************/
Math::real PolarGravity() const
{ return Init() ? _gammap : Math::NaN(); }
/**
* @return <i>f*</i> the gravity flattening (&gamma;<sub>p</sub> &minus;
* &gamma;<sub>e</sub>) / &gamma;<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>&minus;2</sup>).
**********************************************************************/
Math::real SurfacePotential() const
{ return Init() ? _U0 : Math::NaN(); }
/**
* @return the Geocentric object used by this instance.
**********************************************************************/
const Geocentric& Earth() const { return _earth; }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
/**
* 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>&minus;1</sup>).
* @param[in] J2 the dynamical form factor.
* @return \e f the flattening of the ellipsoid.
*
* This routine requires \e a &gt; 0, \e GM &gt; 0, \e J2 &lt; 1/3 &minus;
* <i>omega</i><sup>2</sup><i>a</i><sup>3</sup>/<i>GM</i> 8/(45&pi;). 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>&minus;1</sup>).
* @param[in] f the flattening of the ellipsoid.
* @return \e J2 the dynamical form factor.
*
* This routine requires \e a &gt; 0, \e GM &ne; 0, \e f &lt; 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

255
external/include/GeographicLib/OSGB.hpp vendored Normal file
View File

@@ -0,0 +1,255 @@
/**
* \file OSGB.hpp
* \brief Header for GeographicLib::OSGB class
*
* Copyright (c) Charles Karney (2010-2020) <charles@karney.com> 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>
#if defined(_MSC_VER)
// Squelch warnings about dll vs string
# pragma warning (push)
# pragma warning (disable: 4251)
#endif
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="http://www.ordnancesurvey.co.uk/docs/support/guide-coordinate-systems-great-britain.pdf">
* A guide to coordinate systems in Great Britain</a>
* - <a href="http://www.ordnancesurvey.co.uk/docs/support/national-grid.pdf">
* Guide to 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();
enum {
base_ = 10,
tile_ = 100000,
tilelevel_ = 5,
tilegrid_ = 5,
tileoffx_ = 2 * tilegrid_,
tileoffy_ = 1 * tilegrid_,
minx_ = - tileoffx_ * tile_,
miny_ = - tileoffy_ * tile_,
maxx_ = (tilegrid_*tilegrid_ - tileoffx_) * tile_,
maxy_ = (tilegrid_*tilegrid_ - tileoffy_) * tile_,
// Maximum precision is um
maxprec_ = 5 + 6,
};
static real computenorthoffset();
static void CheckCoords(real x, real y);
OSGB(); // 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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 [&minus;180&deg;,
* 180&deg;].
**********************************************************************/
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&mu;m
*
* The easting must be in the range [&minus;1000 km, 1500 km) and the
* northing must be in the range [&minus;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 coordinates to a grid reference.
*
* @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 &minus;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&minus;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 (p. 37) of <i>A guide to coordinate systems
* in Great Britain</i>, v2.2 (Dec. 2013).
**********************************************************************/
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 &minus; 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 &amp; Datums, PE&amp;RS, Oct. 2003, states that
* this is defined as 10<sup>9.9998268&minus;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 (&minus;2
* degrees).
**********************************************************************/
static Math::real OriginLongitude() { return real(-2); }
/**
* @return false northing the OSGB projection (&minus;100000 meters).
**********************************************************************/
static Math::real FalseNorthing() { return real(-100000); }
/**
* @return false easting the OSGB projection (400000 meters).
**********************************************************************/
static Math::real FalseEasting() { return real(400000); }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
static Math::real MajorRadius() { return EquatorialRadius(); }
///@}
};
} // namespace GeographicLib
#if defined(_MSC_VER)
# pragma warning (pop)
#endif
#endif // GEOGRAPHICLIB_OSGB_HPP

View File

@@ -0,0 +1,160 @@
/**
* \file PolarStereographic.hpp
* \brief Header for GeographicLib::PolarStereographic class
*
* Copyright (c) Charles Karney (2008-2020) <charles@karney.com> 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="http://pubs.er.usgs.gov/usgspubs/pp/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 a 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 &minus; \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 (&minus;90&deg;,
* 90&deg;].
**********************************************************************/
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
* (&minus;90&deg;, 90&deg;] for \e northp = true and in the range
* [&minus;90&deg;, 90&deg;) 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 [&minus;180&deg;, 180&deg;].
**********************************************************************/
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; }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
/**
* 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

View File

@@ -0,0 +1,325 @@
/**
* \file PolygonArea.hpp
* \brief Header for GeographicLib::PolygonAreaT class
*
* Copyright (c) Charles Karney (2010-2021) <charles@karney.com> 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) {
// Return 1 or -1 if crossing prime meridian in east or west direction.
// Otherwise return zero.
// Compute lon12 the same way as Geodesic::Inverse.
lon1 = Math::AngNormalize(lon1);
lon2 = Math::AngNormalize(lon2);
real lon12 = Math::AngDiff(lon1, lon2);
// Treat 0 as negative in these tests. This balances +/- 180 being
// treated as positive, i.e., +180.
int cross =
lon1 <= 0 && lon2 > 0 && lon12 > 0 ? 1 :
(lon2 <= 0 && lon1 > 0 && lon12 < 0 ? -1 : 0);
return cross;
}
// an alternate version of transit to deal with longitudes in the direct
// problem.
static int transitdirect(real lon1, real lon2) {
// Compute exactly the parity of
// int(ceil(lon2 / 360)) - int(ceil(lon1 / 360))
using std::remainder;
lon1 = remainder(lon1, real(720));
lon2 = remainder(lon2, real(720));
return ( (lon2 <= 0 && lon2 > -360 ? 1 : 0) -
(lon1 <= 0 && lon1 > -360 ? 1 : 0) );
}
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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 counter-clockwise)
* 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 counter-clockwise)
* 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 [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 counter-clockwise)
* 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 [&minus;180&deg;, 180&deg;].
**********************************************************************/
void CurrentPoint(real& lat, real& lon) const
{ lat = _lat1; lon = _lon1; }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
};
/**
* @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. (But note that the implementation of
* areas in GeodesicExact uses a high order series and this is only accurate
* for modest flattenings.)
**********************************************************************/
typedef PolygonAreaT<GeodesicExact> PolygonAreaExact;
/**
* @relates PolygonAreaT
*
* Polygon areas using Rhumb.
**********************************************************************/
typedef PolygonAreaT<Rhumb> PolygonAreaRhumb;
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_POLYGONAREA_HPP

621
external/include/GeographicLib/Rhumb.hpp vendored Normal file
View File

@@ -0,0 +1,621 @@
/**
* \file Rhumb.hpp
* \brief Header for GeographicLib::Rhumb and GeographicLib::RhumbLine classes
*
* Copyright (c) Charles Karney (2014-2021) <charles@karney.com> 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/Ellipsoid.hpp>
#if !defined(GEOGRAPHICLIB_RHUMBAREA_ORDER)
/**
* The order of the series approximation used in rhumb area calculations.
* GEOGRAPHICLIB_RHUMBAREA_ORDER can be set to any integer in [4, 8].
**********************************************************************/
# define GEOGRAPHICLIB_RHUMBAREA_ORDER \
(GEOGRAPHICLIB_PRECISION == 2 ? 6 : \
(GEOGRAPHICLIB_PRECISION == 1 ? 4 : 8))
#endif
namespace GeographicLib {
class RhumbLine;
template <class T> class PolygonAreaT;
/**
* \brief Solve of the direct and inverse rhumb problems.
*
* The path of constant azimuth between two points on a 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 counter-clockwise, 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.
*
* 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;
Ellipsoid _ell;
bool _exact;
real _c2;
static const int tm_maxord = GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER;
static const int maxpow_ = GEOGRAPHICLIB_RHUMBAREA_ORDER;
// _R[0] unused
real _R[maxpow_ + 1];
static real gd(real x)
{ using std::atan; using std::sinh; return atan(sinh(x)); }
// Use divided differences to determine (mu2 - mu1) / (psi2 - psi1)
// accurately
//
// Definition: Df(x,y,d) = (f(x) - f(y)) / (x - y)
// See:
// W. M. Kahan and R. J. Fateman,
// Symbolic computation of divided differences,
// SIGSAM Bull. 33(3), 7-28 (1999)
// https://doi.org/10.1145/334714.334716
// http://www.cs.berkeley.edu/~fateman/papers/divdiff.pdf
static real Dlog(real x, real y) {
using std::sqrt; using std::asinh;
real t = x - y;
// Change
//
// atanh(t / (x + y))
//
// to
//
// asinh(t / (2 * sqrt(x*y)))
//
// to avoid taking atanh(1) when x is large and y is 1. N.B., this
// routine is invoked with positive x and y, so no need to guard against
// taking the sqrt of a negative quantity. This fixes bogus results for
// the area being returning when an endpoint is at a pole.
return t != 0 ? 2 * asinh(t / (2 * sqrt(x*y))) / t : 1 / x;
}
// N.B., x and y are in degrees
static real Dtan(real x, real y) {
real d = x - y, tx = Math::tand(x), ty = Math::tand(y), txy = tx * ty;
return d != 0 ?
(2 * txy > -1 ? (1 + txy) * Math::tand(d) : tx - ty) /
(d * Math::degree()) :
1 + txy;
}
static real Datan(real x, real y) {
using std::atan;
real d = x - y, xy = x * y;
return d != 0 ?
(2 * xy > -1 ? atan( d / (1 + xy) ) : atan(x) - atan(y)) / d :
1 / (1 + xy);
}
static real Dsin(real x, real y) {
using std::sin; using std::cos;
real d = (x - y) / 2;
return cos((x + y)/2) * (d != 0 ? sin(d) / d : 1);
}
static real Dsinh(real x, real y) {
using std::sinh; using std::cosh;
real d = (x - y) / 2;
return cosh((x + y) / 2) * (d != 0 ? sinh(d) / d : 1);
}
static real Dcosh(real x, real y) {
using std::sinh;
real d = (x - y) / 2;
return sinh((x + y) / 2) * (d != 0 ? sinh(d) / d : 1);
}
static real Dasinh(real x, real y) {
using std::asinh; using std::hypot;
real d = x - y,
hx = hypot(real(1), x), hy = hypot(real(1), y);
return d != 0 ?
asinh(x*y > 0 ? d * (x + y) / (x*hy + y*hx) : x*hy - y*hx) / d :
1 / hx;
}
static real Dgd(real x, real y) {
using std::sinh;
return Datan(sinh(x), sinh(y)) * Dsinh(x, y);
}
// N.B., x and y are the tangents of the angles
static real Dgdinv(real x, real y)
{ return Dasinh(x, y) / Datan(x, y); }
// Copied from LambertConformalConic...
// 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 - _ell._e2 * x * y;
return t != 0 ? Math::eatanhe(t / d, _ell._es) / t : _ell._e2 / d;
}
// (E(x) - E(y)) / (x - y) -- E = incomplete elliptic integral of 2nd kind
real DE(real x, real y) const;
// (mux - muy) / (phix - phiy) using elliptic integrals
real DRectifying(real latx, real laty) const;
// (psix - psiy) / (phix - phiy)
real DIsometric(real latx, real laty) const;
// (sum(c[j]*sin(2*j*x),j=1..n) - sum(c[j]*sin(2*j*x),j=1..n)) / (x - y)
static real SinCosSeries(bool sinp,
real x, real y, const real c[], int n);
// (mux - muy) / (chix - chiy) using Krueger's series
real DConformalToRectifying(real chix, real chiy) const;
// (chix - chiy) / (mux - muy) using Krueger's series
real DRectifyingToConformal(real mux, real muy) const;
// (mux - muy) / (psix - psiy)
// N.B., psix and psiy are in degrees
real DIsometricToRectifying(real psix, real psiy) const;
// (psix - psiy) / (mux - muy)
real DRectifyingToIsometric(real mux, real muy) const;
real MeanSinXi(real psi1, real psi2) 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 a 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 (the default) use an addition theorem for
* elliptic integrals to compute divided differences; otherwise use
* series expansion (accurate for |<i>f</i>| < 0.01).
* @exception GeographicErr if \e a or (1 &minus; \e f) \e a is not
* positive.
*
* See \ref rhumb, for a detailed description of the \e exact parameter.
**********************************************************************/
Rhumb(real a, real f, bool exact = true);
/**
* 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 [&minus;90&deg;, 90&deg;]. The value of
* \e lon2 returned is in the range [&minus;180&deg;, 180&deg;].
*
* If point 1 is a pole, the cosine of its latitude is taken to be
* 1/&epsilon;<sup>2</sup> (where &epsilon; 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 [&minus;180&deg;, 180&deg;].
* .
* With the Rhumb::LONG_UNROLL bit set, the quantity \e lon2 &minus;
* \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 [&minus;90&deg;,
* 90&deg;]. The value of \e azi12 returned is in the range
* [&minus;180&deg;, 180&deg;].
*
* If either point is a pole, the cosine of its latitude is taken to be
* 1/&epsilon;<sup>2</sup> (where &epsilon; 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;
/**
* 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 [&minus;90&deg;, 90&deg;].
*
* If point 1 is a pole, the cosine of its latitude is taken to be
* 1/&epsilon;<sup>2</sup> (where &epsilon; 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 _ell.EquatorialRadius(); }
/**
* @return \e f the flattening of the ellipsoid. This is the
* value used in the constructor.
**********************************************************************/
Math::real Flattening() const { return _ell.Flattening(); }
/**
* @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 _ell.Area(); }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
/**
* 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;
bool _exact; // TODO: RhumbLine::_exact is unused; retire
real _lat1, _lon1, _azi12, _salp, _calp, _mu1, _psi1, _r1;
// copy assignment not allowed
RhumbLine& operator=(const RhumbLine&) = delete;
RhumbLine(const Rhumb& rh, real lat1, real lon1, real azi12,
bool exact);
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,
};
/**
* 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 [&minus;180&deg;,
* 180&deg;].
*
* 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 [&minus;180&deg;, 180&deg;].
* .
* With the RhumbLine::LONG_UNROLL bit set, the quantity \e lon2 &minus; \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
#endif // GEOGRAPHICLIB_RHUMB_HPP

View File

@@ -0,0 +1,384 @@
/**
* \file SphericalEngine.hpp
* \brief Header for GeographicLib::SphericalEngine class
*
* Copyright (c) Charles Karney (2011-2019) <charles@karney.com> 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 _Nx, _nmx, _mmx;
std::vector<real>::const_iterator _Cnm;
std::vector<real>::const_iterator _Snm;
public:
/**
* A default constructor
**********************************************************************/
coeff() : _Nx(-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 &ge; \e nmx &ge; \e mmx &ge; &minus;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)
: _Nx(N)
, _nmx(nmx)
, _mmx(mmx)
, _Cnm(C.begin())
, _Snm(S.begin())
{
if (!((_Nx >= _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()) + (_Nx + 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 &ge; &minus;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)
: _Nx(N)
, _nmx(N)
, _mmx(N)
, _Cnm(C.begin())
, _Snm(S.begin())
{
if (!(_Nx >= -1))
throw GeographicErr("Bad indices for coeff");
if (!(index(_nmx, _mmx) < int(C.size()) &&
index(_nmx, _mmx) < int(S.size()) + (_Nx + 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 _Nx; }
/**
* @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 * _Nx - 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 *(_Cnm + 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 *(_Snm + (k - (_Nx + 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 : *(_Cnm + 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 : *(_Snm + (k - (_Nx + 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 &ge;
* \e M &ge; &minus;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&minus;1] * c[L&minus;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

View File

@@ -0,0 +1,356 @@
/**
* \file SphericalHarmonic.hpp
* \brief Header for GeographicLib::SphericalHarmonic class
*
* Copyright (c) Charles Karney (2011-2019) <charles@karney.com> 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>,
* - &theta; = atan2(\e p, \e z) = the spherical \e colatitude,
* - &lambda; = 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). (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)
* = (&minus;1)<sup><i>m</i></sup>
* sqrt(\e k (2\e n + 1) (\e n &minus; \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&theta;)
* cos(<i>m</i>&lambda;) and
* <i>P</i><sub><i>nm</i></sub><sup>full</sup>(cos&theta;)
* sin(<i>m</i>&lambda;) 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)
* = (&minus;1)<sup><i>m</i></sup>
* sqrt(\e k (\e n &minus; \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&theta;)
* cos(<i>m</i>&lambda;) and
* <i>P</i><sub><i>nm</i></sub><sup>schmidt</sup>(cos&theta;)
* sin(<i>m</i>&lambda;) 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 &ge; &minus;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 &minus; \e m
* (\e m &minus; 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 &ge; \e nmx &ge; \e mmx &ge; &minus;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., &sum;<sub><i>n</i> = 0..<i>N</i></sub>
* &sum;<sub><i>m</i> = 0..<i>n</i></sub> becomes &sum;<sub><i>m</i> =
* 0..<i>N</i></sub> &sum;<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

View File

@@ -0,0 +1,283 @@
/**
* \file SphericalHarmonic1.hpp
* \brief Header for GeographicLib::SphericalHarmonic1 class
*
* Copyright (c) Charles Karney (2011) <charles@karney.com> 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 &ge;
* \e N1 &ge; &minus;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 &ge; \e
* nmx &ge; \e mmx &ge; &minus;1; \e N1 &ge; \e nmx1 &ge; \e mmx1 &ge;
* &minus;1; \e N &ge; \e N1; \e nmx &ge; \e nmx1; \e mmx &ge; \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., &sum;<sub><i>n</i> = 0..<i>N</i></sub>
* &sum;<sub><i>m</i> = 0..<i>n</i></sub> becomes &sum;<sub><i>m</i> =
* 0..<i>N</i></sub> &sum;<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

View File

@@ -0,0 +1,320 @@
/**
* \file SphericalHarmonic2.hpp
* \brief Header for GeographicLib::SphericalHarmonic2 class
*
* Copyright (c) Charles Karney (2011-2012) <charles@karney.com> 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 &ge;
* \e N1 &ge; &minus;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 &le; \e N and \e N2 &le; \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 &ge; \e
* nmx &ge; \e mmx &ge; &minus;1; \e N1 &ge; \e nmx1 &ge; \e mmx1 &ge;
* &minus;1; \e N &ge; \e N1; \e nmx &ge; \e nmx1; \e mmx &ge; \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., &sum;<sub><i>n</i> = 0..<i>N</i></sub>
* &sum;<sub><i>m</i> = 0..<i>n</i></sub> becomes &sum;<sub><i>m</i> =
* 0..<i>N</i></sub> &sum;<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

View File

@@ -0,0 +1,206 @@
/**
* \file TransverseMercator.hpp
* \brief Header for GeographicLib::TransverseMercator class
*
* Copyright (c) Charles Karney (2008-2020) <charles@karney.com> 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>
#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&uuml;ger's method which evaluates the projection and its
* inverse in terms of a series. See
* - L. Kr&uuml;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&uuml;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
* &times; 10<sup>&minus;15</sup>&quot; and the relative error in the scale
* is 6 &times; 10<sup>&minus;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 &phi; = 0&deg;, &lambda;
* &minus; &lambda;<sub>0</sub> = &plusmn;(1 &minus; \e e)90&deg; (&asymp;
* &plusmn;82.6&deg; 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 &minus; 2e)90&deg; (&asymp; 75&deg; 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.
*
* 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
* TransverseMercator and TransverseMercatorExact.
**********************************************************************/
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, _e2, _es, _e2m, _c, _n;
// _alp[0] and _bet[0] unused
real _a1, _b1, _alp[maxpow_ + 1], _bet[maxpow_ + 1];
friend class Ellipsoid; // For access to taupf, tauf.
public:
/**
* Constructor for a 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 &minus; \e f) \e a, or \e k0 is
* not positive.
**********************************************************************/
TransverseMercator(real a, real f, real k0);
/**
* 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
* [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 [&minus;180&deg;, 180&deg;].
**********************************************************************/
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; }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
/**
* 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

View File

@@ -0,0 +1,264 @@
/**
* \file TransverseMercatorExact.hpp
* \brief Header for GeographicLib::TransverseMercatorExact class
*
* Copyright (c) Charles Karney (2008-2020) <charles@karney.com> 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).
* - 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 &times; 10<sup>&minus;15</sup>&quot;,
* the relative error in the scale is 7 &times; 10<sup>&minus;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&uuml;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;
static const int numit_ = 10;
real tol_, tol2_, taytol_;
real _a, _f, _k0, _mu, _mv, _e;
bool _extendp;
EllipticFunction _Eu, _Ev;
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 a ellipsoid with
*
* @param[in] a equatorial radius (meters).
* @param[in] f flattening of ellipsoid.
* @param[in] k0 central scale factor.
* @param[in] extendp use extended domain.
* @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 &minus; \e lon0 = 90 (1 &minus; \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 &plusmn; \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 = &minus;7131237.022729 m and \e x =
* 29735142.378357 m, \e y = 4235043.607933 m both map to \e lat =
* &minus;2&deg;, \e lon = 88&deg;.
*
* 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 &minus; \e lon0 in [0, 90]
* - \e lat in (-90, 0] and \e lon &minus; \e lon0 in [90 (1 &minus; \e
e), 90]
* - the union of
* - <i>x</i>/(\e k0 \e a) in [0, &infin;) 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 &minus; <i>e</i><sup>2</sup>) &minus;
* E(1 &minus; <i>e</i><sup>2</sup>), &infin;) and <i>y</i>/(\e k0 \e
* a) in (&minus;&infin;, 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
* [&minus;90&deg;, 90&deg;].
**********************************************************************/
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 [&minus;180&deg;, 180&deg;].
**********************************************************************/
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; }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
Math::real MajorRadius() const { return EquatorialRadius(); }
///@}
/**
* 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

View File

@@ -0,0 +1,428 @@
/**
* \file UTMUPS.hpp
* \brief Header for GeographicLib::UTMUPS class
*
* Copyright (c) Charles Karney (2008-2020) <charles@karney.com> 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(); // Disable constructor
public:
/**
* In this class we bring together the UTM and UPS coordinates systems.
* The UTM divides the earth between latitudes &minus;80&deg; and 84&deg;
* into 60 zones numbered 1 thru 60. Zone assign zone number 0 to the UPS
* regions, covering the two poles. Within UTMUPS, non-negative 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&deg;, 48&deg;). 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 [&minus;80&deg;, 84&deg;), 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 [&minus;80&deg;,
* 84&deg;) and longitude is in [42&deg;, 48&deg;).
**********************************************************************/
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 non-negative, otherwise apply the rules given in
* UTMUPS::zonespec.
* @exception GeographicErr if \e setzone is outside the range
* [UTMUPS::MINPSEUDOZONE, UTMUPS::MAXZONE] = [&minus;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 [&minus;90&deg;,
* 90&deg;].
* @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 non-negative,
* 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
* non-negative.) 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 &ge; 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 &lt; 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&deg; &le; \e lat &lt; 40&deg;. 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(); }
/**
* \deprecated An old name for EquatorialRadius().
**********************************************************************/
GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()")
static Math::real MajorRadius() { return EquatorialRadius(); }
///@}
};
} // namespace GeographicLib
#endif // GEOGRAPHICLIB_UTMUPS_HPP

View File

@@ -0,0 +1,733 @@
/**
* \file Utility.hpp
* \brief Header for GeographicLib::Utility class
*
* Copyright (c) Charles Karney (2011-2020) <charles@karney.com> 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 and unsafe gmtime
# pragma warning (push)
# pragma warning (disable: 4127 4996)
#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 from date to sequential day and vice versa
//
// Here is some code to convert a date to sequential day and vice
// versa. The sequential day is numbered so that January 1, 1 AD is day 1
// (a Saturday). So this is offset from the "Julian" day which starts the
// numbering with 4713 BC.
//
// This is inspired by a talk by John Conway at the John von Neumann
// National Supercomputer Center when he described his Doomsday algorithm
// for figuring the day of the week. The code avoids explicitly doing ifs
// (except for the decision of whether to use the Julian or Gregorian
// calendar). Instead the equivalent result is achieved using integer
// arithmetic. I got this idea from the routine for the day of the week
// in MACLisp (I believe that that routine was written by Guy Steele).
//
// There are three issues to take care of
//
// 1. the rules for leap years,
// 2. the inconvenient placement of leap days at the end of February,
// 3. the irregular pattern of month lengths.
//
// We deal with these as follows:
//
// 1. Leap years are given by simple rules which are straightforward to
// accommodate.
//
// 2. We simplify the calculations by moving January and February to the
// previous year. Here we internally number the months MarchDecember,
// January, February as 09, 10, 11.
//
// 3. The pattern of month lengths from March through January is regular
// with a 5-month period—31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31. The
// 5-month period is 153 days long. Since February is now at the end of
// the year, we don't need to include its length in this part of the
// calculation.
bool greg = gregorian(y, m, d);
y += (m + 9) / 12 - 1; // Move Jan and Feb to previous year,
m = (m + 9) % 12; // making March month 0.
return
(1461 * y) / 4 // Julian years converted to days. Julian year is 365 +
// 1/4 = 1461/4 days.
// Gregorian leap year corrections. The 2 offset with respect to the
// Julian calendar synchronizes the vernal equinox with that at the
// time of the Council of Nicea (325 AD).
+ (greg ? (y / 100) / 4 - (y / 100) + 2 : 0)
+ (153 * m + 2) / 5 // The zero-based start of the m'th month
+ d - 1 // The zero-based day
- 305; // The number of days between March 1 and December 31.
// This makes 0001-01-01 day 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) {
int s = day(y, m, d);
if (!check)
return s;
int y1, m1, d1;
date(s, y1, m1, d1);
if (!(s > 0 && y == y1 && m == m1 && d == d1))
throw GeographicErr("Invalid date " +
str(y) + "-" + str(m) + "-" + str(d)
+ (s > 0 ? "; use " +
str(y1) + "-" + str(m1) + "-" + str(d1) :
" before 0001-01-01"));
return s;
}
/**
* 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) {
int c = 0;
bool greg = gregorian(s);
s += 305; // s = 0 on March 1, 1BC
if (greg) {
s -= 2; // The 2 day Gregorian offset
// Determine century with the Gregorian rules for leap years. The
// Gregorian year is 365 + 1/4 - 1/100 + 1/400 = 146097/400 days.
c = (4 * s + 3) / 146097;
s -= (c * 146097) / 4; // s = 0 at beginning of century
}
y = (4 * s + 3) / 1461; // Determine the year using Julian rules.
s -= (1461 * y) / 4; // s = 0 at start of year, i.e., March 1
y += c * 100; // Assemble full year
m = (5 * s + 2) / 153; // Determine the month
s -= (153 * m + 2) / 5; // s = 0 at beginning of month
d = s + 1; // Determine day of month
y += (m + 2) / 12; // Move Jan and Feb back to original year
m = (m + 2) % 12 + 1; // Renumber the months so January = 1
}
/**
* 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) {
if (s == "now") {
std::time_t t = std::time(0);
struct tm* now = gmtime(&t);
y = now->tm_year + 1900;
m = now->tm_mon + 1;
d = now->tm_mday;
return;
}
int y1, m1 = 1, d1 = 1;
const char* digits = "0123456789";
std::string::size_type p1 = s.find_first_not_of(digits);
if (p1 == std::string::npos)
y1 = val<int>(s);
else if (s[p1] != '-')
throw GeographicErr("Delimiter not hyphen in date " + s);
else if (p1 == 0)
throw GeographicErr("Empty year field in date " + s);
else {
y1 = val<int>(s.substr(0, p1));
if (++p1 == s.size())
throw GeographicErr("Empty month field in date " + s);
std::string::size_type p2 = s.find_first_not_of(digits, p1);
if (p2 == std::string::npos)
m1 = val<int>(s.substr(p1));
else if (s[p2] != '-')
throw GeographicErr("Delimiter not hyphen in date " + s);
else if (p2 == p1)
throw GeographicErr("Empty month field in date " + s);
else {
m1 = val<int>(s.substr(p1, p2 - p1));
if (++p2 == s.size())
throw GeographicErr("Empty day field in date " + s);
d1 = val<int>(s.substr(p2));
}
}
y = y1; m = m1; d = d1;
}
/**
* 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.
**********************************************************************/
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 &minus;1).
* @exception std::bad_alloc if memory for the string can't be allocated.
* @return the string representation.
*
* If \e p &ge; 0, then the number fixed format is used with p bits of
* precision. With p < 0, there is no manipulation of the format.
**********************************************************************/
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 << x; return s.str();
}
/**
* Convert a Math::real object to a string.
*
* @param[in] x the value to be converted.
* @param[in] p the precision used (default &minus;1).
* @exception std::bad_alloc if memory for the string can't be allocated.
* @return the string representation.
*
* If \e p &ge; 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.
**********************************************************************/
static std::string str(Math::real x, int p = -1) {
using std::isfinite;
if (!isfinite(x))
return x < 0 ? std::string("-inf") :
(x > 0 ? std::string("inf") : std::string("nan"));
std::ostringstream s;
#if GEOGRAPHICLIB_PRECISION == 4
// boost-quadmath treats precision == 0 as "use as many digits as
// necessary" (see https://svn.boost.org/trac/boost/ticket/10103), so...
using std::floor; using std::fmod;
if (p == 0) {
x += Math::real(0.5);
Math::real ix = floor(x);
// Implement the "round ties to even" rule
x = (ix == x && fmod(ix, Math::real(2)) == 1) ? ix - 1 : ix;
s << std::fixed << std::setprecision(1) << x;
std::string r(s.str());
// strip off trailing ".0"
return r.substr(0, (std::max)(int(r.size()) - 2, 0));
}
#endif
if (p >= 0) s << std::fixed << std::setprecision(p);
s << 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) {
unsigned
beg = 0,
end = unsigned(s.size());
while (beg < end && isspace(s[beg]))
++beg;
while (beg < end && isspace(s[end - 1]))
--end;
return std::string(s, beg, end-beg);
}
/**
* 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;
}
/**
* \deprecated An old name for val<T>(s).
**********************************************************************/
template<typename T>
GEOGRAPHICLIB_DEPRECATED("Use Utility::val<T>(s)")
static T num(const std::string& s) {
return val<T>(s);
}
/**
* 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 (&plusmn;&infin;, 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")
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));
}
/**
* 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
* &minus;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) {
std::string::size_type r = s.find(char(std::toupper(c)));
return r == std::string::npos ? -1 : int(r);
}
/**
* 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
* &minus;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) {
const char* p = std::strchr(s, std::toupper(c));
return p != NULL ? int(p - s) : -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.
**********************************************************************/
template<typename ExtT, typename IntT, bool bigendp>
static void readarray(std::istream& str, IntT array[], size_t num) {
#if GEOGRAPHICLIB_PRECISION < 4
if (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 (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 and cast to IntT
array[i++] = IntT(bigendp == Math::bigendian ? buffer[j] :
Math::swab<ExtT>(buffer[j]));
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 (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] delim delimiter to separate KEY and VALUE, if NULL use first
* space character.
* @exception std::bad_alloc if memory for the internal strings can't be
* allocated.
* @return whether a key was found.
*
* A "#" character and everything after it are discarded and the result
* trimmed of leading and trailing white space. Use the delimiter
* character (or, if it is NULL, 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 delim);
/**
* Parse a KEY VALUE line.
*
* @param[in] line the input line.
* @param[out] key the KEY.
* @param[out] value the VALUE.
* @exception std::bad_alloc if memory for the internal strings can't be
* allocated.
* @return whether a key was found.
*
* \note This is a transition routine. At some point \e delim will be made
* an optional argument in the previous version of ParseLine and this
* version will be removed.
**********************************************************************/
static bool ParseLine(const std::string& line,
std::string& key, std::string& value);
/**
* 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).
**********************************************************************/
static int set_digits(int ndigits = 0);
};
/**
* The specialization of Utility::val<T>() for strings.
**********************************************************************/
template<> inline std::string Utility::val<std::string>(const std::string& s)
{ return trim(s); }
/**
* The specialization of Utility::val<T>() for bools.
**********************************************************************/
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");
}
} // namespace GeographicLib
#if defined(_MSC_VER)
# pragma warning (pop)
#endif
#endif // GEOGRAPHICLIB_UTILITY_HPP