diff --git a/cmake/3rdPartyTargets.cmake b/cmake/3rdPartyTargets.cmake index 37ebfa8..92bbb5d 100644 --- a/cmake/3rdPartyTargets.cmake +++ b/cmake/3rdPartyTargets.cmake @@ -21,6 +21,18 @@ ELSE () MESSAGE(FATAL_ERROR "Unsupported compiler") ENDIF () +# define the import target of GeographicLib +ADD_LIBRARY(GeographicLib STATIC IMPORTED) +IF (MSVC) + SET_TARGET_PROPERTIES(GeographicLib PROPERTIES + IMPORTED_LOCATION_DEBUG "${CMAKE_SOURCE_DIR}/external/lib/Geographic_d.lib" + IMPORTED_LOCATION_RELEASE "${CMAKE_SOURCE_DIR}/external/lib/Geographic.lib" + ) + TARGET_INCLUDE_DIRECTORIES(GeographicLib INTERFACE "${CMAKE_SOURCE_DIR}/external/include") +ELSE () + MESSAGE(FATAL_ERROR "Unsupported compiler") +ENDIF () + # define the import target of libsodium ADD_LIBRARY(libsodium STATIC IMPORTED) IF (MSVC) diff --git a/external/include/GeographicLib/Accumulator.hpp b/external/include/GeographicLib/Accumulator.hpp new file mode 100644 index 0000000..577b556 --- /dev/null +++ b/external/include/GeographicLib/Accumulator.hpp @@ -0,0 +1,198 @@ +/** + * \file Accumulator.hpp + * \brief Header for GeographicLib::Accumulator class + * + * Copyright (c) Charles Karney (2010-2020) 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 + +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, + * Adaptive Precision + * Floating-Point Arithmetic and Fast Robust Geometric Predicates, + * Discrete & Computational Geometry 18(3) 305--363 (1997). + * + * Approximate timings (summing a vector) + * - double: 2ns + * - Accumulator: 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 + 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 Accumulator a = 5;. + * + * @param[in] y set \e sum = \e y. + **********************************************************************/ + Accumulator(T y = T(0)) : _s(y), _t(0) { + static_assert(!std::numeric_limits::is_integer, + "Accumulator type is not floating point"); + } + /** + * Set the accumulator to a number. + * + * @param[in] y set \e sum = \e y. + **********************************************************************/ + Accumulator& operator=(T y) { _s = y; _t = 0; return *this; } + /** + * Return the value held in the accumulator. + * + * @return \e sum. + **********************************************************************/ + T operator()() const { return _s; } + /** + * Return the result of adding a number to \e sum (but don't change \e + * sum). + * + * @param[in] y the number to be added to the sum. + * @return \e sum + \e y. + **********************************************************************/ + T operator()(T y) const { return Sum(y); } + /** + * Add a number to the accumulator. + * + * @param[in] y set \e sum += \e y. + **********************************************************************/ + Accumulator& operator+=(T y) { Add(y); return *this; } + /** + * Subtract a number from the accumulator. + * + * @param[in] y set \e sum -= \e y. + **********************************************************************/ + Accumulator& operator-=(T y) { Add(-y); return *this; } + /** + * Multiply accumulator by an integer. To avoid loss of accuracy, use only + * integers such that \e n × \e T is exactly representable as a \e T + * (i.e., ± powers of two). Use \e n = −1 to negate \e sum. + * + * @param[in] n set \e sum *= \e n. + **********************************************************************/ + Accumulator& operator*=(int n) { _s *= n; _t *= n; return *this; } + /** + * Multiply accumulator by a number. The fma (fused multiply and add) + * instruction is used (if available) in order to maintain accuracy. + * + * @param[in] y set \e sum *= \e y. + **********************************************************************/ + Accumulator& operator*=(T y) { + using std::fma; + T d = _s; _s *= y; + d = fma(y, d, -_s); // the error in the first multiplication + _t = fma(y, _t, d); // add error to the second term + return *this; + } + /** + * Reduce accumulator to the range [-y/2, y/2]. + * + * @param[in] y the modulus. + **********************************************************************/ + Accumulator& remainder(T y) { + using std::remainder; + _s = remainder(_s, y); + Add(0); // This renormalizes the result. + return *this; + } + /** + * Test equality of an Accumulator with a number. + **********************************************************************/ + bool operator==(T y) const { return _s == y; } + /** + * Test inequality of an Accumulator with a number. + **********************************************************************/ + bool operator!=(T y) const { return _s != y; } + /** + * Less operator on an Accumulator and a number. + **********************************************************************/ + bool operator<(T y) const { return _s < y; } + /** + * Less or equal operator on an Accumulator and a number. + **********************************************************************/ + bool operator<=(T y) const { return _s <= y; } + /** + * Greater operator on an Accumulator and a number. + **********************************************************************/ + bool operator>(T y) const { return _s > y; } + /** + * Greater or equal operator on an Accumulator and a number. + **********************************************************************/ + bool operator>=(T y) const { return _s >= y; } + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_ACCUMULATOR_HPP diff --git a/external/include/GeographicLib/AlbersEqualArea.hpp b/external/include/GeographicLib/AlbersEqualArea.hpp new file mode 100644 index 0000000..a759d01 --- /dev/null +++ b/external/include/GeographicLib/AlbersEqualArea.hpp @@ -0,0 +1,321 @@ +/** + * \file AlbersEqualArea.hpp + * \brief Header for GeographicLib::AlbersEqualArea class + * + * Copyright (c) Charles Karney (2010-2021) 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 + +namespace GeographicLib { + + /** + * \brief Albers equal area conic projection + * + * Implementation taken from the report, + * - J. P. Snyder, + * Map Projections: A + * Working Manual, USGS Professional Paper 1395 (1987), + * pp. 101--102. + * + * This is a implementation of the equations in Snyder except that divided + * differences will be [have been] used to transform the expressions into + * ones which may be evaluated accurately. [In this implementation, the + * projection correctly becomes the cylindrical equal area or the azimuthal + * equal area projection when the standard latitude is the equator or a + * pole.] + * + * The ellipsoid parameters, the standard parallels, and the scale on the + * standard parallels are set in the constructor. Internally, the case with + * two standard parallels is converted into a single standard parallel, the + * latitude of minimum azimuthal scale, with an azimuthal scale specified on + * this parallel. This latitude is also used as the latitude of origin which + * is returned by AlbersEqualArea::OriginLatitude. The azimuthal scale on + * the latitude of origin is given by AlbersEqualArea::CentralScale. The + * case with two standard parallels at opposite poles is singular and is + * disallowed. The central meridian (which is a trivial shift of the + * longitude) is specified as the \e lon0 argument of the + * AlbersEqualArea::Forward and AlbersEqualArea::Reverse functions. + * AlbersEqualArea::Forward and AlbersEqualArea::Reverse also return the + * meridian convergence, γ, and azimuthal scale, \e k. A small square + * aligned with the cardinal directions is projected to a rectangle with + * dimensions \e k (in the E-W direction) and 1/\e k (in the N-S direction). + * The E-W sides of the rectangle are oriented γ degrees + * 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 + * + * ConicProj 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 − \e f) \e a, or \e k0 is + * not positive. + * @exception GeographicErr if \e stdlat is not in [−90°, + * 90°]. + **********************************************************************/ + AlbersEqualArea(real a, real f, real stdlat, real k0); + + /** + * Constructor with two standard parallels. + * + * @param[in] a equatorial radius of ellipsoid (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @param[in] stdlat1 first standard parallel (degrees). + * @param[in] stdlat2 second standard parallel (degrees). + * @param[in] k1 azimuthal scale on the standard parallels. + * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k1 is + * not positive. + * @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in + * [−90°, 90°], or if \e stdlat1 and \e stdlat2 are + * opposite poles. + **********************************************************************/ + AlbersEqualArea(real a, real f, real stdlat1, real stdlat2, real k1); + + /** + * Constructor with two standard parallels specified by sines and cosines. + * + * @param[in] a equatorial radius of ellipsoid (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @param[in] sinlat1 sine of first standard parallel. + * @param[in] coslat1 cosine of first standard parallel. + * @param[in] sinlat2 sine of second standard parallel. + * @param[in] coslat2 cosine of second standard parallel. + * @param[in] k1 azimuthal scale on the standard parallels. + * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k1 is + * not positive. + * @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in + * [−90°, 90°], or if \e stdlat1 and \e stdlat2 are + * opposite poles. + * + * This allows parallels close to the poles to be specified accurately. + * This routine computes the latitude of origin and the azimuthal scale at + * this latitude. If \e dlat = abs(\e lat2 − \e lat1) ≤ 160°, + * then the error in the latitude of origin is less than 4.5 × + * 10−14d;. + **********************************************************************/ + AlbersEqualArea(real a, real f, + real sinlat1, real coslat1, + real sinlat2, real coslat2, + real k1); + + /** + * Set the azimuthal scale for the projection. + * + * @param[in] lat (degrees). + * @param[in] k azimuthal scale at latitude \e lat (default 1). + * @exception GeographicErr \e k is not positive. + * @exception GeographicErr if \e lat is not in (−90°, + * 90°). + * + * This allows a "latitude of conformality" to be specified. + **********************************************************************/ + void SetScale(real lat, real k = real(1)); + + /** + * Forward projection, from geographic to Lambert conformal conic. + * + * @param[in] lon0 central meridian longitude (degrees). + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k azimuthal scale of projection at point; the radial + * scale is the 1/\e k. + * + * The latitude origin is given by AlbersEqualArea::LatitudeOrigin(). No + * false easting or northing is added and \e lat should be in the range + * [−90°, 90°]. The values of \e x and \e y returned for + * points which project to infinity (i.e., one or both of the poles) will + * be large but finite. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y, real& gamma, real& k) const; + + /** + * Reverse projection, from Lambert conformal conic to geographic. + * + * @param[in] lon0 central meridian longitude (degrees). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k azimuthal scale of projection at point; the radial + * scale is the 1/\e k. + * + * The latitude origin is given by AlbersEqualArea::LatitudeOrigin(). No + * false easting or northing is added. The value of \e lon returned is in + * the range [−180°, 180°]. The value of \e lat returned is + * in the range [−90°, 90°]. If the input point is outside + * the legal projected space the nearest pole is returned. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon, real& gamma, real& k) const; + + /** + * AlbersEqualArea::Forward without returning the convergence and + * scale. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y) const { + real gamma, k; + Forward(lon0, lat, lon, x, y, gamma, k); + } + + /** + * AlbersEqualArea::Reverse without returning the convergence and + * scale. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon) const { + real gamma, k; + Reverse(lon0, x, y, lat, lon, gamma, k); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _a; } + + /** + * @return \e f the flattening of the ellipsoid. This is the value used in + * the constructor. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * @return latitude of the origin for the projection (degrees). + * + * This is the latitude of minimum azimuthal scale and equals the \e stdlat + * in the 1-parallel constructor and lies between \e stdlat1 and \e stdlat2 + * in the 2-parallel constructors. + **********************************************************************/ + Math::real OriginLatitude() const { return _lat0; } + + /** + * @return central scale for the projection. This is the azimuthal scale + * on the latitude of origin. + **********************************************************************/ + Math::real CentralScale() const { return _k0; } + + /** + * \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°, and \e k0 = 1. This degenerates to the + * Lambert azimuthal equal area projection. + **********************************************************************/ + static const AlbersEqualArea& AzimuthalEqualAreaNorth(); + + /** + * A global instantiation of AlbersEqualArea with the WGS84 ellipsoid, \e + * stdlat = −90°, and \e k0 = 1. This degenerates to the + * Lambert azimuthal equal area projection. + **********************************************************************/ + static const AlbersEqualArea& AzimuthalEqualAreaSouth(); + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_ALBERSEQUALAREA_HPP diff --git a/external/include/GeographicLib/AzimuthalEquidistant.hpp b/external/include/GeographicLib/AzimuthalEquidistant.hpp new file mode 100644 index 0000000..05a6c41 --- /dev/null +++ b/external/include/GeographicLib/AzimuthalEquidistant.hpp @@ -0,0 +1,145 @@ +/** + * \file AzimuthalEquidistant.hpp + * \brief Header for GeographicLib::AzimuthalEquidistant class + * + * Copyright (c) Charles Karney (2009-2020) 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 +#include + +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 + * + * GeodesicProj is a command-line utility + * providing access to the functionality of AzimuthalEquidistant, Gnomonic, + * and CassiniSoldner. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT AzimuthalEquidistant { + private: + typedef Math::real real; + real eps_; + Geodesic _earth; + public: + + /** + * Constructor for AzimuthalEquidistant. + * + * @param[in] earth the Geodesic object to use for geodesic calculations. + * By default this uses the WGS84 ellipsoid. + **********************************************************************/ + explicit AzimuthalEquidistant(const Geodesic& earth = Geodesic::WGS84()); + + /** + * Forward projection, from geographic to azimuthal equidistant. + * + * @param[in] lat0 latitude of center point of projection (degrees). + * @param[in] lon0 longitude of center point of projection (degrees). + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] azi azimuth of geodesic at point (degrees). + * @param[out] rk reciprocal of azimuthal scale at point. + * + * \e lat0 and \e lat should be in the range [−90°, 90°]. + * The scale of the projection is 1 in the "radial" direction, \e azi + * clockwise from true north, and is 1/\e rk in the direction perpendicular + * to this. A call to Forward followed by a call to Reverse will return + * the original (\e lat, \e lon) (to within roundoff). + **********************************************************************/ + void Forward(real lat0, real lon0, real lat, real lon, + real& x, real& y, real& azi, real& rk) const; + + /** + * Reverse projection, from azimuthal equidistant to geographic. + * + * @param[in] lat0 latitude of center point of projection (degrees). + * @param[in] lon0 longitude of center point of projection (degrees). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] azi azimuth of geodesic at point (degrees). + * @param[out] rk reciprocal of azimuthal scale at point. + * + * \e lat0 should be in the range [−90°, 90°]. \e lat will + * be in the range [−90°, 90°] and \e lon will be in the + * range [−180°, 180°]. The scale of the projection is 1 in + * the "radial" direction, \e azi clockwise from true north, and is 1/\e rk + * in the direction perpendicular to this. A call to Reverse followed by a + * call to Forward will return the original (\e x, \e y) (to roundoff) only + * if the geodesic to (\e x, \e y) is a shortest path. + **********************************************************************/ + void Reverse(real lat0, real lon0, real x, real y, + real& lat, real& lon, real& azi, real& rk) const; + + /** + * AzimuthalEquidistant::Forward without returning the azimuth and scale. + **********************************************************************/ + void Forward(real lat0, real lon0, real lat, real lon, + real& x, real& y) const { + real azi, rk; + Forward(lat0, lon0, lat, lon, x, y, azi, rk); + } + + /** + * AzimuthalEquidistant::Reverse without returning the azimuth and scale. + **********************************************************************/ + void Reverse(real lat0, real lon0, real x, real y, + real& lat, real& lon) const { + real azi, rk; + Reverse(lat0, lon0, x, y, lat, lon, azi, rk); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _earth.Flattening(); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_AZIMUTHALEQUIDISTANT_HPP diff --git a/external/include/GeographicLib/CassiniSoldner.hpp b/external/include/GeographicLib/CassiniSoldner.hpp new file mode 100644 index 0000000..fdc0957 --- /dev/null +++ b/external/include/GeographicLib/CassiniSoldner.hpp @@ -0,0 +1,210 @@ +/** + * \file CassiniSoldner.hpp + * \brief Header for GeographicLib::CassiniSoldner class + * + * Copyright (c) Charles Karney (2009-2020) 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 +#include +#include + +namespace GeographicLib { + + /** + * \brief Cassini-Soldner projection + * + * Cassini-Soldner projection centered at an arbitrary position, \e lat0, \e + * lon0, on the ellipsoid. This projection is a transverse cylindrical + * equidistant projection. The projection from (\e lat, \e lon) to easting + * and northing (\e x, \e y) is defined by geodesics as follows. Go north + * along a geodesic a distance \e y from the central point; then turn + * clockwise 90° and go a distance \e x along a geodesic. + * (Although the initial heading is north, this changes to south if the pole + * is crossed.) This procedure uniquely defines the reverse projection. The + * forward projection is constructed as follows. Find the point (\e lat1, \e + * lon1) on the meridian closest to (\e lat, \e lon). Here we consider the + * full meridian so that \e lon1 may be either \e lon0 or \e lon0 + + * 180°. \e x is the geodesic distance from (\e lat1, \e lon1) to + * (\e lat, \e lon), appropriately signed according to which side of the + * central meridian (\e lat, \e lon) lies. \e y is the shortest distance + * along the meridian from (\e lat0, \e lon0) to (\e lat1, \e lon1), again, + * appropriately signed according to the initial heading. [Note that, in the + * case of prolate ellipsoids, the shortest meridional path from (\e lat0, \e + * lon0) to (\e lat1, \e lon1) may not be the shortest path.] This procedure + * uniquely defines the forward projection except for a small class of points + * for which there may be two equally short routes for either leg of the + * path. + * + * Because of the properties of geodesics, the (\e x, \e y) grid is + * orthogonal. The scale in the easting direction is unity. The scale, \e + * k, in the northing direction is unity on the central meridian and + * increases away from the central meridian. The projection routines return + * \e azi, the true bearing of the easting direction, and \e rk = 1/\e k, the + * reciprocal of the scale in the northing direction. + * + * The conversions all take place using a Geodesic object (by default + * Geodesic::WGS84()). For more information on geodesics see \ref geodesic. + * The determination of (\e lat1, \e lon1) in the forward projection is by + * solving the inverse geodesic problem for (\e lat, \e lon) and its twin + * obtained by reflection in the meridional plane. The scale is found by + * determining where two neighboring geodesics intersecting the central + * meridian at \e lat1 and \e lat1 + \e dlat1 intersect and taking the ratio + * of the reduced lengths for the two geodesics between that point and, + * respectively, (\e lat1, \e lon1) and (\e lat, \e lon). + * + * Example of use: + * \include example-CassiniSoldner.cpp + * + * GeodesicProj is a command-line utility + * providing access to the functionality of AzimuthalEquidistant, Gnomonic, + * and CassiniSoldner. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT CassiniSoldner { + private: + typedef Math::real real; + Geodesic _earth; + GeodesicLine _meridian; + real _sbet0, _cbet0; + static const unsigned maxit_ = 10; + + public: + + /** + * Constructor for CassiniSoldner. + * + * @param[in] earth the Geodesic object to use for geodesic calculations. + * By default this uses the WGS84 ellipsoid. + * + * This constructor makes an "uninitialized" object. Call Reset to set the + * central latitude and longitude, prior to calling Forward and Reverse. + **********************************************************************/ + explicit CassiniSoldner(const Geodesic& earth = Geodesic::WGS84()); + + /** + * Constructor for CassiniSoldner specifying a center point. + * + * @param[in] lat0 latitude of center point of projection (degrees). + * @param[in] lon0 longitude of center point of projection (degrees). + * @param[in] earth the Geodesic object to use for geodesic calculations. + * By default this uses the WGS84 ellipsoid. + * + * \e lat0 should be in the range [−90°, 90°]. + **********************************************************************/ + CassiniSoldner(real lat0, real lon0, + const Geodesic& earth = Geodesic::WGS84()); + + /** + * Set the central point of the projection + * + * @param[in] lat0 latitude of center point of projection (degrees). + * @param[in] lon0 longitude of center point of projection (degrees). + * + * \e lat0 should be in the range [−90°, 90°]. + **********************************************************************/ + void Reset(real lat0, real lon0); + + /** + * Forward projection, from geographic to Cassini-Soldner. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] azi azimuth of easting direction at point (degrees). + * @param[out] rk reciprocal of azimuthal northing scale at point. + * + * \e lat should be in the range [−90°, 90°]. A call to + * Forward followed by a call to Reverse will return the original (\e lat, + * \e lon) (to within roundoff). The routine does nothing if the origin + * has not been set. + **********************************************************************/ + void Forward(real lat, real lon, + real& x, real& y, real& azi, real& rk) const; + + /** + * Reverse projection, from Cassini-Soldner to geographic. + * + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] azi azimuth of easting direction at point (degrees). + * @param[out] rk reciprocal of azimuthal northing scale at point. + * + * A call to Reverse followed by a call to Forward will return the original + * (\e x, \e y) (to within roundoff), provided that \e x and \e y are + * sufficiently small not to "wrap around" the earth. The routine does + * nothing if the origin has not been set. + **********************************************************************/ + void Reverse(real x, real y, + real& lat, real& lon, real& azi, real& rk) const; + + /** + * CassiniSoldner::Forward without returning the azimuth and scale. + **********************************************************************/ + void Forward(real lat, real lon, + real& x, real& y) const { + real azi, rk; + Forward(lat, lon, x, y, azi, rk); + } + + /** + * CassiniSoldner::Reverse without returning the azimuth and scale. + **********************************************************************/ + void Reverse(real x, real y, + real& lat, real& lon) const { + real azi, rk; + Reverse(x, y, lat, lon, azi, rk); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return true if the object has been initialized. + **********************************************************************/ + bool Init() const { return _meridian.Init(); } + + /** + * @return \e lat0 the latitude of origin (degrees). + **********************************************************************/ + Math::real LatitudeOrigin() const + { return _meridian.Latitude(); } + + /** + * @return \e lon0 the longitude of origin (degrees). + **********************************************************************/ + Math::real LongitudeOrigin() const + { return _meridian.Longitude(); } + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _earth.Flattening(); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_CASSINISOLDNER_HPP diff --git a/external/include/GeographicLib/CircularEngine.hpp b/external/include/GeographicLib/CircularEngine.hpp new file mode 100644 index 0000000..ef43a21 --- /dev/null +++ b/external/include/GeographicLib/CircularEngine.hpp @@ -0,0 +1,195 @@ +/** + * \file CircularEngine.hpp + * \brief Header for GeographicLib::CircularEngine class + * + * Copyright (c) Charles Karney (2011-2015) 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 +#include +#include + +#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 _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(_M + 1, 0)) + , _ws(std::vector(_M + 1, 0)) + , _wrc(std::vector(_gradp ? _M + 1 : 0, 0)) + , _wrs(std::vector(_gradp ? _M + 1 : 0, 0)) + , _wtc(std::vector(_gradp ? _M + 1 : 0, 0)) + , _wts(std::vector(_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 sinlon2 + + * coslon2 = 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 sinlon2 + + * coslon2 = 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 diff --git a/external/include/GeographicLib/Config.h b/external/include/GeographicLib/Config.h new file mode 100644 index 0000000..74fcd0e --- /dev/null +++ b/external/include/GeographicLib/Config.h @@ -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 diff --git a/external/include/GeographicLib/Constants.hpp b/external/include/GeographicLib/Constants.hpp new file mode 100644 index 0000000..e184f9b --- /dev/null +++ b/external/include/GeographicLib/Constants.hpp @@ -0,0 +1,329 @@ +/** + * \file Constants.hpp + * \brief Header for GeographicLib::Constants class + * + * Copyright (c) Charles Karney (2008-2020) 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 + +/** + * @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 +#include +#include + +/** + * \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(). + **********************************************************************/ + 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 static T WGS84_a() + { return 6378137 * meter(); } + /** + * @tparam T the type of the returned value. + * @return the flattening of WGS84 ellipsoid (1/298.257223563). + **********************************************************************/ + template 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 + * m3 s−2. + **********************************************************************/ + template static T WGS84_GM() + { return T(3986004) * 100000000 + 41800000; } + /** + * @tparam T the type of the returned value. + * @return the angular velocity of the WGS84 ellipsoid, ω, in rad + * s−1. + **********************************************************************/ + template 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 static T GRS80_a() + { return 6378137 * meter(); } + /** + * @tparam T the type of the returned value. + * @return the gravitational constant of the GRS80 ellipsoid, \e GM, in + * m3 s−2. + **********************************************************************/ + template static T GRS80_GM() + { return T(3986005) * 100000000; } + /** + * @tparam T the type of the returned value. + * @return the angular velocity of the GRS80 ellipsoid, ω, in rad + * s−1. + * + * This is about 2 π 366.25 / (365.25 × 24 × 3600) rad + * s−1. 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 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, + * J2. + **********************************************************************/ + template 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 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 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 static T meter() { return T(1); } + /** + * @return the number of meters in a kilometer. + **********************************************************************/ + static Math::real kilometer() + { return 1000 * meter(); } + /** + * @return the number of meters in a nautical mile (approximately 1 arc + * minute) + **********************************************************************/ + static Math::real nauticalmile() + { return 1852 * meter(); } + + /** + * @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 static T square_meter() + { return meter() * meter(); } + /** + * @return the number of square meters in a hectare. + **********************************************************************/ + static Math::real hectare() + { return 10000 * square_meter(); } + /** + * @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(); } + /** + * @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(); } + ///@} + }; + + /** + * \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 diff --git a/external/include/GeographicLib/DMS.hpp b/external/include/GeographicLib/DMS.hpp new file mode 100644 index 0000000..23f4654 --- /dev/null +++ b/external/include/GeographicLib/DMS.hpp @@ -0,0 +1,405 @@ +/** + * \file DMS.hpp + * \brief Header for GeographicLib::DMS class + * + * Copyright (c) Charles Karney (2008-2020) 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 +#include + +#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), " (double quote), and these components may only be + * given in this order. Any (but not all) components may be omitted and + * other symbols (e.g., the ° symbol for degrees and the unicode prime + * and double prime symbols for minutes and seconds) may be substituted; + * two single quotes can be used instead of ". The last component + * indicator may be omitted and is assumed to be the next smallest unit + * (thus 33d10 is interpreted as 33d10'). The final component may be a + * decimal fraction but the non-final components must be integers. Instead + * of using d, ', and " to indicate degrees, minutes, and seconds, : + * (colon) may be used to separate these components (numbers must + * appear before and after each colon); thus 50d30'10.3" may be + * written as 50:30:10.3, 5.5' may be written 0:5.5, and so on. The + * integer parts of the minutes and seconds components must be less + * than 60. A single leading sign is permitted. A hemisphere designator + * (N, E, W, S) may be added to the beginning or end of the string. The + * result is multiplied by the implied sign of the hemisphere designator + * (negative for S and W). In addition \e ind is set to DMS::LATITUDE if N + * or S is present, to DMS::LONGITUDE if E or W is present, and to + * DMS::NONE otherwise. Throws an error on a malformed string. No check + * is performed on the range of the result. Examples of legal and illegal + * strings are + * - LEGAL (all the entries on each line are equivalent) + * - -20.51125, 20d30'40.5"S, -20°30'40.5, -20d30.675, + * N-20d30'40.5", -20:30:40.5 + * - 4d0'9, 4d9", 4d9'', 4:0:9, 004:00:09, 4.0025, 4.0025d, 4d0.15, + * 04:.15 + * - 4:59.99999999999999, 4:60.0, 4:59:59.9999999999999, 4:59:60.0, 5 + * - ILLEGAL (the exception thrown explains the problem) + * - 4d5"4', 4::5, 4:5:, :4:5, 4d4.5'4", -N20.5, 1.8e2d, 4:60, + * 4:59:60 + * + * The decoding operation can also perform addition and subtraction + * operations. If the string includes internal 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 + * S3-2.5+4.1N is parsed as the sum of S3, + * -2.5, +4.1N. 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 + * - LEGAL (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 + * - ILLEGAL (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 + * 7.0E1 is illegal, while 7.0E+1 is parsed as + * (7.0E) + (+1), yielding the same result as + * 8.0E. + * + * \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 + * UTF-8 encoding. (The + * JavaScript implementation of this class uses unicode natively, of + * course.) + * + * Here is the list of Unicode symbols supported for degrees, minutes, + * seconds, and the plus and minus signs; various symbols denoting variants + * of a space, which may separate the components of a DMS string, are + * removed: + * - degrees: + * - d, D lower and upper case letters + * - U+00b0 degree symbol (°) + * - U+00ba masculine ordinal indicator (º) + * - U+2070 superscript zero (⁰) + * - U+02da ring above (˚) + * - U+2218 compose function (∘) + * - * the GRiD symbol for degrees + * - minutes: + * - ' apostrophe + * - ` grave accent + * - U+2032 prime (′) + * - U+2035 back prime (‵) + * - U+00b4 acute accent (´) + * - U+2018 left single quote (‘) + * - U+2019 right single quote (’) + * - U+201b reversed-9 single quote (‛) + * - U+02b9 modifier letter prime (ʹ) + * - U+02ca modifier letter acute accent (ˊ) + * - U+02cb modifier letter grave accent (ˋ) + * - seconds: + * - " quotation mark + * - U+2033 double prime (″) + * - U+2036 reversed double prime (‶) + * + U+02dd double acute accent (˝) + * - U+201c left double quote (“) + * - U+201d right double quote (”) + * - U+201f reversed-9 double quote (‟) + * - U+02ba modifier letter double prime (ʺ) + * - ' ' any two consecutive symbols for minutes + * - plus sign: + * - + plus + * - U+2795 heavy plus (➕) + * - U+2064 invisible plus (|⁤|) + * - minus sign: + * - - hyphen + * - U+2010 dash (‐) + * - U+2011 non-breaking hyphen (‑) + * - U+2013 en dash (–) + * - U+2014 em dash (—) + * - U+2212 minus sign (−) + * - U+2796 heavy minus (➖) + * - ignored spaces: + * - U+00a0 non-breaking space + * - U+2007 figure space (| |) + * - U+2009 thin space (| |) + * - U+200a hair space ( | |) + * - U+200b invisible space (|​|) + * - U+202f narrow space ( | |) + * - U+2063 invisible separator (|⁣|) + * . + * The codes with a leading zero byte, e.g., U+00b0, are accepted in their + * UTF-8 coded form 0xc2 0xb0 and as a single byte 0xb0. + **********************************************************************/ + static Math::real Decode(const std::string& dms, flag& ind); + + /** + * Convert DMS to an angle. + * + * @param[in] d degrees. + * @param[in] m arc minutes. + * @param[in] s arc seconds. + * @return angle (degrees) + * + * This does not propagate the sign on \e d to the other components, + * so -3d20' would need to be represented as - DMS::Decode(3.0, 20.0) or + * DMS::Decode(-3.0, -20.0). + **********************************************************************/ + static Math::real Decode(real d, real m = 0, real s = 0) + { return d + (m + s / 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 [−90°, + * 90°]. + * + * By default, the \e lat (resp., \e lon) is assigned to the results of + * decoding \e dmsa (resp., \e dmsb). However this is overridden if either + * \e dmsa or \e dmsb contain a latitude or longitude hemisphere designator + * (N, S, E, W). If an exception is thrown, \e lat and \e lon are + * unchanged. + **********************************************************************/ + static void DecodeLatLon(const std::string& dmsa, const std::string& dmsb, + real& lat, real& lon, + bool longfirst = false); + + /** + * Convert a string to an angle in degrees. + * + * @param[in] angstr input string. + * @exception GeographicErr if \e angstr is malformed. + * @exception GeographicErr if \e angstr includes a hemisphere designator. + * @return angle (degrees) + * + * No hemisphere designator is allowed and no check is done on the range of + * the result. + **********************************************************************/ + static Math::real DecodeAngle(const std::string& angstr); + + /** + * Convert a string to an azimuth in degrees. + * + * @param[in] azistr input string. + * @exception GeographicErr if \e azistr is malformed. + * @exception GeographicErr if \e azistr includes a N/S designator. + * @return azimuth (degrees) reduced to the range [−180°, + * 180°]. + * + * A hemisphere designator E/W can be used; the result is multiplied by + * −1 if W is present. + **********************************************************************/ + static Math::real DecodeAzimuth(const std::string& azistr); + + /** + * Convert angle (in degrees) into a DMS string (using d, ', and "). + * + * @param[in] angle input angle (degrees) + * @param[in] trailing DMS::component value indicating the trailing units + * of the string (this component is given as a decimal number if + * necessary). + * @param[in] prec the number of digits after the decimal point for the + * trailing component. + * @param[in] ind DMS::flag value indicating additional formatting. + * @param[in] dmssep if non-null, use as the DMS separator character + * (instead of d, ', " delimiters). + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return formatted string + * + * The interpretation of \e ind is as follows: + * - ind == DMS::NONE, signed result no leading zeros on degrees except in + * the units place, e.g., -8d03'. + * - ind == DMS::LATITUDE, trailing N or S hemisphere designator, no sign, + * pad degrees to 2 digits, e.g., 08d03'S. + * - ind == DMS::LONGITUDE, trailing E or W hemisphere designator, no + * sign, pad degrees to 3 digits, e.g., 008d03'W. + * - ind == DMS::AZIMUTH, convert to the range [0, 360°), no + * sign, pad degrees to 3 digits, e.g., 351d57'. + * . + * The integer parts of the minutes and seconds components are always given + * with 2 digits. + **********************************************************************/ + static std::string Encode(real angle, component trailing, unsigned prec, + flag ind = NONE, char dmssep = char(0)); + + /** + * Convert angle into a DMS string (using d, ', and ") selecting the + * trailing component based on the precision. + * + * @param[in] angle input angle (degrees) + * @param[in] prec the precision relative to 1 degree. + * @param[in] ind DMS::flag value indicated additional formatting. + * @param[in] dmssep if non-null, use as the DMS separator character + * (instead of d, ', " delimiters). + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return formatted string + * + * \e prec indicates the precision relative to 1 degree, e.g., \e prec = 3 + * gives a result accurate to 0.1' and \e prec = 4 gives a result accurate + * to 1". \e ind is interpreted as in DMS::Encode with the additional + * facility that DMS::NUMBER represents \e angle as a number in fixed + * format with precision \e prec. + **********************************************************************/ + static std::string Encode(real angle, unsigned prec, flag ind = NONE, + char dmssep = char(0)) { + return ind == NUMBER ? Utility::str(angle, int(prec)) : + Encode(angle, + prec < 2 ? DEGREE : (prec < 4 ? MINUTE : SECOND), + prec < 2 ? prec : (prec < 4 ? prec - 2 : prec - 4), + ind, dmssep); + } + + /** + * Split angle into degrees and minutes + * + * @param[in] ang angle (degrees) + * @param[out] d degrees (an integer returned as a real) + * @param[out] m arc minutes. + **********************************************************************/ + static void Encode(real ang, real& d, real& m) { + d = int(ang); m = 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 diff --git a/external/include/GeographicLib/Ellipsoid.hpp b/external/include/GeographicLib/Ellipsoid.hpp new file mode 100644 index 0000000..6a821ce --- /dev/null +++ b/external/include/GeographicLib/Ellipsoid.hpp @@ -0,0 +1,542 @@ +/** + * \file Ellipsoid.hpp + * \brief Header for GeographicLib::Ellipsoid class + * + * Copyright (c) Charles Karney (2012-2020) 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 +#include +#include +#include + +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 −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 − \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 = (π/2) \e a. The radius + * of a sphere with the same meridian length is \e L / (π/2). + **********************************************************************/ + Math::real QuarterMeridian() const; + + /** + * @return \e A the total area of the ellipsoid (meters2). For + * a sphere \e A = 4π a2. The radius of a sphere + * with the same area is sqrt(\e A / (4π)). + **********************************************************************/ + Math::real Area() const; + + /** + * @return \e V the total volume of the ellipsoid (meters3). + * For a sphere \e V = (4π / 3) a3. The radius of + * a sphere with the same volume is cbrt(\e V / (4π/3)). + **********************************************************************/ + Math::real Volume() const + { return (4 * Math::pi()) * Math::sq(_a) * _b / 3; } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + /** \name %Ellipsoid shape + **********************************************************************/ + ///@{ + + /** + * @return \e f = (\e a − \e b) / \e a, the flattening of the + * ellipsoid. This is the value used in the constructor. This is zero, + * positive, or negative for a sphere, oblate ellipsoid, or prolate + * ellipsoid. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * @return \e f ' = (\e a − \e b) / \e b, the second flattening of + * the ellipsoid. This is zero, positive, or negative for a sphere, + * oblate ellipsoid, or prolate ellipsoid. + **********************************************************************/ + Math::real SecondFlattening() const { return _f / (1 - _f); } + + /** + * @return \e n = (\e a − \e b) / (\e a + \e b), the third flattening + * of the ellipsoid. This is zero, positive, or negative for a sphere, + * oblate ellipsoid, or prolate ellipsoid. + **********************************************************************/ + Math::real ThirdFlattening() const { return _n; } + + /** + * @return e2 = (a2 − + * b2) / a2, 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 e' 2 = (a2 − + * b2) / b2, 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 e'' 2 = (a2 − + * b2) / (a2 + b2), + * the third eccentricity squared of the ellipsoid. This is zero, + * positive, or negative for a sphere, oblate ellipsoid, or prolate + * ellipsoid. + **********************************************************************/ + Math::real ThirdEccentricitySq() const { return _e2 / (2 - _e2); } + ///@} + + /** \name Latitude conversion. + **********************************************************************/ + ///@{ + + /** + * @param[in] phi the geographic latitude (degrees). + * @return β the parametric latitude (degrees). + * + * The geographic latitude, φ, is the angle between the equatorial + * plane and a vector normal to the surface of the ellipsoid. + * + * The parametric latitude (also called the reduced latitude), β, + * allows the cartesian coordinated of a meridian to be expressed + * conveniently in parametric form as + * - \e R = \e a cos β + * - \e Z = \e b sin β + * . + * where \e a and \e b are the equatorial radius and the polar semi-axis. + * For a sphere β = φ. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * β lies in [−90°, 90°]. + **********************************************************************/ + Math::real ParametricLatitude(real phi) const; + + /** + * @param[in] beta the parametric latitude (degrees). + * @return φ the geographic latitude (degrees). + * + * β must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * φ lies in [−90°, 90°]. + **********************************************************************/ + Math::real InverseParametricLatitude(real beta) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return θ the geocentric latitude (degrees). + * + * The geocentric latitude, θ, is the angle between the equatorial + * plane and a line between the center of the ellipsoid and a point on the + * ellipsoid. For a sphere θ = φ. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * θ lies in [−90°, 90°]. + **********************************************************************/ + Math::real GeocentricLatitude(real phi) const; + + /** + * @param[in] theta the geocentric latitude (degrees). + * @return φ the geographic latitude (degrees). + * + * θ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * φ lies in [−90°, 90°]. + **********************************************************************/ + Math::real InverseGeocentricLatitude(real theta) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return μ the rectifying latitude (degrees). + * + * The rectifying latitude, μ, has the property that the distance along + * a meridian of the ellipsoid between two points with rectifying latitudes + * μ1 and μ2 is equal to + * (μ2 - μ1) \e L / 90°, + * where \e L = QuarterMeridian(). For a sphere μ = φ. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * μ lies in [−90°, 90°]. + **********************************************************************/ + Math::real RectifyingLatitude(real phi) const; + + /** + * @param[in] mu the rectifying latitude (degrees). + * @return φ the geographic latitude (degrees). + * + * μ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * φ lies in [−90°, 90°]. + **********************************************************************/ + Math::real InverseRectifyingLatitude(real mu) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return ξ the authalic latitude (degrees). + * + * The authalic latitude, ξ, has the property that the area of the + * ellipsoid between two circles with authalic latitudes + * ξ1 and ξ2 is equal to (sin + * ξ2 - sin ξ1) \e A / 2, where \e A + * = Area(). For a sphere ξ = φ. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * ξ lies in [−90°, 90°]. + **********************************************************************/ + Math::real AuthalicLatitude(real phi) const; + + /** + * @param[in] xi the authalic latitude (degrees). + * @return φ the geographic latitude (degrees). + * + * ξ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * φ lies in [−90°, 90°]. + **********************************************************************/ + Math::real InverseAuthalicLatitude(real xi) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return χ the conformal latitude (degrees). + * + * The conformal latitude, χ, gives the mapping of the ellipsoid to a + * sphere which which is conformal (angles are preserved) and in which the + * equator of the ellipsoid maps to the equator of the sphere. For a + * sphere χ = φ. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * χ lies in [−90°, 90°]. + **********************************************************************/ + Math::real ConformalLatitude(real phi) const; + + /** + * @param[in] chi the conformal latitude (degrees). + * @return φ the geographic latitude (degrees). + * + * χ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. The returned value + * φ lies in [−90°, 90°]. + **********************************************************************/ + Math::real InverseConformalLatitude(real chi) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return ψ the isometric latitude (degrees). + * + * The isometric latitude gives the mapping of the ellipsoid to a plane + * which which is conformal (angles are preserved) and in which the equator + * of the ellipsoid maps to a straight line of constant scale; this mapping + * defines the Mercator projection. For a sphere ψ = + * sinh−1 tan φ. + * + * φ must lie in the range [−90°, 90°]; the result is + * undefined if this condition does not hold. The value returned for φ + * = ±90° is some (positive or negative) large but finite value, + * such that InverseIsometricLatitude returns the original value of φ. + **********************************************************************/ + Math::real IsometricLatitude(real phi) const; + + /** + * @param[in] psi the isometric latitude (degrees). + * @return φ the geographic latitude (degrees). + * + * The returned value φ lies in [−90°, 90°]. For a + * sphere φ = tan−1 sinh ψ. + **********************************************************************/ + Math::real InverseIsometricLatitude(real psi) const; + ///@} + + /** \name Other quantities. + **********************************************************************/ + ///@{ + + /** + * @param[in] phi the geographic latitude (degrees). + * @return \e R = \e a cos β the radius of a circle of latitude + * φ (meters). \e R (π/180°) gives meters per degree + * longitude measured along a circle of latitude. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. + **********************************************************************/ + Math::real CircleRadius(real phi) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return \e Z = \e b sin β the distance of a circle of latitude + * φ from the equator measured parallel to the ellipsoid axis + * (meters). + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. + **********************************************************************/ + Math::real CircleHeight(real phi) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return \e s the distance along a meridian + * between the equator and a point of latitude φ (meters). \e s is + * given by \e s = μ \e L / 90°, where \e L = + * QuarterMeridian()). + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. + **********************************************************************/ + Math::real MeridianDistance(real phi) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return ρ the meridional radius of curvature of the ellipsoid at + * latitude φ (meters); this is the curvature of the meridian. \e + * rho is given by ρ = (180°/π) d\e s / dφ, + * where \e s = MeridianDistance(); thus ρ (π/180°) + * gives meters per degree latitude measured along a meridian. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. + **********************************************************************/ + Math::real MeridionalCurvatureRadius(real phi) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @return ν the transverse radius of curvature of the ellipsoid at + * latitude φ (meters); this is the curvature of a curve on the + * ellipsoid which also lies in a plane perpendicular to the ellipsoid + * and to the meridian. ν is related to \e R = CircleRadius() by \e + * R = ν cos φ. + * + * φ must lie in the range [−90°, 90°]; the + * result is undefined if this condition does not hold. + **********************************************************************/ + Math::real TransverseCurvatureRadius(real phi) const; + + /** + * @param[in] phi the geographic latitude (degrees). + * @param[in] azi the angle between the meridian and the normal section + * (degrees). + * @return the radius of curvature of the ellipsoid in the normal + * section at latitude φ inclined at an angle \e azi to the + * meridian (meters). + * + * φ must lie in the range [−90°, 90°]; the result is + * undefined this condition does not hold. + **********************************************************************/ + Math::real NormalCurvatureRadius(real phi, real azi) const; + ///@} + + /** \name Eccentricity conversions. + **********************************************************************/ + ///@{ + + /** + * @param[in] fp = \e f ' = (\e a − \e b) / \e b, the second + * flattening. + * @return \e f = (\e a − \e b) / \e a, the flattening. + * + * \e f ' should lie in (−1, ∞). + * The returned value \e f lies in (−∞, 1). + **********************************************************************/ + static Math::real SecondFlatteningToFlattening(real fp) + { return fp / (1 + fp); } + + /** + * @param[in] f = (\e a − \e b) / \e a, the flattening. + * @return \e f ' = (\e a − \e b) / \e b, the second flattening. + * + * \e f should lie in (−∞, 1). + * The returned value \e f ' lies in (−1, ∞). + **********************************************************************/ + static Math::real FlatteningToSecondFlattening(real f) + { return f / (1 - f); } + + /** + * @param[in] n = (\e a − \e b) / (\e a + \e b), the third + * flattening. + * @return \e f = (\e a − \e b) / \e a, the flattening. + * + * \e n should lie in (−1, 1). + * The returned value \e f lies in (−∞, 1). + **********************************************************************/ + static Math::real ThirdFlatteningToFlattening(real n) + { return 2 * n / (1 + n); } + + /** + * @param[in] f = (\e a − \e b) / \e a, the flattening. + * @return \e n = (\e a − \e b) / (\e a + \e b), the third + * flattening. + * + * \e f should lie in (−∞, 1). + * The returned value \e n lies in (−1, 1). + **********************************************************************/ + static Math::real FlatteningToThirdFlattening(real f) + { return f / (2 - f); } + + /** + * @param[in] e2 = e2 = (a2 − + * b2) / a2, the eccentricity + * squared. + * @return \e f = (\e a − \e b) / \e a, the flattening. + * + * e2 should lie in (−∞, 1). + * The returned value \e f lies in (−∞, 1). + **********************************************************************/ + static Math::real EccentricitySqToFlattening(real e2) + { using std::sqrt; return e2 / (sqrt(1 - e2) + 1); } + + /** + * @param[in] f = (\e a − \e b) / \e a, the flattening. + * @return e2 = (a2 − + * b2) / a2, the eccentricity + * squared. + * + * \e f should lie in (−∞, 1). + * The returned value e2 lies in (−∞, 1). + **********************************************************************/ + static Math::real FlatteningToEccentricitySq(real f) + { return f * (2 - f); } + + /** + * @param[in] ep2 = e' 2 = (a2 − + * b2) / b2, the second eccentricity + * squared. + * @return \e f = (\e a − \e b) / \e a, the flattening. + * + * e' 2 should lie in (−1, ∞). + * The returned value \e f lies in (−∞, 1). + **********************************************************************/ + static Math::real SecondEccentricitySqToFlattening(real ep2) + { using std::sqrt; return ep2 / (sqrt(1 + ep2) + 1 + ep2); } + + /** + * @param[in] f = (\e a − \e b) / \e a, the flattening. + * @return e' 2 = (a2 − + * b2) / b2, the second eccentricity + * squared. + * + * \e f should lie in (−∞, 1). + * The returned value e' 2 lies in (−1, ∞). + **********************************************************************/ + static Math::real FlatteningToSecondEccentricitySq(real f) + { return f * (2 - f) / Math::sq(1 - f); } + + /** + * @param[in] epp2 = e'' 2 = (a2 + * − b2) / (a2 + + * b2), the third eccentricity squared. + * @return \e f = (\e a − \e b) / \e a, the flattening. + * + * e'' 2 should lie in (−1, 1). + * The returned value \e f lies in (−∞, 1). + **********************************************************************/ + static Math::real ThirdEccentricitySqToFlattening(real epp2) { + using std::sqrt; + return 2 * epp2 / (sqrt((1 - epp2) * (1 + epp2)) + 1 + epp2); + } + + /** + * @param[in] f = (\e a − \e b) / \e a, the flattening. + * @return e'' 2 = (a2 − + * b2) / (a2 + b2), + * the third eccentricity squared. + * + * \e f should lie in (−∞, 1). + * The returned value e'' 2 lies in (−1, 1). + **********************************************************************/ + static Math::real FlatteningToThirdEccentricitySq(real f) + { return f * (2 - f) / (1 + Math::sq(1 - f)); } + + ///@} + + /** + * A global instantiation of Ellipsoid with the parameters for the WGS84 + * ellipsoid. + **********************************************************************/ + static const Ellipsoid& WGS84(); + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_ELLIPSOID_HPP diff --git a/external/include/GeographicLib/EllipticFunction.hpp b/external/include/GeographicLib/EllipticFunction.hpp new file mode 100644 index 0000000..0a353a9 --- /dev/null +++ b/external/include/GeographicLib/EllipticFunction.hpp @@ -0,0 +1,702 @@ +/** + * \file EllipticFunction.hpp + * \brief Header for GeographicLib::EllipticFunction class + * + * Copyright (c) Charles Karney (2008-2021) 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 + +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 α2). The modulus is always + * passed as its square k2 which allows \e k to be pure + * imaginary (k2 < 0). (Confusingly, Abramowitz and + * Stegun call \e m = k2 the "parameter" and \e n = + * α2 the "characteristic".) + * + * In geodesic applications, it is convenient to separate the incomplete + * integrals into secular and periodic components, e.g., + * \f[ + * E(\phi, k) = (2 E(k) / \pi) [ \phi + \delta E(\phi, k) ] + * \f] + * where δ\e E(φ, \e k) is an odd periodic function with period + * π. + * + * The computation of the elliptic integrals uses the algorithms given in + * - B. C. Carlson, + * Computation of real or + * complex elliptic integrals, 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, + * Numerical Calculation of + * Elliptic Integrals and Elliptic Functions, 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 k2. + * k2 must lie in (−∞, 1]. + * @param[in] alpha2 the parameter α2. + * α2 must lie in (−∞, 1]. + * @exception GeographicErr if \e k2 or \e alpha2 is out of its legal + * range. + * + * If only elliptic integrals of the first and second kinds are needed, + * then set α2 = 0 (the default value); in this case, we + * have Π(φ, 0, \e k) = \e F(φ, \e k), \e G(φ, 0, \e k) = \e + * E(φ, \e k), and \e H(φ, 0, \e k) = \e F(φ, \e k) - \e + * D(φ, \e k). + **********************************************************************/ + EllipticFunction(real k2 = 0, real alpha2 = 0) + { Reset(k2, alpha2); } + + /** + * Constructor specifying the modulus and parameter and their complements. + * + * @param[in] k2 the square of the modulus k2. + * k2 must lie in (−∞, 1]. + * @param[in] alpha2 the parameter α2. + * α2 must lie in (−∞, 1]. + * @param[in] kp2 the complementary modulus squared k'2 = + * 1 − k2. This must lie in [0, ∞). + * @param[in] alphap2 the complementary parameter α'2 = 1 + * − α2. This must lie in [0, ∞). + * @exception GeographicErr if \e k2, \e alpha2, \e kp2, or \e alphap2 is + * out of its legal range. + * + * The arguments must satisfy \e k2 + \e kp2 = 1 and \e alpha2 + \e alphap2 + * = 1. (No checking is done that these conditions are met.) This + * constructor is provided to enable accuracy to be maintained, e.g., when + * \e k is very close to unity. + **********************************************************************/ + EllipticFunction(real k2, real alpha2, real kp2, real alphap2) + { Reset(k2, alpha2, kp2, alphap2); } + + /** + * Reset the modulus and parameter. + * + * @param[in] k2 the new value of square of the modulus + * k2 which must lie in (−∞, ]. + * done.) + * @param[in] alpha2 the new value of parameter α2. + * α2 must lie in (−∞, 1]. + * @exception GeographicErr if \e k2 or \e alpha2 is out of its legal + * range. + **********************************************************************/ + void Reset(real k2 = 0, real alpha2 = 0) + { Reset(k2, alpha2, 1 - k2, 1 - alpha2); } + + /** + * Reset the modulus and parameter supplying also their complements. + * + * @param[in] k2 the square of the modulus k2. + * k2 must lie in (−∞, 1]. + * @param[in] alpha2 the parameter α2. + * α2 must lie in (−∞, 1]. + * @param[in] kp2 the complementary modulus squared k'2 = + * 1 − k2. This must lie in [0, ∞). + * @param[in] alphap2 the complementary parameter α'2 = 1 + * − α2. This must lie in [0, ∞). + * @exception GeographicErr if \e k2, \e alpha2, \e kp2, or \e alphap2 is + * out of its legal range. + * + * The arguments must satisfy \e k2 + \e kp2 = 1 and \e alpha2 + \e alphap2 + * = 1. (No checking is done that these conditions are met.) This + * constructor is provided to enable accuracy to be maintained, e.g., when + * is very small. + **********************************************************************/ + void Reset(real k2, real alpha2, real kp2, real alphap2); + + ///@} + + /** \name Inspector functions. + **********************************************************************/ + ///@{ + /** + * @return the square of the modulus k2. + **********************************************************************/ + Math::real k2() const { return _k2; } + + /** + * @return the square of the complementary modulus k'2 = + * 1 − k2. + **********************************************************************/ + Math::real kp2() const { return _kp2; } + + /** + * @return the parameter α2. + **********************************************************************/ + Math::real alpha2() const { return _alpha2; } + + /** + * @return the complementary parameter α'2 = 1 − + * α2. + **********************************************************************/ + 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) − \e E(\e k). + **********************************************************************/ + Math::real KE() const { return _k2 * _Dc; } + + /** + * The complete integral of the third kind. + * + * @return Π(α2, \e k). + * + * Π(α2, \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(α2, \e k). + * + * \e G(α2, \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(α2, \e k). + * + * \e H(α2, \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(φ, \e k). + * + * \e F(φ, \e k) is defined in https://dlmf.nist.gov/19.2.E4 + * \f[ + * F(\phi, k) = \int_0^\phi \frac1{\sqrt{1-k^2\sin^2\theta}}\,d\theta. + * \f] + **********************************************************************/ + Math::real F(real phi) const; + + /** + * The incomplete integral of the second kind. + * + * @param[in] phi + * @return \e E(φ, \e k). + * + * \e E(φ, \e k) is defined in https://dlmf.nist.gov/19.2.E5 + * \f[ + * E(\phi, k) = \int_0^\phi \sqrt{1-k^2\sin^2\theta}\,d\theta. + * \f] + **********************************************************************/ + Math::real E(real phi) const; + + /** + * The incomplete integral of the second kind with the argument given in + * degrees. + * + * @param[in] ang in degrees. + * @return \e E(π ang/180, \e k). + **********************************************************************/ + Math::real Ed(real ang) const; + + /** + * The inverse of the incomplete integral of the second kind. + * + * @param[in] x + * @return φ = E−1(\e x, \e k); i.e., the + * solution of such that \e E(φ, \e k) = \e x. + **********************************************************************/ + Math::real Einv(real x) const; + + /** + * The incomplete integral of the third kind. + * + * @param[in] phi + * @return Π(φ, α2, \e k). + * + * Π(φ, α2, \e k) is defined in + * https://dlmf.nist.gov/19.2.E7 + * \f[ + * \Pi(\phi, \alpha^2, k) = \int_0^\phi + * \frac1{\sqrt{1-k^2\sin^2\theta}(1 - \alpha^2\sin^2\theta)}\,d\theta. + * \f] + **********************************************************************/ + Math::real Pi(real phi) const; + + /** + * Jahnke's incomplete elliptic integral. + * + * @param[in] phi + * @return \e D(φ, \e k). + * + * \e D(φ, \e k) is defined in https://dlmf.nist.gov/19.2.E4 + * \f[ + * D(\phi, k) = \int_0^\phi + * \frac{\sin^2\theta}{\sqrt{1-k^2\sin^2\theta}}\,d\theta. + * \f] + **********************************************************************/ + Math::real D(real phi) const; + + /** + * Legendre's geodesic longitude integral. + * + * @param[in] phi + * @return \e G(φ, α2, \e k). + * + * \e G(φ, α2, \e k) is defined by + * \f[ + * \begin{align} + * G(\phi, \alpha^2, k) &= + * \frac{k^2}{\alpha^2} F(\phi, k) + + * \biggl(1 - \frac{k^2}{\alpha^2}\biggr) \Pi(\phi, \alpha^2, k) \\ + * &= \int_0^\phi + * \frac{\sqrt{1-k^2\sin^2\theta}}{1 - \alpha^2\sin^2\theta}\,d\theta. + * \end{align} + * \f] + * + * Legendre expresses the longitude of a point on the geodesic in terms of + * this combination of elliptic integrals in Exercices de Calcul + * Intégral, Vol. 1 (1811), p. 181, + * https://books.google.com/books?id=riIOAAAAQAAJ&pg=PA181. + * + * See \ref geodellip for the expression for the longitude in terms of this + * function. + **********************************************************************/ + Math::real G(real phi) const; + + /** + * Cayley's geodesic longitude difference integral. + * + * @param[in] phi + * @return \e H(φ, α2, \e k). + * + * \e H(φ, α2, \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. 40 + * (1870), p. 333, https://books.google.com/books?id=Zk0wAAAAIAAJ&pg=PA333. + * + * See \ref geodellip for the expression for the longitude in terms of this + * function. + **********************************************************************/ + Math::real H(real phi) const; + ///@} + + /** \name Incomplete integrals in terms of Jacobi elliptic functions. + **********************************************************************/ + ///@{ + /** + * The incomplete integral of the first kind in terms of Jacobi elliptic + * functions. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return \e F(φ, \e k) as though φ ∈ (−π, π]. + **********************************************************************/ + Math::real F(real sn, real cn, real dn) const; + + /** + * The incomplete integral of the second kind in terms of Jacobi elliptic + * functions. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return \e E(φ, \e k) as though φ ∈ (−π, π]. + **********************************************************************/ + Math::real E(real sn, real cn, real dn) const; + + /** + * The incomplete integral of the third kind in terms of Jacobi elliptic + * functions. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return Π(φ, α2, \e k) as though φ ∈ + * (−π, π]. + **********************************************************************/ + Math::real Pi(real sn, real cn, real dn) const; + + /** + * Jahnke's incomplete elliptic integral in terms of Jacobi elliptic + * functions. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return \e D(φ, \e k) as though φ ∈ (−π, π]. + **********************************************************************/ + Math::real D(real sn, real cn, real dn) const; + + /** + * Legendre's geodesic longitude integral in terms of Jacobi elliptic + * functions. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return \e G(φ, α2, \e k) as though φ ∈ + * (−π, π]. + **********************************************************************/ + Math::real G(real sn, real cn, real dn) const; + + /** + * Cayley's geodesic longitude difference integral in terms of Jacobi + * elliptic functions. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return \e H(φ, α2, \e k) as though φ ∈ + * (−π, π]. + **********************************************************************/ + Math::real H(real sn, real cn, real dn) const; + ///@} + + /** \name Periodic versions of incomplete elliptic integrals. + **********************************************************************/ + ///@{ + /** + * The periodic incomplete integral of the first kind. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return the periodic function π \e F(φ, \e k) / (2 \e K(\e k)) - + * φ. + **********************************************************************/ + Math::real deltaF(real sn, real cn, real dn) const; + + /** + * The periodic incomplete integral of the second kind. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return the periodic function π \e E(φ, \e k) / (2 \e E(\e k)) - + * φ. + **********************************************************************/ + Math::real deltaE(real sn, real cn, real dn) const; + + /** + * The periodic inverse of the incomplete integral of the second kind. + * + * @param[in] stau = sinτ. + * @param[in] ctau = sinτ. + * @return the periodic function E−1(τ (2 \e + * E(\e k)/π), \e k) - τ. + **********************************************************************/ + Math::real deltaEinv(real stau, real ctau) const; + + /** + * The periodic incomplete integral of the third kind. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return the periodic function π Π(φ, α2, + * \e k) / (2 Π(α2, \e k)) - φ. + **********************************************************************/ + Math::real deltaPi(real sn, real cn, real dn) const; + + /** + * The periodic Jahnke's incomplete elliptic integral. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return the periodic function π \e D(φ, \e k) / (2 \e D(\e k)) - + * φ. + **********************************************************************/ + Math::real deltaD(real sn, real cn, real dn) const; + + /** + * Legendre's periodic geodesic longitude integral. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return the periodic function π \e G(φ, \e k) / (2 \e G(\e k)) - + * φ. + **********************************************************************/ + Math::real deltaG(real sn, real cn, real dn) const; + + /** + * Cayley's periodic geodesic longitude difference integral. + * + * @param[in] sn = sinφ. + * @param[in] cn = cosφ. + * @param[in] dn = sqrt(1 − k2 + * sin2φ). + * @return the periodic function π \e H(φ, \e k) / (2 \e H(\e k)) - + * φ. + **********************************************************************/ + Math::real deltaH(real sn, real cn, real dn) const; + ///@} + + /** \name Elliptic functions. + **********************************************************************/ + ///@{ + /** + * The Jacobi 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 Δ amplitude function. + * + * @param[in] sn sinφ. + * @param[in] cn cosφ. + * @return Δ = sqrt(1 − k2 + * sin2φ). + **********************************************************************/ + 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 RF. + * + * @param[in] x + * @param[in] y + * @param[in] z + * @return RF(\e x, \e y, \e z). + * + * RF 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, + * RF with one argument zero. + * + * @param[in] x + * @param[in] y + * @return RF(\e x, \e y, 0). + **********************************************************************/ + static real RF(real x, real y); + + /** + * Degenerate symmetric integral of the first kind + * RC. + * + * @param[in] x + * @param[in] y + * @return RC(\e x, \e y) = + * RF(\e x, \e y, \e y). + * + * RC 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 RG. + * + * @param[in] x + * @param[in] y + * @param[in] z + * @return RG(\e x, \e y, \e z). + * + * RG 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, + * RG with one argument zero. + * + * @param[in] x + * @param[in] y + * @return RG(\e x, \e y, 0). + **********************************************************************/ + static real RG(real x, real y); + + /** + * Symmetric integral of the third kind RJ. + * + * @param[in] x + * @param[in] y + * @param[in] z + * @param[in] p + * @return RJ(\e x, \e y, \e z, \e p). + * + * RJ 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 + * RD. + * + * @param[in] x + * @param[in] y + * @param[in] z + * @return RD(\e x, \e y, \e z) = + * RJ(\e x, \e y, \e z, \e z). + * + * RD 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 diff --git a/external/include/GeographicLib/GARS.hpp b/external/include/GeographicLib/GARS.hpp new file mode 100644 index 0000000..9cc1212 --- /dev/null +++ b/external/include/GeographicLib/GARS.hpp @@ -0,0 +1,143 @@ +/** + * \file GARS.hpp + * \brief Header for GeographicLib::GARS class + * + * Copyright (c) Charles Karney (2015-2021) 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 + +#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 [−90°, + * 90°]. + * @exception std::bad_alloc if memory for \e gars can't be allocated. + * + * \e prec specifies the precision of \e gars as follows: + * - \e prec = 0 (min), 30' precision, e.g., 006AG; + * - \e prec = 1, 15' precision, e.g., 006AG3; + * - \e prec = 2 (max), 5' precision, e.g., 006AG39. + * + * If \e lat or \e lon is NaN, then \e gars is set to "INVALID". + **********************************************************************/ + static void Forward(real lat, real lon, int prec, std::string& gars); + + /** + * Convert from GARS to geographic coordinates. + * + * @param[in] gars the GARS. + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] prec the precision of \e gars. + * @param[in] centerp if true (the default) return the center of the + * \e gars, otherwise return the south-west corner. + * @exception GeographicErr if \e gars is illegal. + * + * The case of the letters in \e gars is ignored. \e prec is in the range + * [0, 2] and gives the precision of \e gars as follows: + * - \e prec = 0 (min), 30' precision, e.g., 006AG; + * - \e prec = 1, 15' precision, e.g., 006AG3; + * - \e prec = 2 (max), 5' precision, e.g., 006AG39. + * + * If the first 3 characters of \e gars are "INV", then \e lat and \e lon + * are set to NaN and \e prec is unchanged. + **********************************************************************/ + static void Reverse(const std::string& gars, real& lat, real& lon, + int& prec, bool centerp = true); + + /** + * The angular resolution of a GARS. + * + * @param[in] prec the precision of the GARS. + * @return the latitude-longitude resolution (degrees). + * + * Internally, \e prec is first put in the range [0, 2]. + **********************************************************************/ + static Math::real Resolution(int prec) { + return 1/real(prec <= 0 ? mult1_ : (prec == 1 ? mult1_ * mult2_ : + mult1_ * mult2_ * mult3_)); + } + + /** + * The GARS precision required to meet a given geographic resolution. + * + * @param[in] res the minimum of resolution in latitude and longitude + * (degrees). + * @return GARS precision. + * + * The returned length is in the range [0, 2]. + **********************************************************************/ + static int Precision(real res) { + using std::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 diff --git a/external/include/GeographicLib/GeoCoords.hpp b/external/include/GeographicLib/GeoCoords.hpp new file mode 100644 index 0000000..95e9f2f --- /dev/null +++ b/external/include/GeographicLib/GeoCoords.hpp @@ -0,0 +1,553 @@ +/** + * \file GeoCoords.hpp + * \brief Header for GeographicLib::GeoCoords class + * + * Copyright (c) Charles Karney (2008-2020) 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 +#include + +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 + * + * GeoConvert is a command-line utility + * providing access to the functionality of GeoCoords. + **********************************************************************/ + class GEOGRAPHICLIB_EXPORT GeoCoords { + private: + typedef Math::real real; + real _lat, _long, _easting, _northing, _gamma, _k; + bool _northp; + int _zone; // See UTMUPS::zonespec + mutable real _alt_easting, _alt_northing, _alt_gamma, _alt_k; + mutable int _alt_zone; + + void CopyToAlt() const { + _alt_easting = _easting; + _alt_northing = _northing; + _alt_gamma = _gamma; + _alt_k = _k; + _alt_zone = _zone; + } + static void UTMUPSString(int zone, bool northp, + real easting, real northing, + int prec, bool abbrev, std::string& utm); + void FixHemisphere(); + public: + + /** \name Initializing the GeoCoords object + **********************************************************************/ + ///@{ + /** + * The default constructor sets the coordinate as undefined. + **********************************************************************/ + GeoCoords() + : _lat(Math::NaN()) + , _long(Math::NaN()) + , _easting(Math::NaN()) + , _northing(Math::NaN()) + , _gamma(Math::NaN()) + , _k(Math::NaN()) + , _northp(false) + , _zone(UTMUPS::INVALID) + { CopyToAlt(); } + + /** + * Construct from a string. + * + * @param[in] s 1-element, 2-element, or 3-element string representation of + * the position. + * @param[in] centerp governs the interpretation of MGRS coordinates (see + * below). + * @param[in] longfirst governs the interpretation of geographic + * coordinates (see below). + * @exception GeographicErr if the \e s is malformed (see below). + * + * Parse as a string and interpret it as a geographic position. The input + * string is broken into space (or comma) separated pieces and Basic + * decision on which format is based on number of components + * -# MGRS + * -# "Lat Long" or "Long Lat" + * -# "Zone Easting Northing" or "Easting Northing Zone" + * + * The following inputs are approximately the same (Ar Ramadi Bridge, Iraq) + * - Latitude and Longitude + * - 33.44 43.27 + * - N33d26.4' E43d16.2' + * - 43d16'12"E 33d26'24"N + * - 43:16:12E 33:26:24 + * - MGRS + * - 38SLC30 + * - 38SLC391014 + * - 38SLC3918701405 + * - 37SHT9708 + * - UTM + * - 38n 339188 3701405 + * - 897039 3708229 37n + * + * Latitude and Longitude parsing: Latitude precedes longitude, + * unless a N, S, E, W hemisphere designator is used on one or both + * coordinates. If \e longfirst = true (default is false), then + * longitude precedes latitude in the absence of a hemisphere designator. + * Thus (with \e longfirst = false) + * - 40 -75 + * - N40 W75 + * - -75 N40 + * - 75W 40N + * - E-75 -40S + * . + * are all the same position. The coordinates may be given in + * decimal degrees, degrees and decimal minutes, degrees, minutes, + * seconds, etc. Use d, ', and " to mark off the degrees, + * minutes and seconds. Various alternative symbols for degrees, minutes, + * and seconds are allowed. Alternatively, use : to separate these + * components. A single addition or subtraction is allowed. (See + * DMS::Decode for details.) Thus + * - 40d30'30" + * - 40d30'30 + * - 40°30'30 + * - 40d30.5' + * - 40d30.5 + * - 40:30:30 + * - 40:30.5 + * - 40.508333333 + * - 40:30+0:0:30 + * - 40:31-0:0.5 + * . + * all specify the same angle. The leading sign applies to the following + * components so -1d30 is -(1+30/60) = −1.5. However, note + * that -1:30-0:0:15 is parsed as (-1:30) + (-0:0:15) = −(1+30/60) + * − (15/3600). Latitudes must be in the range [−90°, + * 90°]. Internally longitudes are reduced to the range + * [−180°, 180°]. + * + * UTM/UPS parsing: For UTM zones (−80° ≤ Lat < + * 84°), the zone designator is made up of a zone number (for 1 to 60) + * and a hemisphere letter (n or s), e.g., 38n (38north can also be used). + * The latitude band designer ([C--M] in the southern hemisphere and [N--X] + * in the northern) should NOT be used. (This is part of the MGRS + * coordinate.) The zone designator for the poles (where UPS is employed) + * is a hemisphere letter by itself, i.e., n or s (north or south can also + * be used). + * + * MGRS parsing interprets the grid references as square area at the + * specified precision (1m, 10m, 100m, etc.). If \e centerp = true (the + * default), the center of this square is then taken to be the precise + * position; thus: + * - 38SMB = 38n 450000 3650000 + * - 38SMB4484 = 38n 444500 3684500 + * - 38SMB44148470 = 38n 444145 3684705 + * . + * Otherwise, the "south-west" corner of the square is used, i.e., + * - 38SMB = 38n 400000 3600000 + * - 38SMB4484 = 38n 444000 3684000 + * - 38SMB44148470 = 38n 444140 3684700 + **********************************************************************/ + explicit GeoCoords(const std::string& s, + bool centerp = true, bool longfirst = false) + { Reset(s, centerp, longfirst); } + + /** + * Construct from geographic coordinates. + * + * @param[in] latitude (degrees). + * @param[in] longitude (degrees). + * @param[in] zone if specified, force the UTM/UPS representation to use a + * specified zone using the rules given in UTMUPS::zonespec. + * @exception GeographicErr if \e latitude is not in [−90°, + * 90°]. + * @exception GeographicErr if \e zone cannot be used for this location. + **********************************************************************/ + GeoCoords(real latitude, real longitude, int zone = UTMUPS::STANDARD) { + Reset(latitude, longitude, zone); + } + + /** + * Construct from UTM/UPS coordinates. + * + * @param[in] zone UTM zone (zero means UPS). + * @param[in] northp hemisphere (true means north, false means south). + * @param[in] easting (meters). + * @param[in] northing (meters). + * @exception GeographicErr if \e zone, \e easting, or \e northing is + * outside its allowed range. + **********************************************************************/ + GeoCoords(int zone, bool northp, real easting, real northing) { + Reset(zone, northp, easting, northing); + } + + /** + * Reset the location from a string. See + * GeoCoords(const std::string& s, bool centerp, bool longfirst). + * + * @param[in] s 1-element, 2-element, or 3-element string representation of + * the position. + * @param[in] centerp governs the interpretation of MGRS coordinates. + * @param[in] longfirst governs the interpretation of geographic + * coordinates. + * @exception GeographicErr if the \e s is malformed. + **********************************************************************/ + void Reset(const std::string& s, + bool centerp = true, bool longfirst = false); + + /** + * Reset the location in terms of geographic coordinates. See + * GeoCoords(real latitude, real longitude, int zone). + * + * @param[in] latitude (degrees). + * @param[in] longitude (degrees). + * @param[in] zone if specified, force the UTM/UPS representation to use a + * specified zone using the rules given in UTMUPS::zonespec. + * @exception GeographicErr if \e latitude is not in [−90°, + * 90°]. + * @exception GeographicErr if \e zone cannot be used for this location. + **********************************************************************/ + void Reset(real latitude, real longitude, int zone = UTMUPS::STANDARD) { + UTMUPS::Forward(latitude, longitude, + _zone, _northp, _easting, _northing, _gamma, _k, + zone); + _lat = latitude; + _long = 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 = −5 (min), 1° + * - prec = 0, 10−5° (about 1m) + * - prec = 3, 10−8° + * - prec = 9 (max), 10−14° + **********************************************************************/ + std::string GeoRepresentation(int prec = 0, bool longfirst = false) const; + + /** + * String representation with latitude and longitude as degrees, minutes, + * seconds, and hemisphere. + * + * @param[in] prec precision (relative to about 1m) + * @param[in] longfirst if true give longitude first (default = false) + * @param[in] dmssep if non-null, use as the DMS separator character + * (instead of d, ', " delimiters). + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return DMS latitude/longitude string representation. + * + * Precision specifies accuracy of representation as follows: + * - prec = −5 (min), 1° + * - prec = −4, 0.1° + * - prec = −3, 1' + * - prec = −2, 0.1' + * - prec = −1, 1" + * - prec = 0, 0.1" (about 3m) + * - prec = 1, 0.01" + * - prec = 10 (max), 10−11" + **********************************************************************/ + std::string DMSRepresentation(int prec = 0, bool longfirst = false, + char dmssep = char(0)) + const; + + /** + * MGRS string. + * + * @param[in] prec precision (relative to about 1m). + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return MGRS string. + * + * This gives the coordinates of the enclosing grid square with size given + * by the precision. Thus 38n 444180 3684790 converted to a MGRS + * coordinate at precision −2 (100m) is 38SMB441847 and not + * 38SMB442848. \e prec specifies the precision of the MGRS string as + * follows: + * - prec = −6 (min), only the grid zone is returned, e.g., 38S + * - prec = −5, 100km, e.g., 38SMB + * - prec = −4, 10km + * - prec = −3, 1km + * - prec = −2, 100m + * - prec = −1, 10m + * - prec = 0, 1m + * - prec = 1, 0.1m + * - prec = 6 (max), 1μm + **********************************************************************/ + std::string MGRSRepresentation(int prec = 0) const; + + /** + * UTM/UPS string. + * + * @param[in] prec precision (relative to about 1m) + * @param[in] abbrev if true (the default) use abbreviated (n/s) notation + * for hemisphere; otherwise spell out the hemisphere (north/south) + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return UTM/UPS string representation: zone designator, easting, and + * northing. + * + * Precision specifies accuracy of representation as follows: + * - prec = −5 (min), 100km + * - prec = −3, 1km + * - prec = 0, 1m + * - prec = 3, 1mm + * - prec = 6, 1μm + * - prec = 9 (max), 1nm + **********************************************************************/ + std::string UTMUPSRepresentation(int prec = 0, bool abbrev = true) const; + + /** + * UTM/UPS string with hemisphere override. + * + * @param[in] northp hemisphere override + * @param[in] prec precision (relative to about 1m) + * @param[in] abbrev if true (the default) use abbreviated (n/s) notation + * for hemisphere; otherwise spell out the hemisphere (north/south) + * @exception GeographicErr if the hemisphere override attempts to change + * UPS N to UPS S or vice versa. + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return UTM/UPS string representation: zone designator, easting, and + * northing. + **********************************************************************/ + std::string UTMUPSRepresentation(bool northp, int prec = 0, + bool abbrev = true) const; + + /** + * MGRS string for the alternate zone. See GeoCoords::MGRSRepresentation. + * + * @param[in] prec precision (relative to about 1m). + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return MGRS string. + **********************************************************************/ + std::string AltMGRSRepresentation(int prec = 0) const; + + /** + * UTM/UPS string for the alternate zone. See + * GeoCoords::UTMUPSRepresentation. + * + * @param[in] prec precision (relative to about 1m) + * @param[in] abbrev if true (the default) use abbreviated (n/s) notation + * for hemisphere; otherwise spell out the hemisphere (north/south) + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return UTM/UPS string representation: zone designator, easting, and + * northing. + **********************************************************************/ + std::string AltUTMUPSRepresentation(int prec = 0, bool abbrev = true) + const; + + /** + * UTM/UPS string for the alternate zone, with hemisphere override. + * + * @param[in] northp hemisphere override + * @param[in] prec precision (relative to about 1m) + * @param[in] abbrev if true (the default) use abbreviated (n/s) notation + * for hemisphere; otherwise spell out the hemisphere (north/south) + * @exception GeographicErr if the hemisphere override attempts to change + * UPS n to UPS s or vice verse. + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return UTM/UPS string representation: zone designator, easting, and + * northing. + **********************************************************************/ + std::string AltUTMUPSRepresentation(bool northp, int prec = 0, + bool abbrev = true) const; + ///@} + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the WGS84 ellipsoid (meters). + * + * (The WGS84 value is returned because the UTM and UPS projections are + * based on this ellipsoid.) + **********************************************************************/ + Math::real EquatorialRadius() const { return UTMUPS::EquatorialRadius(); } + + /** + * @return \e f the flattening of the WGS84 ellipsoid. + * + * (The WGS84 value is returned because the UTM and UPS projections are + * based on this ellipsoid.) + **********************************************************************/ + Math::real Flattening() const { return UTMUPS::Flattening(); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_GEOCOORDS_HPP diff --git a/external/include/GeographicLib/Geocentric.hpp b/external/include/GeographicLib/Geocentric.hpp new file mode 100644 index 0000000..186df9a --- /dev/null +++ b/external/include/GeographicLib/Geocentric.hpp @@ -0,0 +1,274 @@ +/** + * \file Geocentric.hpp + * \brief Header for GeographicLib::Geocentric class + * + * Copyright (c) Charles Karney (2008-2020) 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 +#include + +namespace GeographicLib { + + /** + * \brief %Geocentric coordinates + * + * Convert between geodetic coordinates latitude = \e lat, longitude = \e + * lon, height = \e h (measured vertically from the surface of the ellipsoid) + * to geocentric coordinates (\e X, \e Y, \e Z). The origin of geocentric + * coordinates is at the center of the earth. The \e Z axis goes thru the + * north pole, \e lat = 90°. The \e X axis goes thru \e lat = 0, + * \e lon = 0. %Geocentric coordinates are also known as earth centered, + * earth fixed (ECEF) coordinates. + * + * The conversion from geographic to geocentric coordinates is + * straightforward. For the reverse transformation we use + * - H. Vermeille, + * Direct + * transformation from geocentric coordinates to geodetic coordinates, + * 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, + * Geodesics + * on an ellipsoid of revolution, + * Feb. 2011; + * preprint + * arxiv:1102.1215v1. + * . + * Vermeille similarly updated his method in + * - H. Vermeille, + * + * An analytical method to transform geocentric into + * geodetic coordinates, 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 + * + * CartConvert 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 − \e f) \e a is not + * positive. + **********************************************************************/ + Geocentric(real a, real f); + + /** + * A default constructor (for use by NormalGravity). + **********************************************************************/ + Geocentric() : _a(-1) {} + + /** + * Convert from geodetic to geocentric coordinates. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[in] h height of point above the ellipsoid (meters). + * @param[out] X geocentric coordinate (meters). + * @param[out] Y geocentric coordinate (meters). + * @param[out] Z geocentric coordinate (meters). + * + * \e lat should be in the range [−90°, 90°]. + **********************************************************************/ + void Forward(real lat, real lon, real h, real& X, real& Y, real& Z) + const { + if (Init()) + IntForward(lat, lon, h, X, Y, Z, NULL); + } + + /** + * Convert from geodetic to geocentric coordinates and return rotation + * matrix. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[in] h height of point above the ellipsoid (meters). + * @param[out] X geocentric coordinate (meters). + * @param[out] Y geocentric coordinate (meters). + * @param[out] Z geocentric coordinate (meters). + * @param[out] M if the length of the vector is 9, fill with the rotation + * matrix in row-major order. + * + * Let \e v be a unit vector located at (\e lat, \e lon, \e h). We can + * express \e v as \e column vectors in one of two ways + * - in east, north, up coordinates (where the components are relative to a + * local coordinate system at (\e lat, \e lon, \e h)); call this + * representation \e v1. + * - in geocentric \e X, \e Y, \e Z coordinates; call this representation + * \e v0. + * . + * Then we have \e v0 = \e M ⋅ \e v1. + **********************************************************************/ + void Forward(real lat, real lon, real h, real& X, real& Y, real& Z, + std::vector& 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 + * |h |is returned, i.e., (lat, lon) corresponds to + * the closest point on the ellipsoid. If there are still multiple + * solutions with different latitudes (applies only if \e Z = 0), then the + * solution with \e lat > 0 is returned. If there are still multiple + * solutions with different longitudes (applies only if \e X = \e Y = 0) + * then \e lon = 0 is returned. The value of \e h returned satisfies \e h + * ≥ − \e a (1 − e2) / sqrt(1 − + * e2 sin2\e lat). The value of \e lon + * returned is in the range [−180°, 180°]. + **********************************************************************/ + void Reverse(real X, real Y, real Z, real& lat, real& lon, real& h) + const { + if (Init()) + IntReverse(X, Y, Z, lat, lon, h, NULL); + } + + /** + * Convert from geocentric to geodetic to coordinates. + * + * @param[in] X geocentric coordinate (meters). + * @param[in] Y geocentric coordinate (meters). + * @param[in] Z geocentric coordinate (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] h height of point above the ellipsoid (meters). + * @param[out] M if the length of the vector is 9, fill with the rotation + * matrix in row-major order. + * + * Let \e v be a unit vector located at (\e lat, \e lon, \e h). We can + * express \e v as \e column vectors in one of two ways + * - in east, north, up coordinates (where the components are relative to a + * local coordinate system at (\e lat, \e lon, \e h)); call this + * representation \e v1. + * - in geocentric \e X, \e Y, \e Z coordinates; call this representation + * \e v0. + * . + * Then we have \e v1 = MT ⋅ \e v0, where + * MT is the transpose of \e M. + **********************************************************************/ + void Reverse(real X, real Y, real Z, real& lat, real& lon, real& h, + std::vector& 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 diff --git a/external/include/GeographicLib/Geodesic.hpp b/external/include/GeographicLib/Geodesic.hpp new file mode 100644 index 0000000..53fec25 --- /dev/null +++ b/external/include/GeographicLib/Geodesic.hpp @@ -0,0 +1,977 @@ +/** + * \file Geodesic.hpp + * \brief Header for GeographicLib::Geodesic class + * + * Copyright (c) Charles Karney (2009-2020) 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 + +#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 φ, longitude λ (with λ12 = + * λ2 − λ1), and azimuth α. + * + * spheroidal triangle + * + * Given \e lat1, \e lon1, \e azi1, and \e s12, we can determine \e lat2, \e + * lon2, and \e azi2. This is the \e direct geodesic problem and its + * solution is given by the function Geodesic::Direct. (If \e s12 is + * sufficiently large that the geodesic wraps more than halfway around the + * earth, there will be another geodesic between the points with a smaller \e + * s12.) + * + * Given \e lat1, \e lon1, \e lat2, and \e lon2, we can determine \e azi1, \e + * azi2, and \e s12. This is the \e inverse geodesic problem, whose solution + * is given by Geodesic::Inverse. Usually, the solution to the inverse + * problem is unique. In cases where there are multiple solutions (all with + * the same \e s12, of course), all the solutions can be easily generated + * once a particular solution is provided. + * + * The standard way of specifying the direct problem is the specify the + * distance \e s12 to the second point. However it is sometimes useful + * instead to specify the arc length \e a12 (in degrees) on the auxiliary + * sphere. This is a mathematical construct used in solving the geodesic + * problems. The solution of the direct problem in this form is provided by + * Geodesic::ArcDirect. An arc length in excess of 180° indicates that + * the geodesic is not a shortest path. In addition, the arc length between + * an equatorial crossing and the next extremum of latitude for a geodesic is + * 90°. + * + * This class can also calculate several other quantities related to + * geodesics. These are: + * - reduced length. If we fix the first point and increase \e azi1 + * by \e dazi1 (radians), the second point is displaced \e m12 \e dazi1 in + * the direction \e azi2 + 90°. The quantity \e m12 is called + * the "reduced length" and is symmetric under interchange of the two + * points. On a curved surface the reduced length obeys a symmetry + * relation, \e m12 + \e m21 = 0. On a flat surface, we have \e m12 = \e + * s12. The ratio s12/\e m12 gives the azimuthal scale for an + * azimuthal equidistant projection. + * - geodesic scale. 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. + * - area. 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 + * (lat1,lon1), (0,lon1), (0,lon2), and + * (lat2,lon2). It can be used to compute the area of any + * geodesic polygon. + * + * Overloaded versions of Geodesic::Direct, Geodesic::ArcDirect, and + * Geodesic::Inverse allow these quantities to be returned. In addition + * there are general functions Geodesic::GenDirect, and Geodesic::GenInverse + * which allow an arbitrary set of results to be computed. The quantities \e + * m12, \e M12, \e M21 which all specify the behavior of nearby geodesics + * obey addition rules. If points 1, 2, and 3 all lie on a single geodesic, + * then the following rules hold: + * - \e s13 = \e s12 + \e s23 + * - \e a13 = \e a12 + \e a23 + * - \e S13 = \e S12 + \e S23 + * - \e m13 = \e m12 \e M23 + \e m23 \e M21 + * - \e M13 = \e M12 \e M23 − (1 − \e M12 \e M21) \e m23 / \e m12 + * - \e M31 = \e M32 \e M21 − (1 − \e M23 \e M32) \e m12 / \e m23 + * + * Additional functionality is provided by the GeodesicLine class, which + * allows a sequence of points along a geodesic to be computed. + * + * The shortest distance returned by the solution of the inverse problem is + * (obviously) uniquely defined. However, in a few special cases there are + * multiple azimuths which yield the same shortest distance. Here is a + * catalog of those cases: + * - \e lat1 = −\e lat2 (with neither point at a pole). If \e azi1 = + * \e azi2, the geodesic is unique. Otherwise there are two geodesics and + * the second one is obtained by setting [\e azi1, \e azi2] → [\e + * azi2, \e azi1], [\e M12, \e M21] → [\e M21, \e M12], \e S12 → + * −\e S12. (This occurs when the longitude difference is near + * ±180° for oblate ellipsoids.) + * - \e lon2 = \e lon1 ± 180° (with neither point at a pole). If + * \e azi1 = 0° or ±180°, the geodesic is unique. Otherwise + * there are two geodesics and the second one is obtained by setting [\e + * azi1, \e azi2] → [−\e azi1, −\e azi2], \e S12 → + * −\e S12. (This occurs when \e lat2 is near −\e lat1 for + * prolate ellipsoids.) + * - Points 1 and 2 at opposite poles. There are infinitely many geodesics + * which can be generated by setting [\e azi1, \e azi2] → [\e azi1, \e + * azi2] + [\e d, −\e d], for arbitrary \e d. (For spheres, this + * prescription applies when points 1 and 2 are antipodal.) + * - \e s12 = 0 (coincident points). There are infinitely many geodesics + * which can be generated by setting [\e azi1, \e azi2] → + * [\e azi1, \e azi2] + [\e d, \e d], for arbitrary \e d. + * + * The calculations are accurate to better than 15 nm (15 nanometers) for the + * WGS84 ellipsoid. See Sec. 9 of + * arXiv:1102.1215v1 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 |f| < 0.02; however reasonably accurate results will be + * obtained for |f| < 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.
+   *     |f|      error
+   *     0.01     25 nm
+   *     0.02     30 nm
+   *     0.05     10 um
+   *     0.1     1.5 mm
+   *     0.2     300 mm
+   * 
+ * For very eccentric ellipsoids, use GeodesicExact instead. + * + * The algorithms are described in + * - C. F. F. Karney, + * + * Algorithms for geodesics, + * J. Geodesy 87, 43--55 (2013); + * DOI: + * 10.1007/s00190-012-0578-z; + * addenda: + * + * geod-addenda.html. + * . + * For more information on geodesics see \ref geodesic. + * + * Example of use: + * \include example-Geodesic.cpp + * + * GeodSolve 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 − \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 (meters2). + * @return \e a12 arc length of between point 1 and point 2 (degrees). + * + * \e lat1 should be in the range [−90°, 90°]. The values of + * \e lon2 and \e azi2 returned are in the range [−180°, + * 180°]. + * + * If either point is at a pole, the azimuth is defined by keeping the + * longitude fixed, writing \e lat = ±(90° − ε), + * and taking the limit ε → 0+. An arc length greater that + * 180° signifies a geodesic which is not a shortest path. (For a + * prolate ellipsoid, an additional condition is necessary for a shortest + * path: the longitudinal extent must not exceed of 180°.) + * + * The following functions are overloaded versions of Geodesic::Direct + * which omit some of the output parameters. Note, however, that the arc + * length is always computed and returned as the function value. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21, real& S12) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, t, m12, M12, M21, S12); + } + + /** + * See the documentation for Geodesic::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for Geodesic::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for Geodesic::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, real& m12) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | REDUCEDLENGTH, + lat2, lon2, azi2, t, m12, t, t, t); + } + + /** + * See the documentation for Geodesic::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, + real& M12, real& M21) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | GEODESICSCALE, + lat2, lon2, azi2, t, t, M12, M21, t); + } + + /** + * See the documentation for Geodesic::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, t, m12, M12, M21, t); + } + ///@} + + /** \name Direct geodesic problem specified in terms of arc length. + **********************************************************************/ + ///@{ + /** + * Solve the direct geodesic problem where the length of the geodesic + * is specified in terms of arc length. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] a12 arc length between point 1 and point 2 (degrees); it can + * be negative. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * + * \e lat1 should be in the range [−90°, 90°]. The values of + * \e lon2 and \e azi2 returned are in the range [−180°, + * 180°]. + * + * If either point is at a pole, the azimuth is defined by keeping the + * longitude fixed, writing \e lat = ±(90° − ε), + * and taking the limit ε → 0+. An arc length greater that + * 180° signifies a geodesic which is not a shortest path. (For a + * prolate ellipsoid, an additional condition is necessary for a shortest + * path: the longitudinal extent must not exceed of 180°.) + * + * The following functions are overloaded versions of Geodesic::Direct + * which omit some of the output parameters. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12, + real& m12, real& M12, real& M21, real& S12) + const { + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, s12, m12, M12, M21, S12); + } + + /** + * See the documentation for Geodesic::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for Geodesic::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for Geodesic::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12) + const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE, + lat2, lon2, azi2, s12, t, t, t, t); + } + + /** + * See the documentation for Geodesic::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH, + lat2, lon2, azi2, s12, m12, t, t, t); + } + + /** + * See the documentation for Geodesic::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12, + real& M12, real& M21) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + GEODESICSCALE, + lat2, lon2, azi2, s12, t, M12, M21, t); + } + + /** + * See the documentation for Geodesic::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12, + real& m12, real& M12, real& M21) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, s12, m12, M12, M21, t); + } + ///@} + + /** \name General version of the direct geodesic solution. + **********************************************************************/ + ///@{ + + /** + * The general direct geodesic problem. Geodesic::Direct and + * Geodesic::ArcDirect are defined in terms of this function. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] arcmode boolean flag determining the meaning of the \e + * s12_a12. + * @param[in] s12_a12 if \e arcmode is false, this is the distance between + * point 1 and point 2 (meters); otherwise it is the arc length between + * point 1 and point 2 (degrees); it can be negative. + * @param[in] outmask a bitor'ed combination of Geodesic::mask values + * specifying which of the following parameters should be set. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * @return \e a12 arc length of between point 1 and point 2 (degrees). + * + * The Geodesic::mask values possible for \e outmask are + * - \e outmask |= Geodesic::LATITUDE for the latitude \e lat2; + * - \e outmask |= Geodesic::LONGITUDE for the latitude \e lon2; + * - \e outmask |= Geodesic::AZIMUTH for the latitude \e azi2; + * - \e outmask |= Geodesic::DISTANCE for the distance \e s12; + * - \e outmask |= Geodesic::REDUCEDLENGTH for the reduced length \e + * m12; + * - \e outmask |= Geodesic::GEODESICSCALE for the geodesic scales \e + * M12 and \e M21; + * - \e outmask |= Geodesic::AREA for the area \e S12; + * - \e outmask |= Geodesic::ALL for all of the above; + * - \e outmask |= Geodesic::LONG_UNROLL to unroll \e lon2 instead of + * wrapping it into the range [−180°, 180°]. + * . + * The function value \e a12 is always computed and returned and this + * equals \e s12_a12 is \e arcmode is true. If \e outmask includes + * Geodesic::DISTANCE and \e arcmode is false, then \e s12 = \e s12_a12. + * It is not necessary to include Geodesic::DISTANCE_IN in \e outmask; this + * is automatically included is \e arcmode is false. + * + * With the Geodesic::LONG_UNROLL bit set, the quantity \e lon2 − \e + * lon1 indicates how many times and in what sense the geodesic encircles + * the ellipsoid. + **********************************************************************/ + Math::real GenDirect(real lat1, real lon1, real azi1, + bool arcmode, real s12_a12, unsigned outmask, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21, + real& S12) const; + ///@} + + /** \name Inverse geodesic problem. + **********************************************************************/ + ///@{ + /** + * Solve the inverse geodesic problem. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] lat2 latitude of point 2 (degrees). + * @param[in] lon2 longitude of point 2 (degrees). + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] azi1 azimuth at point 1 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * @return \e a12 arc length of between point 1 and point 2 (degrees). + * + * \e lat1 and \e lat2 should be in the range [−90°, 90°]. + * The values of \e azi1 and \e azi2 returned are in the range + * [−180°, 180°]. + * + * If either point is at a pole, the azimuth is defined by keeping the + * longitude fixed, writing \e lat = ±(90° − ε), + * and taking the limit ε → 0+. + * + * The solution to the inverse problem is found using Newton's method. If + * this fails to converge (this is very unlikely in geodetic applications + * but does occur for very eccentric ellipsoids), then the bisection method + * is used to refine the solution. + * + * The following functions are overloaded versions of Geodesic::Inverse + * which omit some of the output parameters. Note, however, that the arc + * length is always computed and returned as the function value. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, real& m12, + real& M12, real& M21, real& S12) const { + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE | AREA, + s12, azi1, azi2, m12, M12, M21, S12); + } + + /** + * See the documentation for Geodesic::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE, + s12, t, t, t, t, t, t); + } + + /** + * See the documentation for Geodesic::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& azi1, real& azi2) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + AZIMUTH, + t, azi1, azi2, t, t, t, t); + } + + /** + * See the documentation for Geodesic::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2) + const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH, + s12, azi1, azi2, t, t, t, t); + } + + /** + * See the documentation for Geodesic::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, real& m12) + const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | REDUCEDLENGTH, + s12, azi1, azi2, m12, t, t, t); + } + + /** + * See the documentation for Geodesic::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, + real& M12, real& M21) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | GEODESICSCALE, + s12, azi1, azi2, t, M12, M21, t); + } + + /** + * See the documentation for Geodesic::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, real& m12, + real& M12, real& M21) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE, + s12, azi1, azi2, m12, M12, M21, t); + } + ///@} + + /** \name General version of inverse geodesic solution. + **********************************************************************/ + ///@{ + /** + * The general inverse geodesic calculation. Geodesic::Inverse is defined + * in terms of this function. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] lat2 latitude of point 2 (degrees). + * @param[in] lon2 longitude of point 2 (degrees). + * @param[in] outmask a bitor'ed combination of Geodesic::mask values + * specifying which of the following parameters should be set. + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] azi1 azimuth at point 1 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * @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 [−90°, 90°]. + * + * The Geodesic::mask values are + * - \e caps |= Geodesic::LATITUDE for the latitude \e lat2; this is + * added automatically; + * - \e caps |= Geodesic::LONGITUDE for the latitude \e lon2; + * - \e caps |= Geodesic::AZIMUTH for the azimuth \e azi2; this is + * added automatically; + * - \e caps |= Geodesic::DISTANCE for the distance \e s12; + * - \e caps |= Geodesic::REDUCEDLENGTH for the reduced length \e m12; + * - \e caps |= Geodesic::GEODESICSCALE for the geodesic scales \e M12 + * and \e M21; + * - \e caps |= Geodesic::AREA for the area \e S12; + * - \e caps |= Geodesic::DISTANCE_IN permits the length of the + * geodesic to be given in terms of \e s12; without this capability the + * length can only be specified in terms of arc length; + * - \e caps |= Geodesic::ALL for all of the above. + * . + * The default value of \e caps is Geodesic::ALL. + * + * If the point is at a pole, the azimuth is defined by keeping \e lon1 + * fixed, writing \e lat1 = ±(90 − ε), and taking the + * limit ε → 0+. + **********************************************************************/ + GeodesicLine Line(real lat1, real lon1, real azi1, unsigned caps = ALL) + const; + + /** + * Define a GeodesicLine in terms of the inverse geodesic problem. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] lat2 latitude of point 2 (degrees). + * @param[in] lon2 longitude of point 2 (degrees). + * @param[in] caps bitor'ed combination of Geodesic::mask values + * specifying the capabilities the GeodesicLine object should possess, + * i.e., which quantities can be returned in calls to + * GeodesicLine::Position. + * @return a GeodesicLine object. + * + * This function sets point 3 of the GeodesicLine to correspond to point 2 + * of the inverse geodesic problem. + * + * \e lat1 and \e lat2 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLine InverseLine(real lat1, real lon1, real lat2, real lon2, + unsigned caps = ALL) const; + + /** + * Define a GeodesicLine in terms of the direct geodesic problem specified + * in terms of distance. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] s12 distance between point 1 and point 2 (meters); it can be + * negative. + * @param[in] caps bitor'ed combination of Geodesic::mask values + * specifying the capabilities the GeodesicLine object should possess, + * i.e., which quantities can be returned in calls to + * GeodesicLine::Position. + * @return a GeodesicLine object. + * + * This function sets point 3 of the GeodesicLine to correspond to point 2 + * of the direct geodesic problem. + * + * \e lat1 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLine DirectLine(real lat1, real lon1, real azi1, real s12, + unsigned caps = ALL) const; + + /** + * Define a GeodesicLine in terms of the direct geodesic problem specified + * in terms of arc length. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] a12 arc length between point 1 and point 2 (degrees); it can + * be negative. + * @param[in] caps bitor'ed combination of Geodesic::mask values + * specifying the capabilities the GeodesicLine object should possess, + * i.e., which quantities can be returned in calls to + * GeodesicLine::Position. + * @return a GeodesicLine object. + * + * This function sets point 3 of the GeodesicLine to correspond to point 2 + * of the direct geodesic problem. + * + * \e lat1 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLine ArcDirectLine(real lat1, real lon1, real azi1, real a12, + unsigned caps = ALL) const; + + /** + * Define a GeodesicLine in terms of the direct geodesic problem specified + * in terms of either distance or arc length. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] arcmode boolean flag determining the meaning of the \e + * s12_a12. + * @param[in] s12_a12 if \e arcmode is false, this is the distance between + * point 1 and point 2 (meters); otherwise it is the arc length between + * point 1 and point 2 (degrees); it can be negative. + * @param[in] caps bitor'ed combination of Geodesic::mask values + * specifying the capabilities the GeodesicLine object should possess, + * i.e., which quantities can be returned in calls to + * GeodesicLine::Position. + * @return a GeodesicLine object. + * + * This function sets point 3 of the GeodesicLine to correspond to point 2 + * of the direct geodesic problem. + * + * \e lat1 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLine GenDirectLine(real lat1, real lon1, real azi1, + bool arcmode, real s12_a12, + unsigned caps = ALL) const; + ///@} + + /** \name Inspector functions. + **********************************************************************/ + ///@{ + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _a; } + + /** + * @return \e f the flattening of the ellipsoid. This is the + * value used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * @return total area of ellipsoid in meters2. 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 diff --git a/external/include/GeographicLib/GeodesicExact.hpp b/external/include/GeographicLib/GeodesicExact.hpp new file mode 100644 index 0000000..abb8da6 --- /dev/null +++ b/external/include/GeographicLib/GeodesicExact.hpp @@ -0,0 +1,869 @@ +/** + * \file GeodesicExact.hpp + * \brief Header for GeographicLib::GeodesicExact class + * + * Copyright (c) Charles Karney (2012-2020) 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 +#include + +#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 ∈ [-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 + * b/\e a ∈ [0.01, 100] or \e f ∈ [−99, 0.99]. + * + * For the WGS84 ellipsoid, these classes are 2--3 times \e slower than the + * series solution and 2--3 times \e less \e accurate (because it's less easy + * to control round-off errors with the elliptic integral formulation); i.e., + * the error is about 40 nm (40 nanometers) instead of 15 nm. However the + * error in the series solution scales as f7 while the + * error in the elliptic integral solution depends weakly on \e f. If the + * quarter meridian distance is 10000 km and the ratio b/\e a = 1 + * − \e f is varied then the approximate maximum error (expressed as a + * distance) is
+   *       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
+   * 
+ * + * The computation of the area in these classes is via a 30th order series. + * This gives accurate results for b/\e a ∈ [1/2, 2]; the + * accuracy is about 8 decimal digits for b/\e a ∈ [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 + * + * GeodSolve 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 − \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 (meters2). + * @return \e a12 arc length of between point 1 and point 2 (degrees). + * + * \e lat1 should be in the range [−90°, 90°]. The values of + * \e lon2 and \e azi2 returned are in the range [−180°, + * 180°]. + * + * If either point is at a pole, the azimuth is defined by keeping the + * longitude fixed, writing \e lat = ±(90° − ε), + * and taking the limit ε → 0+. An arc length greater that + * 180° signifies a geodesic which is not a shortest path. (For a + * prolate ellipsoid, an additional condition is necessary for a shortest + * path: the longitudinal extent must not exceed of 180°.) + * + * The following functions are overloaded versions of GeodesicExact::Direct + * which omit some of the output parameters. Note, however, that the arc + * length is always computed and returned as the function value. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21, real& S12) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, t, m12, M12, M21, S12); + } + + /** + * See the documentation for GeodesicExact::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, real& m12) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | REDUCEDLENGTH, + lat2, lon2, azi2, t, m12, t, t, t); + } + + /** + * See the documentation for GeodesicExact::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, + real& M12, real& M21) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | GEODESICSCALE, + lat2, lon2, azi2, t, t, M12, M21, t); + } + + /** + * See the documentation for GeodesicExact::Direct. + **********************************************************************/ + Math::real Direct(real lat1, real lon1, real azi1, real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21) + const { + real t; + return GenDirect(lat1, lon1, azi1, false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, t, m12, M12, M21, t); + } + ///@} + + /** \name Direct geodesic problem specified in terms of arc length. + **********************************************************************/ + ///@{ + /** + * Perform the direct geodesic calculation where the length of the geodesic + * is specified in terms of arc length. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] a12 arc length between point 1 and point 2 (degrees); it can + * be signed. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * + * \e lat1 should be in the range [−90°, 90°]. The values of + * \e lon2 and \e azi2 returned are in the range [−180°, + * 180°]. + * + * If either point is at a pole, the azimuth is defined by keeping the + * longitude fixed, writing \e lat = ±(90° − ε), + * and taking the limit ε → 0+. An arc length greater that + * 180° signifies a geodesic which is not a shortest path. (For a + * prolate ellipsoid, an additional condition is necessary for a shortest + * path: the longitudinal extent must not exceed of 180°.) + * + * The following functions are overloaded versions of GeodesicExact::Direct + * which omit some of the output parameters. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12, + real& m12, real& M12, real& M21, real& S12) + const { + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, s12, m12, M12, M21, S12); + } + + /** + * See the documentation for GeodesicExact::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12) + const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE, + lat2, lon2, azi2, s12, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH, + lat2, lon2, azi2, s12, m12, t, t, t); + } + + /** + * See the documentation for GeodesicExact::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12, + real& M12, real& M21) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + GEODESICSCALE, + lat2, lon2, azi2, s12, t, M12, M21, t); + } + + /** + * See the documentation for GeodesicExact::ArcDirect. + **********************************************************************/ + void ArcDirect(real lat1, real lon1, real azi1, real a12, + real& lat2, real& lon2, real& azi2, real& s12, + real& m12, real& M12, real& M21) const { + real t; + GenDirect(lat1, lon1, azi1, true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, s12, m12, M12, M21, t); + } + ///@} + + /** \name General version of the direct geodesic solution. + **********************************************************************/ + ///@{ + + /** + * The general direct geodesic calculation. GeodesicExact::Direct and + * GeodesicExact::ArcDirect are defined in terms of this function. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] arcmode boolean flag determining the meaning of the second + * parameter. + * @param[in] s12_a12 if \e arcmode is false, this is the distance between + * point 1 and point 2 (meters); otherwise it is the arc length between + * point 1 and point 2 (degrees); it can be signed. + * @param[in] outmask a bitor'ed combination of GeodesicExact::mask values + * specifying which of the following parameters should be set. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * @return \e a12 arc length of between point 1 and point 2 (degrees). + * + * The GeodesicExact::mask values possible for \e outmask are + * - \e outmask |= GeodesicExact::LATITUDE for the latitude \e lat2; + * - \e outmask |= GeodesicExact::LONGITUDE for the latitude \e lon2; + * - \e outmask |= GeodesicExact::AZIMUTH for the latitude \e azi2; + * - \e outmask |= GeodesicExact::DISTANCE for the distance \e s12; + * - \e outmask |= GeodesicExact::REDUCEDLENGTH for the reduced length \e + * m12; + * - \e outmask |= GeodesicExact::GEODESICSCALE for the geodesic scales \e + * M12 and \e M21; + * - \e outmask |= GeodesicExact::AREA for the area \e S12; + * - \e outmask |= GeodesicExact::ALL for all of the above; + * - \e outmask |= GeodesicExact::LONG_UNROLL to unroll \e lon2 instead of + * wrapping it into the range [−180°, 180°]. + * . + * The function value \e a12 is always computed and returned and this + * equals \e s12_a12 is \e arcmode is true. If \e outmask includes + * GeodesicExact::DISTANCE and \e arcmode is false, then \e s12 = \e + * s12_a12. It is not necessary to include GeodesicExact::DISTANCE_IN in + * \e outmask; this is automatically included is \e arcmode is false. + * + * With the GeodesicExact::LONG_UNROLL bit set, the quantity \e lon2 + * − \e lon1 indicates how many times and in what sense the geodesic + * encircles the ellipsoid. + **********************************************************************/ + Math::real GenDirect(real lat1, real lon1, real azi1, + bool arcmode, real s12_a12, unsigned outmask, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21, + real& S12) const; + ///@} + + /** \name Inverse geodesic problem. + **********************************************************************/ + ///@{ + /** + * Perform the inverse geodesic calculation. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] lat2 latitude of point 2 (degrees). + * @param[in] lon2 longitude of point 2 (degrees). + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] azi1 azimuth at point 1 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * @return \e a12 arc length of between point 1 and point 2 (degrees). + * + * \e lat1 and \e lat2 should be in the range [−90°, 90°]. + * The values of \e azi1 and \e azi2 returned are in the range + * [−180°, 180°]. + * + * If either point is at a pole, the azimuth is defined by keeping the + * longitude fixed, writing \e lat = ±(90° − ε), + * and taking the limit ε → 0+. + * + * The following functions are overloaded versions of + * GeodesicExact::Inverse which omit some of the output parameters. Note, + * however, that the arc length is always computed and returned as the + * function value. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, real& m12, + real& M12, real& M21, real& S12) const { + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE | AREA, + s12, azi1, azi2, m12, M12, M21, S12); + } + + /** + * See the documentation for GeodesicExact::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE, + s12, t, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& azi1, real& azi2) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + AZIMUTH, + t, azi1, azi2, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2) + const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH, + s12, azi1, azi2, t, t, t, t); + } + + /** + * See the documentation for GeodesicExact::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, real& m12) + const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | REDUCEDLENGTH, + s12, azi1, azi2, m12, t, t, t); + } + + /** + * See the documentation for GeodesicExact::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, + real& M12, real& M21) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | GEODESICSCALE, + s12, azi1, azi2, t, M12, M21, t); + } + + /** + * See the documentation for GeodesicExact::Inverse. + **********************************************************************/ + Math::real Inverse(real lat1, real lon1, real lat2, real lon2, + real& s12, real& azi1, real& azi2, real& m12, + real& M12, real& M21) const { + real t; + return GenInverse(lat1, lon1, lat2, lon2, + DISTANCE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE, + s12, azi1, azi2, m12, M12, M21, t); + } + ///@} + + /** \name General version of inverse geodesic solution. + **********************************************************************/ + ///@{ + /** + * The general inverse geodesic calculation. GeodesicExact::Inverse is + * defined in terms of this function. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] lat2 latitude of point 2 (degrees). + * @param[in] lon2 longitude of point 2 (degrees). + * @param[in] outmask a bitor'ed combination of GeodesicExact::mask values + * specifying which of the following parameters should be set. + * @param[out] s12 distance between point 1 and point 2 (meters). + * @param[out] azi1 azimuth at point 1 (degrees). + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] m12 reduced length of geodesic (meters). + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless). + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless). + * @param[out] S12 area under the geodesic (meters2). + * @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 [−90°, 90°]. + * + * The GeodesicExact::mask values are + * - \e caps |= GeodesicExact::LATITUDE for the latitude \e lat2; this is + * added automatically; + * - \e caps |= GeodesicExact::LONGITUDE for the latitude \e lon2; + * - \e caps |= GeodesicExact::AZIMUTH for the azimuth \e azi2; this is + * added automatically; + * - \e caps |= GeodesicExact::DISTANCE for the distance \e s12; + * - \e caps |= GeodesicExact::REDUCEDLENGTH for the reduced length \e m12; + * - \e caps |= GeodesicExact::GEODESICSCALE for the geodesic scales \e M12 + * and \e M21; + * - \e caps |= GeodesicExact::AREA for the area \e S12; + * - \e caps |= GeodesicExact::DISTANCE_IN permits the length of the + * geodesic to be given in terms of \e s12; without this capability the + * length can only be specified in terms of arc length; + * - \e caps |= GeodesicExact::ALL for all of the above. + * . + * The default value of \e caps is GeodesicExact::ALL which turns on all + * the capabilities. + * + * If the point is at a pole, the azimuth is defined by keeping \e lon1 + * fixed, writing \e lat1 = ±(90 − ε), and taking the + * limit ε → 0+. + **********************************************************************/ + GeodesicLineExact Line(real lat1, real lon1, real azi1, + unsigned caps = ALL) const; + + /** + * Define a GeodesicLineExact in terms of the inverse geodesic problem. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] lat2 latitude of point 2 (degrees). + * @param[in] lon2 longitude of point 2 (degrees). + * @param[in] caps bitor'ed combination of GeodesicExact::mask values + * specifying the capabilities the GeodesicLineExact object should + * possess, i.e., which quantities can be returned in calls to + * GeodesicLineExact::Position. + * @return a GeodesicLineExact object. + * + * This function sets point 3 of the GeodesicLineExact to correspond to + * point 2 of the inverse geodesic problem. + * + * \e lat1 and \e lat2 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLineExact InverseLine(real lat1, real lon1, real lat2, real lon2, + unsigned caps = ALL) const; + + /** + * Define a GeodesicLineExact in terms of the direct geodesic problem + * specified in terms of distance. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] s12 distance between point 1 and point 2 (meters); it can be + * negative. + * @param[in] caps bitor'ed combination of GeodesicExact::mask values + * specifying the capabilities the GeodesicLineExact object should + * possess, i.e., which quantities can be returned in calls to + * GeodesicLineExact::Position. + * @return a GeodesicLineExact object. + * + * This function sets point 3 of the GeodesicLineExact to correspond to + * point 2 of the direct geodesic problem. + * + * \e lat1 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLineExact DirectLine(real lat1, real lon1, real azi1, real s12, + unsigned caps = ALL) const; + + /** + * Define a GeodesicLineExact in terms of the direct geodesic problem + * specified in terms of arc length. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] a12 arc length between point 1 and point 2 (degrees); it can + * be negative. + * @param[in] caps bitor'ed combination of GeodesicExact::mask values + * specifying the capabilities the GeodesicLineExact object should + * possess, i.e., which quantities can be returned in calls to + * GeodesicLineExact::Position. + * @return a GeodesicLineExact object. + * + * This function sets point 3 of the GeodesicLineExact to correspond to + * point 2 of the direct geodesic problem. + * + * \e lat1 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLineExact ArcDirectLine(real lat1, real lon1, real azi1, real a12, + unsigned caps = ALL) const; + + /** + * Define a GeodesicLineExact in terms of the direct geodesic problem + * specified in terms of either distance or arc length. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] azi1 azimuth at point 1 (degrees). + * @param[in] arcmode boolean flag determining the meaning of the \e + * s12_a12. + * @param[in] s12_a12 if \e arcmode is false, this is the distance between + * point 1 and point 2 (meters); otherwise it is the arc length between + * point 1 and point 2 (degrees); it can be negative. + * @param[in] caps bitor'ed combination of GeodesicExact::mask values + * specifying the capabilities the GeodesicLineExact object should + * possess, i.e., which quantities can be returned in calls to + * GeodesicLineExact::Position. + * @return a GeodesicLineExact object. + * + * This function sets point 3 of the GeodesicLineExact to correspond to + * point 2 of the direct geodesic problem. + * + * \e lat1 should be in the range [−90°, 90°]. + **********************************************************************/ + GeodesicLineExact GenDirectLine(real lat1, real lon1, real azi1, + bool arcmode, real s12_a12, + unsigned caps = ALL) const; + ///@} + + /** \name Inspector functions. + **********************************************************************/ + ///@{ + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _a; } + + /** + * @return \e f the flattening of the ellipsoid. This is the + * value used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * @return total area of ellipsoid in meters2. 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 diff --git a/external/include/GeographicLib/GeodesicLine.hpp b/external/include/GeographicLib/GeodesicLine.hpp new file mode 100644 index 0000000..f312096 --- /dev/null +++ b/external/include/GeographicLib/GeodesicLine.hpp @@ -0,0 +1,708 @@ +/** + * \file GeodesicLine.hpp + * \brief Header for GeographicLib::GeodesicLine class + * + * Copyright (c) Charles Karney (2009-2020) 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 +#include + +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 + * arXiv:1102.1215v1 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 |f| < 0.02; however reasonably accurate results will be + * obtained for |f| < 0.2. For very eccentric ellipsoids, use + * GeodesicLineExact instead. + * + * The algorithms are described in + * - C. F. F. Karney, + * + * Algorithms for geodesics, + * J. Geodesy 87, 43--55 (2013); + * DOI: + * 10.1007/s00190-012-0578-z; + * addenda: + * + * geod-addenda.html. + * . + * For more information on geodesics see \ref geodesic. + * + * Example of use: + * \include example-GeodesicLine.cpp + * + * GeodSolve 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 [−90°, 90°]. + * + * The GeodesicLine::mask values are + * - \e caps |= GeodesicLine::LATITUDE for the latitude \e lat2; this is + * added automatically; + * - \e caps |= GeodesicLine::LONGITUDE for the latitude \e lon2; + * - \e caps |= GeodesicLine::AZIMUTH for the latitude \e azi2; this is + * added automatically; + * - \e caps |= GeodesicLine::DISTANCE for the distance \e s12; + * - \e caps |= GeodesicLine::REDUCEDLENGTH for the reduced length \e m12; + * - \e caps |= GeodesicLine::GEODESICSCALE for the geodesic scales \e M12 + * and \e M21; + * - \e caps |= GeodesicLine::AREA for the area \e S12; + * - \e caps |= GeodesicLine::DISTANCE_IN permits the length of the + * geodesic to be given in terms of \e s12; without this capability the + * length can only be specified in terms of arc length; + * - \e caps |= GeodesicLine::ALL for all of the above. + * . + * The default value of \e caps is GeodesicLine::ALL. + * + * If the point is at a pole, the azimuth is defined by keeping \e lon1 + * fixed, writing \e lat1 = ±(90° − ε), and taking + * the limit ε → 0+. + **********************************************************************/ + GeodesicLine(const Geodesic& g, real lat1, real lon1, real azi1, + unsigned caps = ALL); + + /** + * A default constructor. If GeodesicLine::Position is called on the + * resulting object, it returns immediately (without doing any + * calculations). The object can be set with a call to Geodesic::Line. + * Use Init() to test whether object is still in this uninitialized state. + **********************************************************************/ + GeodesicLine() : _caps(0U) {} + ///@} + + /** \name Position in terms of distance + **********************************************************************/ + ///@{ + + /** + * Compute the position of point 2 which is a distance \e s12 (meters) from + * point 1. + * + * @param[in] s12 distance from point 1 to point 2 (meters); it can be + * negative. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees); requires that the + * GeodesicLine object was constructed with \e caps |= + * GeodesicLine::LONGITUDE. + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] m12 reduced length of geodesic (meters); requires that the + * GeodesicLine object was constructed with \e caps |= + * GeodesicLine::REDUCEDLENGTH. + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless); requires that the GeodesicLine object was constructed + * with \e caps |= GeodesicLine::GEODESICSCALE. + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless); requires that the GeodesicLine object was constructed + * with \e caps |= GeodesicLine::GEODESICSCALE. + * @param[out] S12 area under the geodesic (meters2); requires + * that the GeodesicLine object was constructed with \e caps |= + * GeodesicLine::AREA. + * @return \e a12 arc length from point 1 to point 2 (degrees). + * + * The values of \e lon2 and \e azi2 returned are in the range + * [−180°, 180°]. + * + * The GeodesicLine object \e must have been constructed with \e caps |= + * GeodesicLine::DISTANCE_IN; otherwise Math::NaN() is returned and no + * parameters are set. Requesting a value which the GeodesicLine object is + * not capable of computing is not an error; the corresponding argument + * will not be altered. + * + * The following functions are overloaded versions of + * GeodesicLine::Position which omit some of the output parameters. Note, + * however, that the arc length is always computed and returned as the + * function value. + **********************************************************************/ + Math::real Position(real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21, + real& S12) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, t, m12, M12, M21, S12); + } + + /** + * See the documentation for GeodesicLine::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLine::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2, + real& azi2) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLine::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2, + real& azi2, real& m12) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | + AZIMUTH | REDUCEDLENGTH, + lat2, lon2, azi2, t, m12, t, t, t); + } + + /** + * See the documentation for GeodesicLine::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2, + real& azi2, real& M12, real& M21) + const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | + AZIMUTH | GEODESICSCALE, + lat2, lon2, azi2, t, t, M12, M21, t); + } + + /** + * See the documentation for GeodesicLine::Position. + **********************************************************************/ + Math::real Position(real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21) + const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, t, m12, M12, M21, t); + } + ///@} + + /** \name Position in terms of arc length + **********************************************************************/ + ///@{ + + /** + * Compute the position of point 2 which is an arc length \e a12 (degrees) + * from point 1. + * + * @param[in] a12 arc length from point 1 to point 2 (degrees); it can + * be negative. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees); requires that the + * GeodesicLine object was constructed with \e caps |= + * GeodesicLine::LONGITUDE. + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance from point 1 to point 2 (meters); requires + * that the GeodesicLine object was constructed with \e caps |= + * GeodesicLine::DISTANCE. + * @param[out] m12 reduced length of geodesic (meters); requires that the + * GeodesicLine object was constructed with \e caps |= + * GeodesicLine::REDUCEDLENGTH. + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless); requires that the GeodesicLine object was constructed + * with \e caps |= GeodesicLine::GEODESICSCALE. + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless); requires that the GeodesicLine object was constructed + * with \e caps |= GeodesicLine::GEODESICSCALE. + * @param[out] S12 area under the geodesic (meters2); requires + * that the GeodesicLine object was constructed with \e caps |= + * GeodesicLine::AREA. + * + * The values of \e lon2 and \e azi2 returned are in the range + * [−180°, 180°]. + * + * Requesting a value which the GeodesicLine object is not capable of + * computing is not an error; the corresponding argument will not be + * altered. + * + * The following functions are overloaded versions of + * GeodesicLine::ArcPosition which omit some of the output parameters. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21, + real& S12) const { + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, s12, m12, M12, M21, S12); + } + + /** + * See the documentation for GeodesicLine::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLine::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, + real& lat2, real& lon2, real& azi2) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLine::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12) const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE, + lat2, lon2, azi2, s12, t, t, t, t); + } + + /** + * See the documentation for GeodesicLine::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& m12) const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | + DISTANCE | REDUCEDLENGTH, + lat2, lon2, azi2, s12, m12, t, t, t); + } + + /** + * See the documentation for GeodesicLine::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& M12, real& M21) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | + DISTANCE | GEODESICSCALE, + lat2, lon2, azi2, s12, t, M12, M21, t); + } + + /** + * See the documentation for GeodesicLine::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | + DISTANCE | REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, s12, m12, M12, M21, t); + } + ///@} + + /** \name The general position function. + **********************************************************************/ + ///@{ + + /** + * The general position function. GeodesicLine::Position and + * GeodesicLine::ArcPosition are defined in terms of this function. + * + * @param[in] arcmode boolean flag determining the meaning of the second + * parameter; if \e arcmode is false, then the GeodesicLine object must + * have been constructed with \e caps |= GeodesicLine::DISTANCE_IN. + * @param[in] s12_a12 if \e arcmode is false, this is the distance between + * point 1 and point 2 (meters); otherwise it is the arc length between + * point 1 and point 2 (degrees); it can be negative. + * @param[in] outmask a bitor'ed combination of GeodesicLine::mask values + * specifying which of the following parameters should be set. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees); requires that the + * GeodesicLine object was constructed with \e caps |= + * GeodesicLine::LONGITUDE. + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance from point 1 to point 2 (meters); requires + * that the GeodesicLine object was constructed with \e caps |= + * GeodesicLine::DISTANCE. + * @param[out] m12 reduced length of geodesic (meters); requires that the + * GeodesicLine object was constructed with \e caps |= + * GeodesicLine::REDUCEDLENGTH. + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless); requires that the GeodesicLine object was constructed + * with \e caps |= GeodesicLine::GEODESICSCALE. + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless); requires that the GeodesicLine object was constructed + * with \e caps |= GeodesicLine::GEODESICSCALE. + * @param[out] S12 area under the geodesic (meters2); requires + * that the GeodesicLine object was constructed with \e caps |= + * GeodesicLine::AREA. + * @return \e a12 arc length from point 1 to point 2 (degrees). + * + * The GeodesicLine::mask values possible for \e outmask are + * - \e outmask |= GeodesicLine::LATITUDE for the latitude \e lat2; + * - \e outmask |= GeodesicLine::LONGITUDE for the latitude \e lon2; + * - \e outmask |= GeodesicLine::AZIMUTH for the latitude \e azi2; + * - \e outmask |= GeodesicLine::DISTANCE for the distance \e s12; + * - \e outmask |= GeodesicLine::REDUCEDLENGTH for the reduced length \e + * m12; + * - \e outmask |= GeodesicLine::GEODESICSCALE for the geodesic scales \e + * M12 and \e M21; + * - \e outmask |= GeodesicLine::AREA for the area \e S12; + * - \e outmask |= GeodesicLine::ALL for all of the above; + * - \e outmask |= GeodesicLine::LONG_UNROLL to unroll \e lon2 instead of + * reducing it into the range [−180°, 180°]. + * . + * Requesting a value which the GeodesicLine object is not capable of + * computing is not an error; the corresponding argument will not be + * altered. Note, however, that the arc length is always computed and + * returned as the function value. + * + * With the GeodesicLine::LONG_UNROLL bit set, the quantity \e lon2 − + * \e lon1 indicates how many times and in what sense the geodesic + * encircles the ellipsoid. + **********************************************************************/ + Math::real GenPosition(bool arcmode, real s12_a12, unsigned outmask, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21, + real& S12) const; + ///@} + + /** \name Setting point 3 + **********************************************************************/ + ///@{ + + /** + * Specify position of point 3 in terms of distance. + * + * @param[in] s13 the distance from point 1 to point 3 (meters); it + * can be negative. + * + * This is only useful if the GeodesicLine object has been constructed + * with \e caps |= GeodesicLine::DISTANCE_IN. + **********************************************************************/ + void SetDistance(real s13); + + /** + * Specify position of point 3 in terms of arc length. + * + * @param[in] a13 the arc length from point 1 to point 3 (degrees); it + * can be negative. + * + * The distance \e s13 is only set if the GeodesicLine object has been + * constructed with \e caps |= GeodesicLine::DISTANCE. + **********************************************************************/ + void SetArc(real a13); + + /** + * Specify position of point 3 in terms of either distance or arc length. + * + * @param[in] arcmode boolean flag determining the meaning of the second + * parameter; if \e arcmode is false, then the GeodesicLine object must + * have been constructed with \e caps |= GeodesicLine::DISTANCE_IN. + * @param[in] s13_a13 if \e arcmode is false, this is the distance from + * point 1 to point 3 (meters); otherwise it is the arc length from + * point 1 to point 3 (degrees); it can be negative. + **********************************************************************/ + void GenSetDistance(bool arcmode, real s13_a13); + ///@} + + /** \name Inspector functions + **********************************************************************/ + ///@{ + + /** + * @return true if the object has been initialized. + **********************************************************************/ + bool Init() const { return _caps != 0U; } + + /** + * @return \e lat1 the latitude of point 1 (degrees). + **********************************************************************/ + Math::real Latitude() const + { return Init() ? _lat1 : Math::NaN(); } + + /** + * @return \e lon1 the longitude of point 1 (degrees). + **********************************************************************/ + Math::real Longitude() const + { return Init() ? _lon1 : Math::NaN(); } + + /** + * @return \e azi1 the azimuth (degrees) of the geodesic line at point 1. + **********************************************************************/ + Math::real Azimuth() const + { return Init() ? _azi1 : Math::NaN(); } + + /** + * The sine and cosine of \e azi1. + * + * @param[out] sazi1 the sine of \e azi1. + * @param[out] cazi1 the cosine of \e azi1. + **********************************************************************/ + void Azimuth(real& sazi1, real& cazi1) const + { if (Init()) { sazi1 = _salp1; cazi1 = _calp1; } } + + /** + * @return \e azi0 the azimuth (degrees) of the geodesic line as it crosses + * the equator in a northward direction. + * + * The result lies in [−90°, 90°]. + **********************************************************************/ + Math::real EquatorialAzimuth() const + { return Init() ? Math::atan2d(_salp0, _calp0) : Math::NaN(); } + + /** + * The sine and cosine of \e azi0. + * + * @param[out] sazi0 the sine of \e azi0. + * @param[out] cazi0 the cosine of \e azi0. + **********************************************************************/ + void EquatorialAzimuth(real& sazi0, real& cazi0) const + { if (Init()) { sazi0 = _salp0; cazi0 = _calp0; } } + + /** + * @return \e a1 the arc length (degrees) between the northward equatorial + * crossing and point 1. + * + * The result lies in (−180°, 180°]. + **********************************************************************/ + Math::real EquatorialArc() const { + return Init() ? Math::atan2d(_ssig1, _csig1) : Math::NaN(); + } + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const + { return Init() ? _a : Math::NaN(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real Flattening() const + { return Init() ? _f : Math::NaN(); } + + /** + * @return \e 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 diff --git a/external/include/GeographicLib/GeodesicLineExact.hpp b/external/include/GeographicLib/GeodesicLineExact.hpp new file mode 100644 index 0000000..5fdf635 --- /dev/null +++ b/external/include/GeographicLib/GeodesicLineExact.hpp @@ -0,0 +1,673 @@ +/** + * \file GeodesicLineExact.hpp + * \brief Header for GeographicLib::GeodesicLineExact class + * + * Copyright (c) Charles Karney (2012-2020) 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 +#include +#include + +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 + * + * GeodSolve 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 [−90°, 90°]. + * + * The GeodesicLineExact::mask values are + * - \e caps |= GeodesicLineExact::LATITUDE for the latitude \e lat2; this + * is added automatically; + * - \e caps |= GeodesicLineExact::LONGITUDE for the latitude \e lon2; + * - \e caps |= GeodesicLineExact::AZIMUTH for the latitude \e azi2; this + * is added automatically; + * - \e caps |= GeodesicLineExact::DISTANCE for the distance \e s12; + * - \e caps |= GeodesicLineExact::REDUCEDLENGTH for the reduced length \e + m12; + * - \e caps |= GeodesicLineExact::GEODESICSCALE for the geodesic scales \e + * M12 and \e M21; + * - \e caps |= GeodesicLineExact::AREA for the area \e S12; + * - \e caps |= GeodesicLineExact::DISTANCE_IN permits the length of the + * geodesic to be given in terms of \e s12; without this capability the + * length can only be specified in terms of arc length; + * - \e caps |= GeodesicLineExact::ALL for all of the above. + * . + * The default value of \e caps is GeodesicLineExact::ALL. + * + * If the point is at a pole, the azimuth is defined by keeping \e lon1 + * fixed, writing \e lat1 = ±(90° − ε), and taking + * the limit ε → 0+. + **********************************************************************/ + GeodesicLineExact(const GeodesicExact& g, real lat1, real lon1, real azi1, + unsigned caps = ALL); + + /** + * A default constructor. If GeodesicLineExact::Position is called on the + * resulting object, it returns immediately (without doing any + * calculations). The object can be set with a call to + * GeodesicExact::Line. Use Init() to test whether object is still in this + * uninitialized state. + **********************************************************************/ + GeodesicLineExact() : _caps(0U) {} + ///@} + + /** \name Position in terms of distance + **********************************************************************/ + ///@{ + + /** + * Compute the position of point 2 which is a distance \e s12 (meters) + * from point 1. + * + * @param[in] s12 distance from point 1 to point 2 (meters); it can be + * signed. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees); requires that the + * GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::LONGITUDE. + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] m12 reduced length of geodesic (meters); requires that the + * GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::REDUCEDLENGTH. + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless); requires that the GeodesicLineExact object was + * constructed with \e caps |= GeodesicLineExact::GEODESICSCALE. + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless); requires that the GeodesicLineExact object was + * constructed with \e caps |= GeodesicLineExact::GEODESICSCALE. + * @param[out] S12 area under the geodesic (meters2); requires + * that the GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::AREA. + * @return \e a12 arc length from point 1 to point 2 (degrees). + * + * The values of \e lon2 and \e azi2 returned are in the range + * [−180°, 180°]. + * + * The GeodesicLineExact object \e must have been constructed with \e caps + * |= GeodesicLineExact::DISTANCE_IN; otherwise Math::NaN() is returned and + * no parameters are set. Requesting a value which the GeodesicLineExact + * object is not capable of computing is not an error; the corresponding + * argument will not be altered. + * + * The following functions are overloaded versions of + * GeodesicLineExact::Position which omit some of the output parameters. + * Note, however, that the arc length is always computed and returned as + * the function value. + **********************************************************************/ + Math::real Position(real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21, + real& S12) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, t, m12, M12, M21, S12); + } + + /** + * See the documentation for GeodesicLineExact::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLineExact::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2, + real& azi2) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLineExact::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2, + real& azi2, real& m12) const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | + AZIMUTH | REDUCEDLENGTH, + lat2, lon2, azi2, t, m12, t, t, t); + } + + /** + * See the documentation for GeodesicLineExact::Position. + **********************************************************************/ + Math::real Position(real s12, real& lat2, real& lon2, + real& azi2, real& M12, real& M21) + const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | + AZIMUTH | GEODESICSCALE, + lat2, lon2, azi2, t, t, M12, M21, t); + } + + /** + * See the documentation for GeodesicLineExact::Position. + **********************************************************************/ + Math::real Position(real s12, + real& lat2, real& lon2, real& azi2, + real& m12, real& M12, real& M21) + const { + real t; + return GenPosition(false, s12, + LATITUDE | LONGITUDE | AZIMUTH | + REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, t, m12, M12, M21, t); + } + ///@} + + /** \name Position in terms of arc length + **********************************************************************/ + ///@{ + + /** + * Compute the position of point 2 which is an arc length \e a12 (degrees) + * from point 1. + * + * @param[in] a12 arc length from point 1 to point 2 (degrees); it can + * be signed. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees); requires that the + * GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::LONGITUDE. + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance from point 1 to point 2 (meters); requires + * that the GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::DISTANCE. + * @param[out] m12 reduced length of geodesic (meters); requires that the + * GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::REDUCEDLENGTH. + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless); requires that the GeodesicLineExact object was + * constructed with \e caps |= GeodesicLineExact::GEODESICSCALE. + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless); requires that the GeodesicLineExact object was + * constructed with \e caps |= GeodesicLineExact::GEODESICSCALE. + * @param[out] S12 area under the geodesic (meters2); requires + * that the GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::AREA. + * + * The values of \e lon2 and \e azi2 returned are in the range + * [−180°, 180°]. + * + * Requesting a value which the GeodesicLineExact object is not capable of + * computing is not an error; the corresponding argument will not be + * altered. + * + * The following functions are overloaded versions of + * GeodesicLineExact::ArcPosition which omit some of the output parameters. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21, + real& S12) const { + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE | + REDUCEDLENGTH | GEODESICSCALE | AREA, + lat2, lon2, azi2, s12, m12, M12, M21, S12); + } + + /** + * See the documentation for GeodesicLineExact::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE, + lat2, lon2, t, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLineExact::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, + real& lat2, real& lon2, real& azi2) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH, + lat2, lon2, azi2, t, t, t, t, t); + } + + /** + * See the documentation for GeodesicLineExact::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12) const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | DISTANCE, + lat2, lon2, azi2, s12, t, t, t, t); + } + + /** + * See the documentation for GeodesicLineExact::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& m12) const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | + DISTANCE | REDUCEDLENGTH, + lat2, lon2, azi2, s12, m12, t, t, t); + } + + /** + * See the documentation for GeodesicLineExact::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& M12, real& M21) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | + DISTANCE | GEODESICSCALE, + lat2, lon2, azi2, s12, t, M12, M21, t); + } + + /** + * See the documentation for GeodesicLineExact::ArcPosition. + **********************************************************************/ + void ArcPosition(real a12, real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21) + const { + real t; + GenPosition(true, a12, + LATITUDE | LONGITUDE | AZIMUTH | + DISTANCE | REDUCEDLENGTH | GEODESICSCALE, + lat2, lon2, azi2, s12, m12, M12, M21, t); + } + ///@} + + /** \name The general position function. + **********************************************************************/ + ///@{ + + /** + * The general position function. GeodesicLineExact::Position and + * GeodesicLineExact::ArcPosition are defined in terms of this function. + * + * @param[in] arcmode boolean flag determining the meaning of the second + * parameter; if \e arcmode is false, then the GeodesicLineExact object + * must have been constructed with \e caps |= + * GeodesicLineExact::DISTANCE_IN. + * @param[in] s12_a12 if \e arcmode is false, this is the distance between + * point 1 and point 2 (meters); otherwise it is the arc length between + * point 1 and point 2 (degrees); it can be signed. + * @param[in] outmask a bitor'ed combination of GeodesicLineExact::mask + * values specifying which of the following parameters should be set. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees); requires that the + * GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::LONGITUDE. + * @param[out] azi2 (forward) azimuth at point 2 (degrees). + * @param[out] s12 distance from point 1 to point 2 (meters); requires + * that the GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::DISTANCE. + * @param[out] m12 reduced length of geodesic (meters); requires that the + * GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::REDUCEDLENGTH. + * @param[out] M12 geodesic scale of point 2 relative to point 1 + * (dimensionless); requires that the GeodesicLineExact object was + * constructed with \e caps |= GeodesicLineExact::GEODESICSCALE. + * @param[out] M21 geodesic scale of point 1 relative to point 2 + * (dimensionless); requires that the GeodesicLineExact object was + * constructed with \e caps |= GeodesicLineExact::GEODESICSCALE. + * @param[out] S12 area under the geodesic (meters2); requires + * that the GeodesicLineExact object was constructed with \e caps |= + * GeodesicLineExact::AREA. + * @return \e a12 arc length from point 1 to point 2 (degrees). + * + * The GeodesicLineExact::mask values possible for \e outmask are + * - \e outmask |= GeodesicLineExact::LATITUDE for the latitude \e lat2; + * - \e outmask |= GeodesicLineExact::LONGITUDE for the latitude \e lon2; + * - \e outmask |= GeodesicLineExact::AZIMUTH for the latitude \e azi2; + * - \e outmask |= GeodesicLineExact::DISTANCE for the distance \e s12; + * - \e outmask |= GeodesicLineExact::REDUCEDLENGTH for the reduced length + * \e m12; + * - \e outmask |= GeodesicLineExact::GEODESICSCALE for the geodesic scales + * \e M12 and \e M21; + * - \e outmask |= GeodesicLineExact::AREA for the area \e S12; + * - \e outmask |= GeodesicLineExact::ALL for all of the above; + * - \e outmask |= GeodesicLineExact::LONG_UNROLL to unroll \e lon2 instead + * of wrapping it into the range [−180°, 180°]. + * . + * Requesting a value which the GeodesicLineExact object is not capable of + * computing is not an error; the corresponding argument will not be + * altered. Note, however, that the arc length is always computed and + * returned as the function value. + * + * With the GeodesicLineExact::LONG_UNROLL bit set, the quantity \e lon2 + * − \e lon1 indicates how many times and in what sense the geodesic + * encircles the ellipsoid. + **********************************************************************/ + Math::real GenPosition(bool arcmode, real s12_a12, unsigned outmask, + real& lat2, real& lon2, real& azi2, + real& s12, real& m12, real& M12, real& M21, + real& S12) const; + ///@} + + /** \name Setting point 3 + **********************************************************************/ + ///@{ + + /** + * Specify position of point 3 in terms of distance. + * + * @param[in] s13 the distance from point 1 to point 3 (meters); it + * can be negative. + * + * This is only useful if the GeodesicLineExact object has been constructed + * with \e caps |= GeodesicLineExact::DISTANCE_IN. + **********************************************************************/ + void SetDistance(real s13); + + /** + * Specify position of point 3 in terms of arc length. + * + * @param[in] a13 the arc length from point 1 to point 3 (degrees); it + * can be negative. + * + * The distance \e s13 is only set if the GeodesicLineExact object has been + * constructed with \e caps |= GeodesicLineExact::DISTANCE. + **********************************************************************/ + void SetArc(real a13); + + /** + * Specify position of point 3 in terms of either distance or arc length. + * + * @param[in] arcmode boolean flag determining the meaning of the second + * parameter; if \e arcmode is false, then the GeodesicLineExact object + * must have been constructed with \e caps |= + * GeodesicLineExact::DISTANCE_IN. + * @param[in] s13_a13 if \e arcmode is false, this is the distance from + * point 1 to point 3 (meters); otherwise it is the arc length from + * point 1 to point 3 (degrees); it can be negative. + **********************************************************************/ + void GenSetDistance(bool arcmode, real s13_a13); + + /** \name Inspector functions + **********************************************************************/ + ///@{ + + /** + * @return true if the object has been initialized. + **********************************************************************/ + bool Init() const { return _caps != 0U; } + + /** + * @return \e lat1 the latitude of point 1 (degrees). + **********************************************************************/ + Math::real Latitude() const + { return Init() ? _lat1 : Math::NaN(); } + + /** + * @return \e lon1 the longitude of point 1 (degrees). + **********************************************************************/ + Math::real Longitude() const + { return Init() ? _lon1 : Math::NaN(); } + + /** + * @return \e azi1 the azimuth (degrees) of the geodesic line at point 1. + **********************************************************************/ + Math::real Azimuth() const + { return Init() ? _azi1 : Math::NaN(); } + + /** + * The sine and cosine of \e azi1. + * + * @param[out] sazi1 the sine of \e azi1. + * @param[out] cazi1 the cosine of \e azi1. + **********************************************************************/ + void Azimuth(real& sazi1, real& cazi1) const + { if (Init()) { sazi1 = _salp1; cazi1 = _calp1; } } + + /** + * @return \e azi0 the azimuth (degrees) of the geodesic line as it crosses + * the equator in a northward direction. + * + * The result lies in [−90°, 90°]. + **********************************************************************/ + Math::real EquatorialAzimuth() const + { return Init() ? Math::atan2d(_salp0, _calp0) : Math::NaN(); } + + /** + * The sine and cosine of \e azi0. + * + * @param[out] sazi0 the sine of \e azi0. + * @param[out] cazi0 the cosine of \e azi0. + **********************************************************************/ + void EquatorialAzimuth(real& sazi0, real& cazi0) const + { if (Init()) { sazi0 = _salp0; cazi0 = _calp0; } } + + /** + * @return \e a1 the arc length (degrees) between the northward equatorial + * crossing and point 1. + * + * The result lies in (−180°, 180°]. + **********************************************************************/ + Math::real EquatorialArc() const { + using std::atan2; + return Init() ? atan2(_ssig1, _csig1) / Math::degree() : Math::NaN(); + } + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value inherited from the GeodesicExact object used in the + * constructor. + **********************************************************************/ + Math::real EquatorialRadius() const + { return Init() ? _a : Math::NaN(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the GeodesicExact object used in the constructor. + **********************************************************************/ + Math::real Flattening() const + { return Init() ? _f : Math::NaN(); } + + /** + * @return \e caps the computational capabilities that this object was + * constructed with. LATITUDE and AZIMUTH are always included. + **********************************************************************/ + unsigned Capabilities() const { return _caps; } + + /** + * Test what capabilities are available. + * + * @param[in] testcaps a set of bitor'ed GeodesicLineExact::mask values. + * @return true if the GeodesicLineExact object has all these capabilities. + **********************************************************************/ + bool Capabilities(unsigned testcaps) const { + testcaps &= OUT_ALL; + return (_caps & testcaps) == testcaps; + } + + /** + * The distance or arc length to point 3. + * + * @param[in] arcmode boolean flag determining the meaning of returned + * value. + * @return \e s13 if \e arcmode is false; \e a13 if \e arcmode is true. + **********************************************************************/ + Math::real GenDistance(bool arcmode) const + { return Init() ? (arcmode ? _a13 : _s13) : Math::NaN(); } + + /** + * @return \e s13, the distance to point 3 (meters). + **********************************************************************/ + Math::real Distance() const { return GenDistance(false); } + + /** + * @return \e a13, the arc length to point 3 (degrees). + **********************************************************************/ + Math::real Arc() const { return GenDistance(true); } + + /** + * \deprecated An old name for EquatorialRadius(). + **********************************************************************/ + GEOGRAPHICLIB_DEPRECATED("Use EquatorialRadius()") + Math::real MajorRadius() const { return EquatorialRadius(); } + ///@} + + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_GEODESICLINEEXACT_HPP diff --git a/external/include/GeographicLib/Geohash.hpp b/external/include/GeographicLib/Geohash.hpp new file mode 100644 index 0000000..d7d1d8a --- /dev/null +++ b/external/include/GeographicLib/Geohash.hpp @@ -0,0 +1,180 @@ +/** + * \file Geohash.hpp + * \brief Header for GeographicLib::Geohash class + * + * Copyright (c) Charles Karney (2012-2017) 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 + +#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 [−90°, + * 90°]. + * @exception std::bad_alloc if memory for \e geohash can't be allocated. + * + * Internally, \e len is first put in the range [0, 18]. (\e len = 18 + * provides approximately 1μm precision.) + * + * If \e lat or \e lon is NaN, the returned geohash is "invalid". + **********************************************************************/ + static void Forward(real lat, real lon, int len, std::string& geohash); + + /** + * Convert from a geohash to geographic coordinates. + * + * @param[in] geohash the geohash. + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] len the length of the geohash. + * @param[in] centerp if true (the default) return the center of the + * geohash location, otherwise return the south-west corner. + * @exception GeographicErr if \e geohash contains illegal characters. + * + * Only the first 18 characters for \e geohash are considered. (18 + * characters provides approximately 1μm precision.) The case of the + * letters in \e geohash is ignored. + * + * If the first 3 characters of \e geohash are "inv", then \e lat and \e + * lon are set to NaN and \e len is unchanged. ("nan" is treated + * similarly.) + **********************************************************************/ + static void Reverse(const std::string& geohash, real& lat, real& lon, + int& len, bool centerp = true); + + /** + * The latitude resolution of a geohash. + * + * @param[in] len the length of the geohash. + * @return the latitude resolution (degrees). + * + * Internally, \e len is first put in the range [0, 18]. + **********************************************************************/ + static Math::real LatitudeResolution(int len) { + using std::ldexp; + len = (std::max)(0, (std::min)(int(maxlen_), len)); + return ldexp(real(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 [−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 diff --git a/external/include/GeographicLib/Geoid.hpp b/external/include/GeographicLib/Geoid.hpp new file mode 100644 index 0000000..1320200 --- /dev/null +++ b/external/include/GeographicLib/Geoid.hpp @@ -0,0 +1,478 @@ +/** + * \file Geoid.hpp + * \brief Header for GeographicLib::Geoid class + * + * Copyright (c) Charles Karney (2009-2021) 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 +#include +#include + +#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 216−1. Setting it + * to 4 uses a maxval of 232−1 and changes the extension for + * the data files from .pgm to .pgm4. Note that the format of these pgm4 files + * is a non-standard extension of the pgm format. + **********************************************************************/ +# define GEOGRAPHICLIB_GEOID_PGM_PIXEL_WIDTH 2 +#endif + +namespace GeographicLib { + + /** + * \brief Looking up the height of the geoid above the ellipsoid + * + * This class evaluates the height of one of the standard geoids, EGM84, + * EGM96, or EGM2008 by bilinear or cubic interpolation into a rectangular + * grid of data. These geoid models are documented in + * - EGM84: + * https://earth-info.nga.mil/index.php?dir=wgs84&action=wgs84#tab_egm84 + * - EGM96: + * https://earth-info.nga.mil/index.php?dir=wgs84&action=wgs84#tab_egm96 + * - EGM2008: + * https://earth-info.nga.mil/index.php?dir=wgs84&action=wgs84#tab_egm2008 + * + * The geoids are defined in terms of spherical harmonics. However in order + * to provide a quick and flexible method of evaluating the geoid heights, + * this class evaluates the height by interpolation into a grid of + * precomputed values. + * + * The height of the geoid above the ellipsoid, \e N, is sometimes called the + * geoid undulation. It can be used to convert a height above the ellipsoid, + * \e h, to the corresponding height above the geoid (the orthometric height, + * roughly the height above mean sea level), \e H, using the relations + * + *    \e h = \e N + \e H; + *   \e H = −\e N + \e h. + * + * See \ref geoid for details of how to install the data sets, the data + * format, estimates of the interpolation errors, and how to use caching. + * + * This class is typically \e not thread safe in that a single instantiation + * cannot be safely used by multiple threads because of the way the object + * reads the data set and because it maintains a single-cell cache. If + * multiple threads need to calculate geoid heights they should all construct + * thread-local instantiations. Alternatively, set the optional \e + * threadsafe parameter to true in the constructor. This causes the + * constructor to read all the data into memory and to turn off the + * single-cell caching which results in a Geoid object which \e is thread + * safe. + * + * Example of use: + * \include example-Geoid.cpp + * + * GeoidEval 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 > _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° to its value. \e south and \e north should be in + * the range [−90°, 90°]. + **********************************************************************/ + void CacheArea(real south, real west, real north, real east) const; + + /** + * Cache all the data. + * + * @exception GeographicErr if the memory necessary for caching the data + * can't be allocated (in this case, you will have no cache and can try + * again with a smaller area). + * @exception GeographicErr if there's a problem reading the data. + * @exception GeographicErr if this is called on a threadsafe Geoid. + * + * On most computers, this is fast for data sets with grid resolution of 5' + * or coarser. For a 1' grid, the required RAM is 450MB; a 2.5' grid needs + * 72MB; and a 5' grid needs 18MB. + **********************************************************************/ + void CacheAll() const { CacheArea(real(-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 [−90°, 90°]. + **********************************************************************/ + Math::real operator()(real lat, real lon) const { + return height(lat, lon); + } + + /** + * Convert a height above the geoid to a height above the ellipsoid and + * vice versa. + * + * @param[in] lat latitude of the point (degrees). + * @param[in] lon longitude of the point (degrees). + * @param[in] h height of the point (degrees). + * @param[in] d a Geoid::convertflag specifying the direction of the + * conversion; Geoid::GEOIDTOELLIPSOID means convert a height above the + * geoid to a height above the ellipsoid; Geoid::ELLIPSOIDTOGEOID means + * convert a height above the ellipsoid to a height above the geoid. + * @exception GeographicErr if there's a problem reading the data; this + * never happens if (\e lat, \e lon) is within a successfully cached + * area. + * @return converted height (meters). + **********************************************************************/ + Math::real ConvertHeight(real lat, real lon, real h, + convertflag d) const { + return h + real(d) * height(lat, lon); + } + + ///@} + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return geoid description, if available, in the data file; if + * absent, return "NONE". + **********************************************************************/ + const std::string& Description() const { return _description; } + + /** + * @return date of the data file; if absent, return "UNKNOWN". + **********************************************************************/ + const std::string& DateTime() const { return _datetime; } + + /** + * @return full file name used to load the geoid data. + **********************************************************************/ + const std::string& GeoidFile() const { return _filename; } + + /** + * @return "name" used to load the geoid data (from the first argument of + * the constructor). + **********************************************************************/ + const std::string& GeoidName() const { return _name; } + + /** + * @return directory used to load the geoid data. + **********************************************************************/ + const std::string& GeoidDirectory() const { return _dir; } + + /** + * @return interpolation method ("cubic" or "bilinear"). + **********************************************************************/ + const std::string Interpolation() const + { return std::string(_cubic ? "cubic" : "bilinear"); } + + /** + * @return estimate of the maximum interpolation and quantization error + * (meters). + * + * This relies on the value being stored in the data file. If the value is + * absent, return −1. + **********************************************************************/ + Math::real MaxError() const { return _maxerror; } + + /** + * @return estimate of the RMS interpolation and quantization error + * (meters). + * + * This relies on the value being stored in the data file. If the value is + * absent, return −1. + **********************************************************************/ + Math::real RMSError() const { return _rmserror; } + + /** + * @return offset (meters). + * + * This in used in converting from the pixel values in the data file to + * geoid heights. + **********************************************************************/ + Math::real Offset() const { return _offset; } + + /** + * @return scale (meters). + * + * This in used in converting from the pixel values in the data file to + * geoid heights. + **********************************************************************/ + Math::real Scale() const { return _scale; } + + /** + * @return true if the object is constructed to be thread safe. + **********************************************************************/ + bool ThreadSafe() const { return _threadsafe; } + + /** + * @return true if a data cache is active. + **********************************************************************/ + bool Cache() const { return _cache; } + + /** + * @return west edge of the cached area; the cache includes this edge. + **********************************************************************/ + Math::real CacheWest() const { + return _cache ? ((_xoffset + (_xsize == _width ? 0 : _cubic) + + _width/2) % _width - _width/2) / _rlonres : + 0; + } + + /** + * @return east edge of the cached area; the cache excludes this edge. + **********************************************************************/ + Math::real CacheEast() const { + return _cache ? + CacheWest() + + (_xsize - (_xsize == _width ? 0 : 1 + 2 * _cubic)) / _rlonres : + 0; + } + + /** + * @return north edge of the cached area; the cache includes this edge. + **********************************************************************/ + Math::real CacheNorth() const { + return _cache ? 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 diff --git a/external/include/GeographicLib/Georef.hpp b/external/include/GeographicLib/Georef.hpp new file mode 100644 index 0000000..9c3966f --- /dev/null +++ b/external/include/GeographicLib/Georef.hpp @@ -0,0 +1,161 @@ +/** + * \file Georef.hpp + * \brief Header for GeographicLib::Georef class + * + * Copyright (c) Charles Karney (2015-2021) 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 + +#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 [−90°, + * 90°]. + * @exception std::bad_alloc if memory for \e georef can't be allocated. + * + * \e prec specifies the precision of \e georef as follows: + * - \e prec = −1 (min), 15° + * - \e prec = 0, 1° + * - \e prec = 1, converted to \e prec = 2 + * - \e prec = 2, 1' + * - \e prec = 3, 0.1' + * - \e prec = 4, 0.01' + * - \e prec = 5, 0.001' + * - … + * - \e prec = 11 (max), 10−9' + * + * If \e lat or \e lon is NaN, then \e georef is set to "INVALID". + **********************************************************************/ + static void Forward(real lat, real lon, int prec, std::string& georef); + + /** + * Convert from Georef to geographic coordinates. + * + * @param[in] georef the Georef. + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] prec the precision of \e georef. + * @param[in] centerp if true (the default) return the center + * \e georef, otherwise return the south-west corner. + * @exception GeographicErr if \e georef is illegal. + * + * The case of the letters in \e georef is ignored. \e prec is in the + * range [−1, 11] and gives the precision of \e georef as follows: + * - \e prec = −1 (min), 15° + * - \e prec = 0, 1° + * - \e prec = 1, not returned + * - \e prec = 2, 1' + * - \e prec = 3, 0.1' + * - \e prec = 4, 0.01' + * - \e prec = 5, 0.001' + * - … + * - \e prec = 11 (max), 10−9' + * + * If the first 3 characters of \e georef are "INV", then \e lat and \e lon + * are set to NaN and \e prec is unchanged. + **********************************************************************/ + static void Reverse(const std::string& georef, real& lat, real& lon, + int& prec, bool centerp = true); + + /** + * The angular resolution of a Georef. + * + * @param[in] prec the precision of the Georef. + * @return the latitude-longitude resolution (degrees). + * + * Internally, \e prec is first put in the range [−1, 11]. + **********************************************************************/ + static Math::real Resolution(int prec) { + if (prec < 1) + return real(prec < 0 ? 15 : 1); + else { + using std::pow; + // Treat prec = 1 as 2. + prec = (std::max)(2, (std::min)(int(maxprec_), prec)); + // Need extra real because, since C++11, pow(float, int) returns double + return 1/(60 * real(pow(real(base_), prec - 2))); + } + } + + /** + * The Georef precision required to meet a given geographic resolution. + * + * @param[in] res the minimum of resolution in latitude and longitude + * (degrees). + * @return Georef precision. + * + * The returned length is in the range [0, 11]. + **********************************************************************/ + static int Precision(real res) { + using std::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 diff --git a/external/include/GeographicLib/Gnomonic.hpp b/external/include/GeographicLib/Gnomonic.hpp new file mode 100644 index 0000000..1448fb0 --- /dev/null +++ b/external/include/GeographicLib/Gnomonic.hpp @@ -0,0 +1,221 @@ +/** + * \file Gnomonic.hpp + * \brief Header for GeographicLib::Gnomonic class + * + * Copyright (c) Charles Karney (2010-2020) 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 +#include +#include + +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, + * + * Algorithms for geodesics, + * J. Geodesy 87, 43--55 (2013); + * DOI: + * 10.1007/s00190-012-0578-z; + * addenda: + * + * geod-addenda.html. + * . + * 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 ρ = m12/\e M12; finally \e x = ρ sin \e azi1; \e + * y = ρ cos \e azi1, where \e azi1 is the azimuth of the geodesic at \e + * C. The Gnomonic::Forward and Gnomonic::Reverse methods also return the + * azimuth \e azi of the geodesic at \e P and reciprocal scale \e rk in the + * azimuthal direction. The scale in the radial direction if + * 1/rk2. + * + * For a sphere, ρ is reduces to \e a tan(s12/a), 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
+ *
+ * (K(T) - K(C)) + * l2 \e t / 32.
+ *
+ * where \e K is the Gaussian curvature. + * + * This result applies for any surface. For an ellipsoid of revolution, + * consider all geodesics whose end points are within a distance \e r of \e + * C. For a given \e r, the deviation is maximum when the latitude of \e C + * is 45°, when endpoints are a distance \e r away, and when their + * azimuths from the center are ± 45° or ± 135°. + * To lowest order in \e r and the flattening \e f, the deviation is \e f + * (r/2a)3 \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 + * + * GeodesicProj is a command-line utility + * providing access to the functionality of AzimuthalEquidistant, Gnomonic, + * and CassiniSoldner. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT Gnomonic { + private: + typedef Math::real real; + real eps0_, eps_; + Geodesic _earth; + real _a, _f; + // numit_ increased from 10 to 20 to fix convergence failure with high + // precision (e.g., GEOGRAPHICLIB_DIGITS=2000) calculations. Reverse uses + // Newton's method which converges quadratically and so numit_ = 10 would + // normally be big enough. However, since the Geodesic class is based on a + // series it is of limited accuracy; in particular, the derivative rules + // used by Reverse only hold approximately. Consequently, after a few + // iterations, the convergence in the Reverse falls back to improvements in + // each step by a constant (albeit small) factor. + static const int numit_ = 20; + public: + + /** + * Constructor for Gnomonic. + * + * @param[in] earth the Geodesic object to use for geodesic calculations. + * By default this uses the WGS84 ellipsoid. + **********************************************************************/ + explicit Gnomonic(const Geodesic& earth = Geodesic::WGS84()); + + /** + * Forward projection, from geographic to gnomonic. + * + * @param[in] lat0 latitude of center point of projection (degrees). + * @param[in] lon0 longitude of center point of projection (degrees). + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] azi azimuth of geodesic at point (degrees). + * @param[out] rk reciprocal of azimuthal scale at point. + * + * \e lat0 and \e lat should be in the range [−90°, 90°]. + * The scale of the projection is 1/rk2 in the "radial" + * direction, \e azi clockwise from true north, and is 1/\e rk in the + * direction perpendicular to this. If the point lies "over the horizon", + * i.e., if \e rk ≤ 0, then NaNs are returned for \e x and \e y (the + * correct values are returned for \e azi and \e rk). A call to Forward + * followed by a call to Reverse will return the original (\e lat, \e lon) + * (to within roundoff) provided the point in not over the horizon. + **********************************************************************/ + void Forward(real lat0, real lon0, real lat, real lon, + real& x, real& y, real& azi, real& rk) const; + + /** + * Reverse projection, from gnomonic to geographic. + * + * @param[in] lat0 latitude of center point of projection (degrees). + * @param[in] lon0 longitude of center point of projection (degrees). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] azi azimuth of geodesic at point (degrees). + * @param[out] rk reciprocal of azimuthal scale at point. + * + * \e lat0 should be in the range [−90°, 90°]. \e lat will + * be in the range [−90°, 90°] and \e lon will be in the + * range [−180°, 180°]. The scale of the projection is + * 1/rk2 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 diff --git a/external/include/GeographicLib/GravityCircle.hpp b/external/include/GeographicLib/GravityCircle.hpp new file mode 100644 index 0000000..4828907 --- /dev/null +++ b/external/include/GeographicLib/GravityCircle.hpp @@ -0,0 +1,287 @@ +/** + * \file GravityCircle.hpp + * \brief Header for GeographicLib::GravityCircle class + * + * Copyright (c) Charles Karney (2011-2020) 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 +#include +#include +#include + +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 + * + * Gravity 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−2). + * @param[out] gy the northerly component of the acceleration + * (m s−2). + * @param[out] gz the upward component of the acceleration + * (m s−2); this is usually negative. + * @return \e W the sum of the gravitational and centrifugal potentials + * (m2 s−2). + * + * 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−2). + * @param[out] deltay the northerly component of the disturbance vector + * (m s−2). + * @param[out] deltaz the upward component of the disturbance vector + * (m s−2). + * @return \e T the corresponding disturbing potential + * (m2 s−2). + **********************************************************************/ + 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−2). + * @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−2). + * @param[out] gY the \e Y component of the acceleration + * (m s−2). + * @param[out] gZ the \e Z component of the acceleration + * (m s−2). + * @return \e W = \e V + Φ the sum of the gravitational and + * centrifugal potentials (m2 s−2). + **********************************************************************/ + 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−2). + * @param[out] GY the \e Y component of the acceleration + * (m s−2). + * @param[out] GZ the \e Z component of the acceleration + * (m s−2). + * @return \e V = \e W - Φ the gravitational potential + * (m2 s−2). + **********************************************************************/ + 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−2). + * @param[out] deltaY the \e Y component of the gravity disturbance + * (m s−2). + * @param[out] deltaZ the \e Z component of the gravity disturbance + * (m s−2). + * @return \e T = \e W - \e U the disturbing potential (also called the + * anomalous potential) (m2 s−2). + **********************************************************************/ + 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) (m2 s−2). + **********************************************************************/ + 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 diff --git a/external/include/GeographicLib/GravityModel.hpp b/external/include/GeographicLib/GravityModel.hpp new file mode 100644 index 0000000..131294c --- /dev/null +++ b/external/include/GeographicLib/GravityModel.hpp @@ -0,0 +1,549 @@ +/** + * \file GravityModel.hpp + * \brief Header for GeographicLib::GravityModel class + * + * Copyright (c) Charles Karney (2011-2021) 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 +#include +#include +#include + +#if defined(_MSC_VER) +// Squelch warnings about dll vs vector +# pragma warning (push) +# pragma warning (disable: 4251) +#endif + +namespace GeographicLib { + + class GravityCircle; + + /** + * \brief Model of the earth's gravity field + * + * Evaluate the earth's gravity field according to a model. The supported + * models treat only the gravitational field exterior to the mass of the + * earth. When computing the field at points near (but above) the surface of + * the earth a small correction can be applied to account for the mass of the + * atmosphere above the point in question; see \ref gravityatmos. + * Determining the height of the geoid above the ellipsoid entails correcting + * for the mass of the earth above the geoid. The egm96 and egm2008 include + * separate correction terms to account for this mass. + * + * Definitions and terminology (from Heiskanen and Moritz, Sec 2-13): + * - \e V = gravitational potential; + * - Φ = rotational potential; + * - \e W = \e V + Φ = \e T + \e U = total potential; + * - V0 = normal gravitation potential; + * - \e U = V0 + Φ = total normal potential; + * - \e T = \e W − \e U = \e V − V0 = anomalous + * or disturbing potential; + * - g = ∇\e W = γ + δ; + * - f = ∇Φ; + * - Γ = ∇V0; + * - γ = ∇\e U; + * - δ = ∇\e T = gravity disturbance vector + * = gPγP; + * - δ\e g = gravity disturbance = gP − + * γP; + * - Δg = gravity anomaly vector = gP + * − γQ; here the line \e PQ is + * perpendicular to ellipsoid and the potential at \e P equals the normal + * potential at \e Q; + * - Δ\e g = gravity anomaly = gP − + * γQ; + * - (ξ, η) deflection of the vertical, the difference in + * directions of gP and + * γQ, ξ = NS, η = EW. + * - \e X, \e Y, \e Z, geocentric coordinates; + * - \e x, \e y, \e z, local cartesian coordinates used to denote the east, + * north and up directions. + * + * See \ref gravity for details of how to install the gravity models and the + * data format. + * + * References: + * - W. A. Heiskanen and H. Moritz, Physical Geodesy (Freeman, San + * Francisco, 1967). + * + * Example of use: + * \include example-GravityModel.cpp + * + * Gravity 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 _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 ≥ 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−2). + * @param[out] gy the northerly component of the acceleration + * (m s−2). + * @param[out] gz the upward component of the acceleration + * (m s−2); this is usually negative. + * @return \e W the sum of the gravitational and centrifugal potentials + * (m2 s−2). + * + * 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−2). + * @param[out] deltay the northerly component of the disturbance vector + * (m s−2). + * @param[out] deltaz the upward component of the disturbance vector + * (m s−2). + * @return \e T the corresponding disturbing potential + * (m2 s−2). + **********************************************************************/ + 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−2). + * @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−2). + * @param[out] gY the \e Y component of the acceleration + * (m s−2). + * @param[out] gZ the \e Z component of the acceleration + * (m s−2). + * @return \e W = \e V + Φ the sum of the gravitational and + * centrifugal potentials (m2 s−2). + * + * 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−2). + * @param[out] GY the \e Y component of the acceleration + * (m s−2). + * @param[out] GZ the \e Z component of the acceleration + * (m s−2). + * @return \e V = \e W - Φ the gravitational potential + * (m2 s−2). + **********************************************************************/ + 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−2). + * @param[out] deltaY the \e Y component of the gravity disturbance + * (m s−2). + * @param[out] deltaZ the \e Z component of the gravity disturbance + * (m s−2). + * @return \e T = \e W - \e U the disturbing potential (also called the + * anomalous potential) (m2 s−2). + **********************************************************************/ + 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) (m2 s−2). + **********************************************************************/ + 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−2). + * @param[out] gammaY the \e Y component of the normal acceleration + * (m s−2). + * @param[out] gammaZ the \e Z component of the normal acceleration + * (m s−2). + * @return \e U = V0 + Φ the sum of the + * normal gravitational and centrifugal potentials + * (m2 s−2). + * + * 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−2). + * @param[out] fY the \e Y component of the centrifugal acceleration + * (m s−2). + * @return Φ the centrifugal potential (m2 + * s−2). + * + * 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 (m3 + * s−2); 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() + * (m3 s−2). + **********************************************************************/ + Math::real ReferenceMassConstant() const + { return _earth.MassConstant(); } + + /** + * @return ω the angular velocity of the model and the + * ReferenceEllipsoid() (rad s−1). + **********************************************************************/ + 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 diff --git a/external/include/GeographicLib/LambertConformalConic.hpp b/external/include/GeographicLib/LambertConformalConic.hpp new file mode 100644 index 0000000..da968e8 --- /dev/null +++ b/external/include/GeographicLib/LambertConformalConic.hpp @@ -0,0 +1,330 @@ +/** + * \file LambertConformalConic.hpp + * \brief Header for GeographicLib::LambertConformalConic class + * + * Copyright (c) Charles Karney (2010-2020) 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 + +namespace GeographicLib { + + /** + * \brief Lambert conformal conic projection + * + * Implementation taken from the report, + * - J. P. Snyder, + * Map Projections: A + * Working Manual, 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 ( + * EPSG:3364) is obtained by: + * \include example-LambertConformalConic.cpp + * + * ConicProj 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 − \e f) \e a, or \e k0 is + * not positive. + * @exception GeographicErr if \e stdlat is not in [−90°, + * 90°]. + **********************************************************************/ + LambertConformalConic(real a, real f, real stdlat, real k0); + + /** + * Constructor with two standard parallels. + * + * @param[in] a equatorial radius of ellipsoid (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @param[in] stdlat1 first standard parallel (degrees). + * @param[in] stdlat2 second standard parallel (degrees). + * @param[in] k1 scale on the standard parallels. + * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k1 is + * not positive. + * @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in + * [−90°, 90°], or if either \e stdlat1 or \e + * stdlat2 is a pole and \e stdlat1 is not equal \e stdlat2. + **********************************************************************/ + LambertConformalConic(real a, real f, real stdlat1, real stdlat2, real k1); + + /** + * Constructor with two standard parallels specified by sines and cosines. + * + * @param[in] a equatorial radius of ellipsoid (meters). + * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere. + * Negative \e f gives a prolate ellipsoid. + * @param[in] sinlat1 sine of first standard parallel. + * @param[in] coslat1 cosine of first standard parallel. + * @param[in] sinlat2 sine of second standard parallel. + * @param[in] coslat2 cosine of second standard parallel. + * @param[in] k1 scale on the standard parallels. + * @exception GeographicErr if \e a, (1 − \e f) \e a, or \e k1 is + * not positive. + * @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in + * [−90°, 90°], or if either \e stdlat1 or \e + * stdlat2 is a pole and \e stdlat1 is not equal \e stdlat2. + * + * This allows parallels close to the poles to be specified accurately. + * This routine computes the latitude of origin and the scale at this + * latitude. In the case where \e lat1 and \e lat2 are different, the + * errors in this routines are as follows: if \e dlat = abs(\e lat2 − + * \e lat1) ≤ 160° and max(abs(\e lat1), abs(\e lat2)) ≤ 90 + * − min(0.0002, 2.2 × 10−6(180 − \e + * dlat), 6 × 10−8 dlat2) (in + * degrees), then the error in the latitude of origin is less than 4.5 + * × 10−14d and the relative error in the scale is + * less than 7 × 10−15. + **********************************************************************/ + LambertConformalConic(real a, real f, + real sinlat1, real coslat1, + real sinlat2, real coslat2, + real k1); + + /** + * Set the scale for the projection. + * + * @param[in] lat (degrees). + * @param[in] k scale at latitude \e lat (default 1). + * @exception GeographicErr \e k is not positive. + * @exception GeographicErr if \e lat is not in [−90°, + * 90°]. + **********************************************************************/ + void SetScale(real lat, real k = real(1)); + + /** + * Forward projection, from geographic to Lambert conformal conic. + * + * @param[in] lon0 central meridian longitude (degrees). + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * The latitude origin is given by LambertConformalConic::LatitudeOrigin(). + * No false easting or northing is added and \e lat should be in the range + * [−90°, 90°]. The error in the projection is less than + * about 10 nm (10 nanometers), true distance, and the errors in the + * meridian convergence and scale are consistent with this. The values of + * \e x and \e y returned for points which project to infinity (i.e., one + * or both of the poles) will be large but finite. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y, real& gamma, real& k) const; + + /** + * Reverse projection, from Lambert conformal conic to geographic. + * + * @param[in] lon0 central meridian longitude (degrees). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * The latitude origin is given by LambertConformalConic::LatitudeOrigin(). + * No false easting or northing is added. The value of \e lon returned is + * in the range [−180°, 180°]. The error in the projection + * is less than about 10 nm (10 nanometers), true distance, and the errors + * in the meridian convergence and scale are consistent with this. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon, real& gamma, real& k) const; + + /** + * LambertConformalConic::Forward without returning the convergence and + * scale. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y) const { + real gamma, k; + Forward(lon0, lat, lon, x, y, gamma, k); + } + + /** + * LambertConformalConic::Reverse without returning the convergence and + * scale. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon) const { + real gamma, k; + Reverse(lon0, x, y, lat, lon, gamma, k); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _a; } + + /** + * @return \e f the flattening of the ellipsoid. This is the + * value used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * @return latitude of the origin for the projection (degrees). + * + * This is the latitude of minimum scale and equals the \e stdlat in the + * 1-parallel constructor and lies between \e stdlat1 and \e stdlat2 in the + * 2-parallel constructors. + **********************************************************************/ + Math::real OriginLatitude() const { return _lat0; } + + /** + * @return central scale for the projection. This is the scale on the + * latitude of origin. + **********************************************************************/ + Math::real CentralScale() const { return _k0; } + + /** + * \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 diff --git a/external/include/GeographicLib/LocalCartesian.hpp b/external/include/GeographicLib/LocalCartesian.hpp new file mode 100644 index 0000000..abb4f11 --- /dev/null +++ b/external/include/GeographicLib/LocalCartesian.hpp @@ -0,0 +1,244 @@ +/** + * \file LocalCartesian.hpp + * \brief Header for GeographicLib::LocalCartesian class + * + * Copyright (c) Charles Karney (2008-2020) 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 +#include + +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 + * + * CartConvert is a command-line utility + * providing access to the functionality of Geocentric and LocalCartesian. + **********************************************************************/ + + class GEOGRAPHICLIB_EXPORT LocalCartesian { + private: + typedef Math::real real; + static const size_t dim_ = 3; + static const size_t dim2_ = dim_ * dim_; + Geocentric _earth; + real _lat0, _lon0, _h0; + real _x0, _y0, _z0, _r[dim2_]; + void IntForward(real lat, real lon, real h, real& x, real& y, real& z, + real M[dim2_]) const; + void IntReverse(real x, real y, real z, real& lat, real& lon, real& h, + real M[dim2_]) const; + void MatrixMultiply(real M[dim2_]) const; + public: + + /** + * Constructor setting the origin. + * + * @param[in] lat0 latitude at origin (degrees). + * @param[in] lon0 longitude at origin (degrees). + * @param[in] h0 height above ellipsoid at origin (meters); default 0. + * @param[in] earth Geocentric object for the transformation; default + * Geocentric::WGS84(). + * + * \e lat0 should be in the range [−90°, 90°]. + **********************************************************************/ + LocalCartesian(real lat0, real lon0, real h0 = 0, + const Geocentric& earth = Geocentric::WGS84()) + : _earth(earth) + { Reset(lat0, lon0, h0); } + + /** + * Default constructor. + * + * @param[in] earth Geocentric object for the transformation; default + * Geocentric::WGS84(). + * + * Sets \e lat0 = 0, \e lon0 = 0, \e h0 = 0. + **********************************************************************/ + explicit LocalCartesian(const Geocentric& earth = Geocentric::WGS84()) + : _earth(earth) + { Reset(real(0), real(0), real(0)); } + + /** + * Reset the origin. + * + * @param[in] lat0 latitude at origin (degrees). + * @param[in] lon0 longitude at origin (degrees). + * @param[in] h0 height above ellipsoid at origin (meters); default 0. + * + * \e lat0 should be in the range [−90°, 90°]. + **********************************************************************/ + void Reset(real lat0, real lon0, real h0 = 0); + + /** + * Convert from geodetic to local cartesian coordinates. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[in] h height of point above the ellipsoid (meters). + * @param[out] x local cartesian coordinate (meters). + * @param[out] y local cartesian coordinate (meters). + * @param[out] z local cartesian coordinate (meters). + * + * \e lat should be in the range [−90°, 90°]. + **********************************************************************/ + void Forward(real lat, real lon, real h, real& x, real& y, real& z) + const { + IntForward(lat, lon, h, x, y, z, NULL); + } + + /** + * Convert from geodetic to local cartesian coordinates and return rotation + * matrix. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[in] h height of point above the ellipsoid (meters). + * @param[out] x local cartesian coordinate (meters). + * @param[out] y local cartesian coordinate (meters). + * @param[out] z local cartesian coordinate (meters). + * @param[out] M if the length of the vector is 9, fill with the rotation + * matrix in row-major order. + * + * \e lat should be in the range [−90°, 90°]. + * + * Let \e v be a unit vector located at (\e lat, \e lon, \e h). We can + * express \e v as \e column vectors in one of two ways + * - in east, north, up coordinates (where the components are relative to a + * local coordinate system at (\e lat, \e lon, \e h)); call this + * representation \e v1. + * - in \e x, \e y, \e z coordinates (where the components are relative to + * the local coordinate system at (\e lat0, \e lon0, \e h0)); call this + * representation \e v0. + * . + * Then we have \e v0 = \e M ⋅ \e v1. + **********************************************************************/ + void Forward(real lat, real lon, real h, real& x, real& y, real& z, + std::vector& 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 + * |h |is returned, i.e., (lat, lon) corresponds to + * the closest point on the ellipsoid. The value of \e lon returned is in + * the range [−180°, 180°]. + **********************************************************************/ + void Reverse(real x, real y, real z, real& lat, real& lon, real& h) + const { + IntReverse(x, y, z, lat, lon, h, NULL); + } + + /** + * Convert from local cartesian to geodetic coordinates and return rotation + * matrix. + * + * @param[in] x local cartesian coordinate (meters). + * @param[in] y local cartesian coordinate (meters). + * @param[in] z local cartesian coordinate (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] h height of point above the ellipsoid (meters). + * @param[out] M if the length of the vector is 9, fill with the rotation + * matrix in row-major order. + * + * Let \e v be a unit vector located at (\e lat, \e lon, \e h). We can + * express \e v as \e column vectors in one of two ways + * - in east, north, up coordinates (where the components are relative to a + * local coordinate system at (\e lat, \e lon, \e h)); call this + * representation \e v1. + * - in \e x, \e y, \e z coordinates (where the components are relative to + * the local coordinate system at (\e lat0, \e lon0, \e h0)); call this + * representation \e v0. + * . + * Then we have \e v1 = MT ⋅ \e v0, where + * MT is the transpose of \e M. + **********************************************************************/ + void Reverse(real x, real y, real z, real& lat, real& lon, real& h, + std::vector& 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 diff --git a/external/include/GeographicLib/MGRS.hpp b/external/include/GeographicLib/MGRS.hpp new file mode 100644 index 0000000..b2cef75 --- /dev/null +++ b/external/include/GeographicLib/MGRS.hpp @@ -0,0 +1,361 @@ +/** + * \file MGRS.hpp + * \brief Header for GeographicLib::MGRS class + * + * Copyright (c) Charles Karney (2008-2020) 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 +#include + +#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, + * + * Datums, Ellipsoids, Grids, and Grid Reference Systems, + * Defense Mapping Agency, Technical Manual TM8358.1 (1990). + * . + * This document has been updated by the two NGA documents + * - + * Universal Grids and Grid Reference Systems, + * NGA.STND.0037 (2014). + * - + * The Universal Grids and the Transverse Mercator and Polar Stereographic + * Map Projections, 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 ⌊106 x⌋ 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 NGA software package + * geotrans + * 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 = −1 (min), only the grid zone is returned + * - \e prec = 0, 100 km + * - \e prec = 1, 10 km + * - \e prec = 2, 1 km + * - \e prec = 3, 100 m + * - \e prec = 4, 10 m + * - \e prec = 5, 1 m + * - \e prec = 6, 0.1 m + * - … + * - \e prec = 11 (max), 1 μm + * + * UTM eastings are allowed to be in the range [100 km, 900 km], northings + * are allowed to be in in [0 km, 9500 km] for the northern hemisphere and + * in [1000 km, 10000 km] for the southern hemisphere. (However UTM + * northings can be continued across the equator. So the actual limits on + * the northings are [−9000 km, 9500 km] for the "northern" + * hemisphere and [1000 km, 19500 km] for the "southern" hemisphere.) + * + * UPS eastings/northings are allowed to be in the range [1300 km, 2700 km] + * in the northern hemisphere and in [800 km, 3200 km] in the southern + * hemisphere. + * + * The ranges are 100 km more restrictive than for the conversion between + * geographic coordinates and UTM and UPS given by UTMUPS. These + * restrictions are dictated by the allowed letters in MGRS coordinates. + * The choice of 9500 km for the maximum northing for northern hemisphere + * and of 1000 km as the minimum northing for southern hemisphere provide + * at least 0.5 degree extension into standard UPS zones. The upper ends + * of the ranges for the UPS coordinates is dictated by requiring symmetry + * about the meridians 0E and 90E. + * + * All allowed UTM and UPS coordinates may now be converted to legal MGRS + * coordinates with the proviso that eastings and northings on the upper + * boundaries are silently reduced by about 4 nm (4 nanometers) to place + * them \e within the allowed range. (This includes reducing a southern + * hemisphere northing of 10000 km by 4 nm so that it is placed in latitude + * band M.) The UTM or UPS coordinates are truncated to requested + * precision to determine the MGRS coordinate. Thus in UTM zone 38n, the + * square area with easting in [444 km, 445 km) and northing in [3688 km, + * 3689 km) maps to MGRS coordinate 38SMB4488 (at \e prec = 2, 1 km), + * Khulani Sq., Baghdad. + * + * The UTM/UPS selection and the UTM zone is preserved in the conversion to + * MGRS coordinate. Thus for \e zone > 0, the MGRS coordinate begins with + * the zone number followed by one of [C--M] for the southern + * hemisphere and [N--X] for the northern hemisphere. For \e zone = + * 0, the MGRS coordinates begins with one of [AB] for the southern + * hemisphere and [XY] for the northern hemisphere. + * + * The conversion to the MGRS is exact for prec in [0, 5] except that a + * neighboring latitude band letter may be given if the point is within 5nm + * of a band boundary. For prec in [6, 11], the conversion is accurate to + * roundoff. + * + * If \e prec = −1, then the "grid zone designation", e.g., 18T, is + * returned. This consists of the UTM zone number (absent for UPS) and the + * first letter of the MGRS string which labels the latitude band for UTM + * and the hemisphere for UPS. + * + * If \e x or \e y is NaN or if \e zone is UTMUPS::INVALID, the returned + * MGRS string is "INVALID". + * + * Return the result via a reference argument to avoid the overhead of + * allocating a potentially large number of small strings. If an error is + * thrown, then \e mgrs is unchanged. + **********************************************************************/ + static void Forward(int zone, bool northp, real x, real y, + int prec, std::string& mgrs); + + /** + * Convert UTM or UPS coordinate to an MGRS coordinate when the latitude is + * known. + * + * @param[in] zone UTM zone (zero means UPS). + * @param[in] northp hemisphere (true means north, false means south). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[in] lat latitude (degrees). + * @param[in] prec precision relative to 100 km. + * @param[out] mgrs MGRS string. + * @exception GeographicErr if \e zone, \e x, or \e y is outside its + * allowed range. + * @exception GeographicErr if \e lat is inconsistent with the given UTM + * coordinates. + * @exception std::bad_alloc if the memory for \e mgrs can't be allocated. + * + * The latitude is ignored for \e zone = 0 (UPS); otherwise the latitude is + * used to determine the latitude band and this is checked for consistency + * using the same tests as Reverse. + **********************************************************************/ + static void Forward(int zone, bool northp, real x, real y, real lat, + int prec, std::string& mgrs); + + /** + * Convert a MGRS coordinate to UTM or UPS coordinates. + * + * @param[in] mgrs MGRS string. + * @param[out] zone UTM zone (zero means UPS). + * @param[out] northp hemisphere (true means north, false means south). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] prec precision relative to 100 km. + * @param[in] centerp if true (default), return center of the MGRS square, + * else return SW (lower left) corner. + * @exception GeographicErr if \e mgrs is illegal. + * + * All conversions from MGRS to UTM/UPS are permitted provided the MGRS + * coordinate is a possible result of a conversion in the other direction. + * (The leading 0 may be dropped from an input MGRS coordinate for UTM + * zones 1--9.) In addition, MGRS coordinates with a neighboring + * latitude band letter are permitted provided that some portion of the + * 100 km block is within the given latitude band. Thus + * - 38VLS and 38WLS are allowed (latitude 64N intersects the square + * 38[VW]LS); but 38VMS is not permitted (all of 38WMS is north of 64N) + * - 38MPE and 38NPF are permitted (they straddle the equator); but 38NPE + * and 38MPF are not permitted (the equator does not intersect either + * block). + * - Similarly ZAB and YZB are permitted (they straddle the prime + * meridian); but YAB and ZZB are not (the prime meridian does not + * intersect either block). + * + * The UTM/UPS selection and the UTM zone is preserved in the conversion + * from MGRS coordinate. The conversion is exact for prec in [0, 5]. With + * \e centerp = true, the conversion from MGRS to geographic and back is + * stable. This is not assured if \e centerp = false. + * + * If a "grid zone designation" (for example, 18T or A) is given, then some + * suitable (but essentially arbitrary) point within that grid zone is + * returned. The main utility of the conversion is to allow \e zone and \e + * northp to be determined. In this case, the \e centerp parameter is + * ignored and \e prec is set to −1. + * + * If the first 3 characters of \e mgrs are "INV", then \e x and \e y are + * set to NaN, \e zone is set to UTMUPS::INVALID, and \e prec is set to + * −2. + * + * If an exception is thrown, then the arguments are unchanged. + **********************************************************************/ + static void Reverse(const std::string& mgrs, + int& zone, bool& northp, real& x, real& y, + int& prec, bool centerp = true); + + /** \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 diff --git a/external/include/GeographicLib/MagneticCircle.hpp b/external/include/GeographicLib/MagneticCircle.hpp new file mode 100644 index 0000000..515ddaf --- /dev/null +++ b/external/include/GeographicLib/MagneticCircle.hpp @@ -0,0 +1,204 @@ +/** + * \file MagneticCircle.hpp + * \brief Header for GeographicLib::MagneticCircle class + * + * Copyright (c) Charles Karney (2011-2021) 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 +#include +#include + +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 + * + * MagneticField 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 diff --git a/external/include/GeographicLib/MagneticModel.hpp b/external/include/GeographicLib/MagneticModel.hpp new file mode 100644 index 0000000..2d8239a --- /dev/null +++ b/external/include/GeographicLib/MagneticModel.hpp @@ -0,0 +1,406 @@ +/** + * \file MagneticModel.hpp + * \brief Header for GeographicLib::MagneticModel class + * + * Copyright (c) Charles Karney (2011-2021) 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 +#include +#include + +#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 + * + * MagneticField 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 > _G; + std::vector< std::vector > _H; + std::vector _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 ≥ 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 diff --git a/external/include/GeographicLib/Math.hpp b/external/include/GeographicLib/Math.hpp new file mode 100644 index 0000000..3093544 --- /dev/null +++ b/external/include/GeographicLib/Math.hpp @@ -0,0 +1,684 @@ +/** + * \file Math.hpp + * \brief Header for GeographicLib::Math class + * + * Copyright (c) Charles Karney (2008-2020) 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 + +#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 +#include +#include + +#if GEOGRAPHICLIB_PRECISION == 4 +#include +#include +#include +#elif GEOGRAPHICLIB_PRECISION == 5 +#include +#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). (CAUTION: 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 π. + **********************************************************************/ + template 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 static T degree() { + static const T degree = pi() / 180; + return degree; + } + + /** + * Square a number. + * + * @tparam T the type of the argument and the returned value. + * @param[in] x + * @return x2. + **********************************************************************/ + template 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(x2 + y2). + * + * \deprecated Use std::hypot(x, y). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use std::hypot(x, y)") + static T hypot(T x, T y); + + /** + * exp(\e x) − 1 accurate near \e x = 0. + * + * @tparam T the type of the argument and the returned value. + * @param[in] x + * @return exp(\e x) − 1. + * + * \deprecated Use std::expm1(x). + **********************************************************************/ + template + 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 + 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 + 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 + 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 = −0, returning + * &minus|x|. + * + * \deprecated Use std::copysign(x, y). + **********************************************************************/ + template + 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 + 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 [−\e y/2, \e y/2]. + * + * \deprecated Use std::remainder(x). + **********************************************************************/ + template + 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 [−\e y/2, \e y/2]. + * + * \deprecated Use std::remquo(x, y, n). + **********************************************************************/ + template + 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 + 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 + 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 xy + z, correctly rounded (on those platforms with + * support for the fma instruction). + * + * On platforms without the fma 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 + 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 x/hypot(x, y). + * @param[in,out] y on output set to y/hypot(x, y). + **********************************************************************/ + template 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 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 y = ∑n=0..N + * pn xNn. + * Return 0 if \e N < 0. Return p0, if \e N = 0 (even + * if \e x is infinite or a nan). The evaluation uses Horner's method. + **********************************************************************/ + template 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 (−180°, 180°]. + * + * The range of \e x is unrestricted. + **********************************************************************/ + template 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 [−90°, 90°], otherwise + * return NaN. + **********************************************************************/ + template static T LatFix(T x) + { using std::abs; return abs(x) > 90 ? NaN() : x; } + + /** + * The exact difference of two angles reduced to + * (−180°, 180°]. + * + * @tparam T the type of the arguments and returned value. + * @param[in] x the first angle in degrees. + * @param[in] y the second angle in degrees. + * @param[out] e the error term in degrees. + * @return \e d, the truncated value of \e y − \e x. + * + * This computes \e z = \e y − \e x exactly, reduced to + * (−180°, 180°]; and then sets \e z = \e d + \e e where \e d + * is the nearest representable number to \e z and \e e is the truncation + * error. If \e d = −180, then \e e > 0; If \e d = 180, then \e e + * ≤ 0. + **********************************************************************/ + template 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 [−180°, 180°] + * + * @tparam T the type of the arguments and returned value. + * @param[in] x the first angle in degrees. + * @param[in] y the second angle in degrees. + * @return \e y − \e x, reduced to the range [−180°, + * 180°]. + * + * The result is equivalent to computing the difference exactly, reducing + * it to (−180°, 180°] and rounding the result. Note that + * this prescription allows −180° to be returned (e.g., if \e x + * is tiny and negative and \e y = 180°). + **********************************************************************/ + template static T AngDiff(T x, T y) + { T e; return AngDiff(x, y, e); } + + /** + * Coarsen a value close to zero. + * + * @tparam T the type of the argument and returned value. + * @param[in] x + * @return the coarsened value. + * + * The makes the smallest gap in \e x = 1/16 − nextafter(1/16, 0) = + * 1/257 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°.) We use this to avoid having to deal with near + * singular cases when \e x is non-zero but tiny (e.g., + * 10−200). This converts −0 to +0; however tiny + * negative numbers get converted to −0. + **********************************************************************/ + template 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(x). + * @param[out] cosx cos(x). + * + * The results obey exactly the elementary properties of the trigonometric + * functions, e.g., sin 9° = cos 81° = − sin 123456789°. + * If x = −0, then \e sinx = −0; this is the only case where + * −0 is returned. + **********************************************************************/ + template 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(x). + **********************************************************************/ + template 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(x). + **********************************************************************/ + template 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(x). + * + * If \e x = ±90°, then a suitably large (but finite) value is + * returned. + **********************************************************************/ + template 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(y, x) in degrees. + * + * The result is in the range (−180° 180°]. N.B., + * atan2d(±0, −1) = +180°; atan2d(−ε, + * −1) = −180°, for ε positive and tiny; + * atan2d(±0, +1) = ±0°. + **********************************************************************/ + template 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(x) in degrees. + **********************************************************************/ + template static T atand(T x); + + /** + * Evaluate e atanh(e x) + * + * @tparam T the type of the argument and the returned value. + * @param[in] x + * @param[in] es the signed eccentricity = sign(e2) + * sqrt(|e2|) + * @return e atanh(e x) + * + * If e2 is negative (e is imaginary), the + * expression is evaluated in terms of atan. + **********************************************************************/ + template static T eatanhe(T x, T es); + + /** + * tanχ in terms of tanφ + * + * @tparam T the type of the argument and the returned value. + * @param[in] tau τ = tanφ + * @param[in] es the signed eccentricity = sign(e2) + * sqrt(|e2|) + * @return τ′ = tanχ + * + * See Eqs. (7--9) of + * C. F. F. Karney, + * + * Transverse Mercator with an accuracy of a few nanometers, + * J. Geodesy 85(8), 475--485 (Aug. 2011) + * (preprint + * arXiv:1002.1417). + **********************************************************************/ + template static T taupf(T tau, T es); + + /** + * tanφ in terms of tanχ + * + * @tparam T the type of the argument and the returned value. + * @param[in] taup τ′ = tanχ + * @param[in] es the signed eccentricity = sign(e2) + * sqrt(|e2|) + * @return τ = tanφ + * + * See Eqs. (19--21) of + * C. F. F. Karney, + * + * Transverse Mercator with an accuracy of a few nanometers, + * J. Geodesy 85(8), 475--485 (Aug. 2011) + * (preprint + * arXiv:1002.1417). + **********************************************************************/ + template 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 + 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 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 + 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 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 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 diff --git a/external/include/GeographicLib/NearestNeighbor.hpp b/external/include/GeographicLib/NearestNeighbor.hpp new file mode 100644 index 0000000..56edba2 --- /dev/null +++ b/external/include/GeographicLib/NearestNeighbor.hpp @@ -0,0 +1,837 @@ +/** + * \file NearestNeighbor.hpp + * \brief Header for GeographicLib::NearestNeighbor class + * + * Copyright (c) Charles Karney (2016-2020) 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 // for nth_element, max_element, etc. +#include +#include // for priority_queue +#include // for swap + pair +#include +#include +#include +#include +#include +// Only for GeographicLib::GeographicErr +#include + +#if defined(GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION) && \ + GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION +#include +#include +#include +#include +#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 double. + * @tparam pos_t the type for specifying the positions of points; geodetic + * application might bundled the latitude and longitude into a + * std::pair. + * @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 dist_t operator() (const pos_t&, const + * pos_t&) const, 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 do not. 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 Boost + * serialization 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 + 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& 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& pts, const distfun_t& dist, + int bucket = 4) { + static_assert(std::numeric_limits::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::max())) + throw GeographicLib::GeographicErr("pts array too big"); + // the pair contains distance+id + std::vector ids(pts.size()); + for (int k = int(ids.size()); k--;) + ids[k] = std::make_pair(dist_t(0), k); + int cost = 0; + std::vector 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::max(); + } + + /** + * Search the NearestNeighbor. + * + * @param[in] pts the vector of points used for initialization. + * @param[in] dist the distance function object used for initialization. + * @param[in] query the query point. + * @param[out] ind a vector of indices to the closest points found. + * @param[in] k the number of points to search for (default = 1). + * @param[in] maxdist only return points with distances of \e maxdist or + * less from \e query (default is the maximum \e dist_t). + * @param[in] mindist only return points with distances of more than + * \e mindist from \e query (default = −1). + * @param[in] exhaustive whether to do an exhaustive search (default true). + * @param[in] tol the tolerance on the results (default 0). + * @return the distance to the closest point found (−1 if no points + * are found). + * @exception GeographicErr if \e pts has a different size from that used + * to construct the object. + * + * The indices returned in \e ind are sorted by distance from \e query + * (closest first). + * + * The simplest invocation is with just the 4 non-optional arguments. This + * returns the closest distance and the index to the closest point in + * ind0. If there are several points equally close, then + * ind0 gives the index of an arbirary one of them. If + * there's no closest point (because the set of points is empty), then \e + * ind is empty and −1 is returned. + * + * With \e exhaustive = true and \e tol = 0 (their default values), this + * finds the indices of \e k closest neighbors to \e query whose distances + * to \e query are in (\e mindist, \e maxdist]. If \e mindist and \e + * maxdist have their default values, then these bounds have no effect. If + * \e query is one of the points in the tree, then set \e mindist = 0 to + * prevent this point (and other coincident points) from being returned. + * + * If \e exhaustive = false, exit as soon as \e k results satisfying the + * distance criteria are found. If less than \e k results are returned + * then the search was exhaustive even if \e exhaustive = false. + * + * If \e tol is positive, do an approximate search; in this case the + * results are to be interpreted as follows: if the k'th distance is + * \e dk, then all results with distances less than or equal \e dk − + * \e tol are correct; all others are suspect — there may be other + * closer results with distances greater or equal to \e dk − \e tol. + * If less than \e k results are found, then the search is exact. + * + * \e mindist should be used to exclude a "small" neighborhood of the query + * point (relative to the average spacing of the data). If \e mindist is + * large, the efficiency of the search deteriorates. + * + * \note Only the shortest distance is returned (as as the function value). + * The distances to other points (indexed by indj + * 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& pts, const distfun_t& dist, + const pos_t& query, + std::vector& ind, + int k = 1, + dist_t maxdist = std::numeric_limits::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 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 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 + * Boost serialization 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::digits * + (std::numeric_limits::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(buf), 6 * sizeof(int)); + for (int i = 0; i < int(_tree.size()); ++i) { + const Node& node = _tree[i]; + os.write(reinterpret_cast(&node.index), sizeof(int)); + if (node.index >= 0) { + os.write(reinterpret_cast(node.data.lower), + 2 * sizeof(dist_t)); + os.write(reinterpret_cast(node.data.upper), + 2 * sizeof(dist_t)); + os.write(reinterpret_cast(node.data.child), + 2 * sizeof(int)); + } else { + os.write(reinterpret_cast(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::is_integer) { + static const int prec + = int(std::ceil(std::numeric_limits::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 + * Boost serialization 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(&version1), sizeof(int)); + is.read(reinterpret_cast(&realspec), sizeof(int)); + is.read(reinterpret_cast(&bucket), sizeof(int)); + is.read(reinterpret_cast(&numpoints), sizeof(int)); + is.read(reinterpret_cast(&treesize), sizeof(int)); + is.read(reinterpret_cast(&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::digits * + (std::numeric_limits::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 tree; + tree.reserve(treesize); + for (int i = 0; i < treesize; ++i) { + Node node; + if (bin) { + is.read(reinterpret_cast(&node.index), sizeof(int)); + if (node.index >= 0) { + is.read(reinterpret_cast(node.data.lower), + 2 * sizeof(dist_t)); + is.read(reinterpret_cast(node.data.upper), + 2 * sizeof(dist_t)); + is.read(reinterpret_cast(node.data.child), + 2 * sizeof(int)); + } else { + is.read(reinterpret_cast(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::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::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 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 + 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 + 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 + 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 void save(Archive& ar, const unsigned) const { + int realspec = std::numeric_limits::digits * + (std::numeric_limits::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 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 tree; + ar & boost::serialization::make_nvp("realspec", realspec); + if (!( realspec == std::numeric_limits::digits * + (std::numeric_limits::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::max(); + } + template + void serialize(Archive& ar, const unsigned int file_version) + { boost::serialization::split_member(ar, *this, file_version); } +#endif + + int _numpoints, _bucket, _cost; + std::vector _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& pts, const distfun_t& dist, int bucket, + std::vector& tree, std::vector& 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::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::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 + void swap(GeographicLib::NearestNeighbor& a, + GeographicLib::NearestNeighbor& b) { + a.swap(b); + } + +} // namespace std + +#if defined(_MSC_VER) +# pragma warning (pop) +#endif + +#endif // GEOGRAPHICLIB_NEARESTNEIGHBOR_HPP diff --git a/external/include/GeographicLib/NormalGravity.hpp b/external/include/GeographicLib/NormalGravity.hpp new file mode 100644 index 0000000..07aa88e --- /dev/null +++ b/external/include/GeographicLib/NormalGravity.hpp @@ -0,0 +1,400 @@ +/** + * \file NormalGravity.hpp + * \brief Header for GeographicLib::NormalGravity class + * + * Copyright (c) Charles Karney (2011-2020) 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 +#include + +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, + * (a2b2)1/2, 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(b2 − + * a2)1/2. 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 + * b/\e a ∈ [0.01, 100] or \e f ∈ [−99, 0.99]. + * + * Definitions: + * - V0, the gravitational contribution to the normal + * potential; + * - Φ, the rotational contribution to the normal potential; + * - \e U = V0 + Φ, the total potential; + * - Γ = ∇V0, the acceleration due to + * mass of the earth; + * - f = ∇Φ, the centrifugal acceleration; + * - γ = ∇\e U = Γ + f, 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, 4, 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 + * (meters3/seconds2); 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−1). + * @param[in] f_J2 either the flattening of the ellipsoid \e f or the + * the dynamical form factor \e J2. + * @param[out] geometricp if true (the default), then \e f_J2 denotes the + * flattening, else it denotes the dynamical form factor \e J2. + * @exception if \e a is not positive or if the other parameters do not + * obey the restrictions given below. + * + * The shape of the ellipsoid can be given in one of two ways: + * - geometrically (\e geomtricp = true), the ellipsoid is defined by the + * flattening \e f = (\e a − \e b) / \e a, where \e a and \e b are + * the equatorial radius and the polar semi-axis. The parameters should + * obey \e a > 0, \e f < 1. There are no restrictions on \e GM or + * \e omega, in particular, \e GM need not be positive. + * - physically (\e geometricp = false), the ellipsoid is defined by the + * dynamical form factor J2 = (\e C − \e A) / + * Ma2, where \e A and \e C are the equatorial and + * polar moments of inertia and \e M is the mass of the earth. The + * parameters should obey \e a > 0, \e GM > 0 and \e J2 < 1/3 + * − (omega2a3/GM) + * 8/(45π). There is no restriction on \e omega. + **********************************************************************/ + NormalGravity(real a, real GM, real omega, real f_J2, + bool geometricp = true); + + /** + * A default constructor for the normal gravity. This sets up an + * uninitialized object and is used by GravityModel which constructs this + * object before it has read in the parameters for the reference ellipsoid. + **********************************************************************/ + NormalGravity() : _a(-1) {} + ///@} + + /** \name Compute the gravity + **********************************************************************/ + ///@{ + /** + * Evaluate the gravity on the surface of the ellipsoid. + * + * @param[in] lat the geographic latitude (degrees). + * @return γ the acceleration due to gravity, positive downwards + * (m s−2). + * + * 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−2). + * @param[out] gammaz the upward component of the acceleration + * (m s−2); this is usually negative. + * @return \e U the corresponding normal potential + * (m2 s−2). + * + * 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−2). + * @param[out] gammaY the \e Y component of the acceleration + * (m s−2). + * @param[out] gammaZ the \e Z component of the acceleration + * (m s−2). + * @return \e U = V0 + Φ the sum of the + * gravitational and centrifugal potentials + * (m2 s−2). + * + * The acceleration given by γ = ∇\e U = + * ∇V0 + ∇Φ = Γ + f. + **********************************************************************/ + 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−2). + * @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−2). + * @return V0 the gravitational potential + * (m2 s−2). + * + * 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−2). + * @param[out] fY the \e Y component of the centrifugal acceleration + * (m s−2). + * @return Φ the centrifugal potential (m2 + * s−2). + * + * Φ 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 + * (m3 s−2). This is the value used in the + * constructor. + **********************************************************************/ + Math::real MassConstant() const + { return Init() ? _GM : Math::NaN(); } + + /** + * @return Jn the dynamical form factors of the + * ellipsoid. + * + * If \e n = 2 (the default), this is the value of J2 + * used in the constructor. Otherwise it is the zonal coefficient of the + * Legendre harmonic sum of the normal gravitational potential. Note that + * Jn = 0 if \e n is odd. In most gravity + * applications, fully normalized Legendre functions are used and the + * corresponding coefficient is Cn0 = + * −Jn / sqrt(2 \e n + 1). + **********************************************************************/ + Math::real DynamicalFormFactor(int n = 2) const + { return Init() ? ( n == 2 ? _J2 : Jn(n)) : Math::NaN(); } + + /** + * @return ω the angular velocity of the ellipsoid (rad + * s−1). This is the value used in the constructor. + **********************************************************************/ + Math::real AngularVelocity() const + { return Init() ? _omega : Math::NaN(); } + + /** + * @return f the flattening of the ellipsoid (\e a − \e b)/\e + * a. + **********************************************************************/ + Math::real Flattening() const + { return Init() ? _f : Math::NaN(); } + + /** + * @return γe the normal gravity at equator (m + * s−2). + **********************************************************************/ + Math::real EquatorialGravity() const + { return Init() ? _gammae : Math::NaN(); } + + /** + * @return γp the normal gravity at poles (m + * s−2). + **********************************************************************/ + Math::real PolarGravity() const + { return Init() ? _gammap : Math::NaN(); } + + /** + * @return f* the gravity flattening (γp − + * γe) / γe. + **********************************************************************/ + Math::real GravityFlattening() const + { return Init() ? _fstar : Math::NaN(); } + + /** + * @return U0 the constant normal potential for the + * surface of the ellipsoid (m2 s−2). + **********************************************************************/ + 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 + * (meters3/seconds2); 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−1). + * @param[in] J2 the dynamical form factor. + * @return \e f the flattening of the ellipsoid. + * + * This routine requires \e a > 0, \e GM > 0, \e J2 < 1/3 − + * omega2a3/GM 8/(45π). A + * NaN is returned if these conditions do not hold. The restriction to + * positive \e GM is made because for negative \e GM two solutions are + * possible. + **********************************************************************/ + static Math::real J2ToFlattening(real a, real GM, real omega, real J2); + + /** + * Compute the dynamical form factor from the flattening. + * + * @param[in] a equatorial radius (meters). + * @param[in] GM mass constant of the ellipsoid + * (meters3/seconds2); 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−1). + * @param[in] f the flattening of the ellipsoid. + * @return \e J2 the dynamical form factor. + * + * This routine requires \e a > 0, \e GM ≠ 0, \e f < 1. The + * values of these parameters are not checked. + **********************************************************************/ + static Math::real FlatteningToJ2(real a, real GM, real omega, real f); + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_NORMALGRAVITY_HPP diff --git a/external/include/GeographicLib/OSGB.hpp b/external/include/GeographicLib/OSGB.hpp new file mode 100644 index 0000000..c2e7797 --- /dev/null +++ b/external/include/GeographicLib/OSGB.hpp @@ -0,0 +1,255 @@ +/** + * \file OSGB.hpp + * \brief Header for GeographicLib::OSGB class + * + * Copyright (c) Charles Karney (2010-2020) 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 +#include + +#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 guide to coordinate systems in Great Britain + * - + * Guide to the National Grid + * + * \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 [−90°, 90°]. + **********************************************************************/ + static void Forward(real lat, real lon, + real& x, real& y, real& gamma, real& k) { + OSGBTM().Forward(OriginLongitude(), lat, lon, x, y, gamma, k); + x += FalseEasting(); + y += computenorthoffset(); + } + + /** + * Reverse projection, from OSGB coordinates to geographic. + * + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * The value of \e lon returned is in the range [−180°, + * 180°]. + **********************************************************************/ + + static void Reverse(real x, real y, + real& lat, real& lon, real& gamma, real& k) { + x -= FalseEasting(); + y -= computenorthoffset(); + OSGBTM().Reverse(OriginLongitude(), x, y, lat, lon, gamma, k); + } + + /** + * OSGB::Forward without returning the convergence and scale. + **********************************************************************/ + static void Forward(real lat, real lon, real& x, real& y) { + real gamma, k; + Forward(lat, lon, x, y, gamma, k); + } + + /** + * OSGB::Reverse without returning the convergence and scale. + **********************************************************************/ + static void Reverse(real x, real y, real& lat, real& lon) { + real gamma, k; + Reverse(x, y, lat, lon, gamma, k); + } + + /** + * Convert OSGB coordinates to a grid reference. + * + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[in] prec precision relative to 100 km. + * @param[out] gridref National Grid reference. + * @exception GeographicErr if \e prec, \e x, or \e y is outside its + * allowed range. + * @exception std::bad_alloc if the memory for \e gridref can't be + * allocatied. + * + * \e prec specifies the precision of the grid reference string as follows: + * - prec = 0 (min), 100km + * - prec = 1, 10km + * - prec = 2, 1km + * - prec = 3, 100m + * - prec = 4, 10m + * - prec = 5, 1m + * - prec = 6, 0.1m + * - prec = 11 (max), 1μm + * + * The easting must be in the range [−1000 km, 1500 km) and the + * northing must be in the range [−500 km, 2000 km). These bounds + * are consistent with rules for the letter designations for the grid + * system. + * + * If \e x or \e y is NaN, the returned grid reference is "INVALID". + **********************************************************************/ + static void GridReference(real x, real y, int prec, std::string& gridref); + + /** + * Convert OSGB 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 −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 = + * 109.48401603−10 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 A guide to coordinate systems + * in Great Britain, 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 − 20853810)/20923713 = + * 7767/2324857 = 1/299.32496459... (The Airy 1830 value is returned + * because the OSGB projection is based on this ellipsoid.) + **********************************************************************/ + static Math::real Flattening() + { return real(20923713 - 20853810) / real(20923713); } + + /** + * @return \e k0 central scale for the OSGB projection (0.9996012717...). + * + * C. J. Mugnier, Grids & Datums, PE&RS, Oct. 2003, states that + * this is defined as 109.9998268−10. + **********************************************************************/ + static Math::real CentralScale() { + using std::pow; + return pow(real(10), real(9998268 - 10000000) / 10000000); + } + + /** + * @return latitude of the origin for the OSGB projection (49 degrees). + **********************************************************************/ + static Math::real OriginLatitude() { return real(49); } + + /** + * @return longitude of the origin for the OSGB projection (−2 + * degrees). + **********************************************************************/ + static Math::real OriginLongitude() { return real(-2); } + + /** + * @return false northing the OSGB projection (−100000 meters). + **********************************************************************/ + static Math::real FalseNorthing() { return real(-100000); } + + /** + * @return false easting the OSGB projection (400000 meters). + **********************************************************************/ + static Math::real FalseEasting() { return real(400000); } + + /** + * \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 diff --git a/external/include/GeographicLib/PolarStereographic.hpp b/external/include/GeographicLib/PolarStereographic.hpp new file mode 100644 index 0000000..e828706 --- /dev/null +++ b/external/include/GeographicLib/PolarStereographic.hpp @@ -0,0 +1,160 @@ +/** + * \file PolarStereographic.hpp + * \brief Header for GeographicLib::PolarStereographic class + * + * Copyright (c) Charles Karney (2008-2020) 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 + +namespace GeographicLib { + + /** + * \brief Polar stereographic projection + * + * Implementation taken from the report, + * - J. P. Snyder, + * Map Projections: A + * Working Manual, 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 − \e f) \e a, or \e k0 is + * not positive. + **********************************************************************/ + PolarStereographic(real a, real f, real k0); + + /** + * Set the scale for the projection. + * + * @param[in] lat (degrees) assuming \e northp = true. + * @param[in] k scale at latitude \e lat (default 1). + * @exception GeographicErr \e k is not positive. + * @exception GeographicErr if \e lat is not in (−90°, + * 90°]. + **********************************************************************/ + void SetScale(real lat, real k = real(1)); + + /** + * Forward projection, from geographic to polar stereographic. + * + * @param[in] northp the pole which is the center of projection (true means + * north, false means south). + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * No false easting or northing is added. \e lat should be in the range + * (−90°, 90°] for \e northp = true and in the range + * [−90°, 90°) for \e northp = false. + **********************************************************************/ + void Forward(bool northp, real lat, real lon, + real& x, real& y, real& gamma, real& k) const; + + /** + * Reverse projection, from polar stereographic to geographic. + * + * @param[in] northp the pole which is the center of projection (true means + * north, false means south). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * No false easting or northing is added. The value of \e lon returned is + * in the range [−180°, 180°]. + **********************************************************************/ + void Reverse(bool northp, real x, real y, + real& lat, real& lon, real& gamma, real& k) const; + + /** + * PolarStereographic::Forward without returning the convergence and scale. + **********************************************************************/ + void Forward(bool northp, real lat, real lon, + real& x, real& y) const { + real gamma, k; + Forward(northp, lat, lon, x, y, gamma, k); + } + + /** + * PolarStereographic::Reverse without returning the convergence and scale. + **********************************************************************/ + void Reverse(bool northp, real x, real y, + real& lat, real& lon) const { + real gamma, k; + Reverse(northp, x, y, lat, lon, gamma, k); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _a; } + + /** + * @return \e f the flattening of the ellipsoid. This is the value used in + * the constructor. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * The central scale for the projection. This is the value of \e k0 used + * in the constructor and is the scale at the pole unless overridden by + * PolarStereographic::SetScale. + **********************************************************************/ + Math::real CentralScale() const { return _k0; } + + /** + * \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 diff --git a/external/include/GeographicLib/PolygonArea.hpp b/external/include/GeographicLib/PolygonArea.hpp new file mode 100644 index 0000000..f94b32c --- /dev/null +++ b/external/include/GeographicLib/PolygonArea.hpp @@ -0,0 +1,325 @@ +/** + * \file PolygonArea.hpp + * \brief Header for GeographicLib::PolygonAreaT class + * + * Copyright (c) Charles Karney (2010-2021) 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 +#include +#include +#include + +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, + * + * Algorithms for geodesics, + * J. Geodesy 87, 43--55 (2013); + * DOI: + * 10.1007/s00190-012-0578-z; + * addenda: + * + * geod-addenda.html. + * + * 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 m2 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 107 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
+   *     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
+   * 
+ * 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
+   *     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
+   * 
+ * + * @tparam GeodType the geodesic class to use. + * + * Example of use: + * \include example-PolygonArea.cpp + * + * Planimeter is a command-line utility + * providing access to the functionality of PolygonAreaT. + **********************************************************************/ + + template + 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 + void AreaReduce(T& area, int crossings, bool reverse, bool sign) const; + public: + + /** + * Constructor for PolygonAreaT. + * + * @param[in] earth the Geodesic object to use for geodesic calculations. + * @param[in] polyline if true that treat the points as defining a polyline + * instead of a polygon (default = false). + **********************************************************************/ + PolygonAreaT(const GeodType& earth, bool polyline = false) + : _earth(earth) + , _area0(_earth.EllipsoidArea()) + , _polyline(polyline) + , _mask(GeodType::LATITUDE | GeodType::LONGITUDE | GeodType::DISTANCE | + (_polyline ? GeodType::NONE : + GeodType::AREA | GeodType::LONG_UNROLL)) + { Clear(); } + + /** + * Clear PolygonAreaT, allowing a new polygon to be started. + **********************************************************************/ + void Clear() { + _num = 0; + _crossings = 0; + _areasum = 0; + _perimetersum = 0; + _lat0 = _lon0 = _lat1 = _lon1 = Math::NaN(); + } + + /** + * Add a point to the polygon or polyline. + * + * @param[in] lat the latitude of the point (degrees). + * @param[in] lon the longitude of the point (degrees). + * + * \e lat should be in the range [−90°, 90°]. + **********************************************************************/ + void AddPoint(real lat, real lon); + + /** + * Add an edge to the polygon or polyline. + * + * @param[in] azi azimuth at current point (degrees). + * @param[in] s distance from current point to next point (meters). + * + * This does nothing if no points have been added yet. Use + * PolygonAreaT::CurrentPoint to determine the position of the new vertex. + **********************************************************************/ + void AddEdge(real azi, real s); + + /** + * Return the results so far. + * + * @param[in] reverse if true then clockwise (instead of 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 (meters2); 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 + * (meters2); only set if polyline is false in the + * constructor. + * @return the number of points. + * + * \e lat should be in the range [−90°, 90°]. + **********************************************************************/ + unsigned TestPoint(real lat, real lon, bool reverse, bool sign, + real& perimeter, real& area) const; + + /** + * Return the results assuming a tentative final test point is added via an + * azimuth and distance; however, the data for the test point is not saved. + * This lets you report a running result for the perimeter and area as the + * user moves the mouse cursor. Ordinary floating point arithmetic is used + * to accumulate the data for the test point; thus the area and perimeter + * returned are less accurate than if PolygonAreaT::AddEdge and + * PolygonAreaT::Compute are used. + * + * @param[in] azi azimuth at current point (degrees). + * @param[in] s distance from current point to final test point (meters). + * @param[in] reverse if true then clockwise (instead of 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 + * (meters2); only set if polyline is false in the + * constructor. + * @return the number of points. + **********************************************************************/ + unsigned TestEdge(real azi, real s, bool reverse, bool sign, + real& perimeter, real& area) const; + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value inherited from the Geodesic object used in the constructor. + **********************************************************************/ + + Math::real EquatorialRadius() const { return _earth.EquatorialRadius(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the Geodesic object used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _earth.Flattening(); } + + /** + * Report the previous vertex added to the polygon or polyline. + * + * @param[out] lat the latitude of the point (degrees). + * @param[out] lon the longitude of the point (degrees). + * + * If no points have been added, then NaNs are returned. Otherwise, \e lon + * will be in the range [−180°, 180°]. + **********************************************************************/ + void CurrentPoint(real& lat, real& lon) const + { lat = _lat1; lon = _lon1; } + + /** + * \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 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 PolygonAreaExact; + + /** + * @relates PolygonAreaT + * + * Polygon areas using Rhumb. + **********************************************************************/ + typedef PolygonAreaT PolygonAreaRhumb; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_POLYGONAREA_HPP diff --git a/external/include/GeographicLib/Rhumb.hpp b/external/include/GeographicLib/Rhumb.hpp new file mode 100644 index 0000000..0cb8afd --- /dev/null +++ b/external/include/GeographicLib/Rhumb.hpp @@ -0,0 +1,621 @@ +/** + * \file Rhumb.hpp + * \brief Header for GeographicLib::Rhumb and GeographicLib::RhumbLine classes + * + * Copyright (c) Charles Karney (2014-2021) 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 +#include + +#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 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 (lat1,lon1), (0,lon1), + * (0,lon2), and (lat2,lon2). + * + * 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 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 |f| < 0.01). + * @exception GeographicErr if \e a or (1 − \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 (meters2). + * + * \e lat1 should be in the range [−90°, 90°]. The value of + * \e lon2 returned is in the range [−180°, 180°]. + * + * If point 1 is a pole, the cosine of its latitude is taken to be + * 1/ε2 (where ε is 2-52). 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 (meters2). + * + * The Rhumb::mask values possible for \e outmask are + * - \e outmask |= Rhumb::LATITUDE for the latitude \e lat2; + * - \e outmask |= Rhumb::LONGITUDE for the latitude \e lon2; + * - \e outmask |= Rhumb::AREA for the area \e S12; + * - \e outmask |= Rhumb::ALL for all of the above; + * - \e outmask |= Rhumb::LONG_UNROLL to unroll \e lon2 instead of wrapping + * it into the range [−180°, 180°]. + * . + * With the Rhumb::LONG_UNROLL bit set, the quantity \e lon2 − + * \e lon1 indicates how many times and in what sense the rhumb line + * encircles the ellipsoid. + **********************************************************************/ + void GenDirect(real lat1, real lon1, real azi12, real s12, + unsigned outmask, real& lat2, real& lon2, real& S12) const; + + /** + * Solve the inverse rhumb problem returning also the area. + * + * @param[in] lat1 latitude of point 1 (degrees). + * @param[in] lon1 longitude of point 1 (degrees). + * @param[in] lat2 latitude of point 2 (degrees). + * @param[in] lon2 longitude of point 2 (degrees). + * @param[out] s12 rhumb distance between point 1 and point 2 (meters). + * @param[out] azi12 azimuth of the rhumb line (degrees). + * @param[out] S12 area under the rhumb line (meters2). + * + * The shortest rhumb line is found. If the end points are on opposite + * meridians, there are two shortest rhumb lines and the east-going one is + * chosen. \e lat1 and \e lat2 should be in the range [−90°, + * 90°]. The value of \e azi12 returned is in the range + * [−180°, 180°]. + * + * If either point is a pole, the cosine of its latitude is taken to be + * 1/ε2 (where ε is 2-52). 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 (meters2). + * + * 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 [−90°, 90°]. + * + * If point 1 is a pole, the cosine of its latitude is taken to be + * 1/ε2 (where ε is 2-52). 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 meters2. 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 (meters2). + * + * The value of \e lon2 returned is in the range [−180°, + * 180°]. + * + * If \e s12 is large enough that the rhumb line crosses a pole, the + * longitude of point 2 is indeterminate (a NaN is returned for \e lon2 and + * \e S12). + **********************************************************************/ + void Position(real s12, real& lat2, real& lon2, real& S12) const { + GenPosition(s12, LATITUDE | LONGITUDE | AREA, lat2, lon2, S12); + } + + /** + * Compute the position of point 2 which is a distance \e s12 (meters) from + * point 1. The area is not computed. + **********************************************************************/ + void Position(real s12, real& lat2, real& lon2) const { + real t; + GenPosition(s12, LATITUDE | LONGITUDE, lat2, lon2, t); + } + + /** + * The general position routine. RhumbLine::Position is defined in term so + * this function. + * + * @param[in] s12 distance between point 1 and point 2 (meters); it can be + * negative. + * @param[in] outmask a bitor'ed combination of RhumbLine::mask values + * specifying which of the following parameters should be set. + * @param[out] lat2 latitude of point 2 (degrees). + * @param[out] lon2 longitude of point 2 (degrees). + * @param[out] S12 area under the rhumb line (meters2). + * + * The RhumbLine::mask values possible for \e outmask are + * - \e outmask |= RhumbLine::LATITUDE for the latitude \e lat2; + * - \e outmask |= RhumbLine::LONGITUDE for the latitude \e lon2; + * - \e outmask |= RhumbLine::AREA for the area \e S12; + * - \e outmask |= RhumbLine::ALL for all of the above; + * - \e outmask |= RhumbLine::LONG_UNROLL to unroll \e lon2 instead of + * wrapping it into the range [−180°, 180°]. + * . + * With the RhumbLine::LONG_UNROLL bit set, the quantity \e lon2 − \e + * lon1 indicates how many times and in what sense the rhumb line encircles + * the ellipsoid. + * + * If \e s12 is large enough that the rhumb line crosses a pole, the + * longitude of point 2 is indeterminate (a NaN is returned for \e lon2 and + * \e S12). + **********************************************************************/ + void GenPosition(real s12, unsigned outmask, + real& lat2, real& lon2, real& S12) const; + + /** \name Inspector functions + **********************************************************************/ + ///@{ + + /** + * @return \e lat1 the latitude of point 1 (degrees). + **********************************************************************/ + Math::real Latitude() const { return _lat1; } + + /** + * @return \e lon1 the longitude of point 1 (degrees). + **********************************************************************/ + Math::real Longitude() const { return _lon1; } + + /** + * @return \e azi12 the azimuth of the rhumb line (degrees). + **********************************************************************/ + Math::real Azimuth() const { return _azi12; } + + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value inherited from the Rhumb object used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _rh.EquatorialRadius(); } + + /** + * @return \e f the flattening of the ellipsoid. This is the value + * inherited from the Rhumb object used in the constructor. + **********************************************************************/ + Math::real Flattening() const { return _rh.Flattening(); } + }; + +} // namespace GeographicLib + +#endif // GEOGRAPHICLIB_RHUMB_HPP diff --git a/external/include/GeographicLib/SphericalEngine.hpp b/external/include/GeographicLib/SphericalEngine.hpp new file mode 100644 index 0000000..8b8ad38 --- /dev/null +++ b/external/include/GeographicLib/SphericalEngine.hpp @@ -0,0 +1,384 @@ +/** + * \file SphericalEngine.hpp + * \brief Header for GeographicLib::SphericalEngine class + * + * Copyright (c) Charles Karney (2011-2019) 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 +#include +#include + +#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& 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::radix), + -3 * (std::numeric_limits::max_exponent < (1<<14) ? + std::numeric_limits::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::epsilon() * + sqrt(std::numeric_limits::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::const_iterator _Cnm; + std::vector::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 ≥ \e nmx ≥ \e mmx ≥ −1. + * @exception GeographicErr if \e C or \e S is not big enough to hold the + * coefficients. + * @exception std::bad_alloc if the memory for the square root table + * can't be allocated. + **********************************************************************/ + coeff(const std::vector& C, + const std::vector& 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 ≥ −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& C, + const std::vector& 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 ≥ + * \e M ≥ −1. + * @exception GeographicErr if there's an error reading the data. + * @exception std::bad_alloc if the memory for \e C or \e S can't be + * allocated. + * + * \e N and \e M are read as 4-byte ints. \e C and \e S are resized to + * accommodate all the coefficients (with the \e m = 0 coefficients for + * \e S excluded) and the data for these coefficients read as 8-byte + * doubles. The coefficients are stored in column major order. The + * bytes in the stream should use little-endian ordering. IEEE floating + * point is assumed for the coefficients. + **********************************************************************/ + static void readcoeffs(std::istream& stream, int& N, int& M, + std::vector& C, std::vector& S, + bool truncate = false); + }; + + /** + * Evaluate a spherical harmonic sum and its gradient. + * + * @tparam gradp should the gradient be calculated. + * @tparam norm the normalization for the associated Legendre polynomials. + * @tparam L the number of terms in the coefficients. + * @param[in] c an array of coeff objects. + * @param[in] f array of coefficient multipliers. f[0] should be 1. + * @param[in] x the \e x component of the cartesian position. + * @param[in] y the \e y component of the cartesian position. + * @param[in] z the \e z component of the cartesian position. + * @param[in] a the normalizing radius. + * @param[out] gradx the \e x component of the gradient. + * @param[out] grady the \e y component of the gradient. + * @param[out] gradz the \e z component of the gradient. + * @result the spherical harmonic sum. + * + * See the SphericalHarmonic class for the definition of the sum. The + * coefficients used by this function are, for example, c[0].Cv + f[1] * + * c[1].Cv + ... + f[L−1] * c[L−1].Cv. (Note that f[0] is \e + * not used.) The upper limits on the sum are determined by c[0].nmx() and + * c[0].mmx(); these limits apply to \e all the components of the + * coefficients. The parameters \e gradp, \e norm, and \e L are template + * parameters, to allow more optimization to be done at compile time. + * + * Clenshaw summation is used which permits the evaluation of the sum + * without the need to allocate temporary arrays. Thus this function never + * throws an exception. + **********************************************************************/ + template + 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(x2 + + * y2). + * @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(x2 + + * y2), \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 x/\e p and + * y/\e p. + **********************************************************************/ + template + 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 temp(0); + sqrttable().swap(temp); + } + }; + +} // namespace GeographicLib + +#if defined(_MSC_VER) +# pragma warning (pop) +#endif + +#endif // GEOGRAPHICLIB_SPHERICALENGINE_HPP diff --git a/external/include/GeographicLib/SphericalHarmonic.hpp b/external/include/GeographicLib/SphericalHarmonic.hpp new file mode 100644 index 0000000..38da9ba --- /dev/null +++ b/external/include/GeographicLib/SphericalHarmonic.hpp @@ -0,0 +1,356 @@ +/** + * \file SphericalHarmonic.hpp + * \brief Header for GeographicLib::SphericalHarmonic class + * + * Copyright (c) Charles Karney (2011-2019) 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 +#include +#include +#include + +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 + * - p2 = x2 + y2, + * - r2 = p2 + z2, + * - \e q = a/r, + * - θ = atan2(\e p, \e z) = the spherical \e colatitude, + * - λ = atan2(\e y, \e x) = the longitude. + * - Pnm(\e t) is the associated Legendre polynomial of + * degree \e n and order \e m. + * + * Two normalizations are supported for Pnm + * - 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 note on the summation of Chebyshev series, + * %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 unified approach to the Clenshaw summation and the recursive + * computation of very high degree and order normalised associated Legendre + * functions, J. Geodesy 76(5), 279--299 (2002). + * - C. C. Tscherning and K. Poder, + * + * Some geodetic applications of Clenshaw summation, + * 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 + * Pnmfull(\e z) + * = (−1)m + * sqrt(\e k (2\e n + 1) (\e n − \e m)! / (\e n + \e m)!) + * Pnm(\e z), where + * Pnm(\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 + * Pnmfull(cosθ) + * cos(mλ) and + * Pnmfull(cosθ) + * sin(mλ) over the sphere is 1. + * + * @hideinitializer + **********************************************************************/ + FULL = SphericalEngine::FULL, + /** + * Schmidt semi-normalized associated Legendre polynomials. + * + * These are defined by + * Pnmschmidt(\e z) + * = (−1)m + * sqrt(\e k (\e n − \e m)! / (\e n + \e m)!) + * Pnm(\e z), where + * Pnm(\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 + * Pnmschmidt(cosθ) + * cos(mλ) and + * Pnmschmidt(cosθ) + * sin(mλ) 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 Cnm. + * @param[in] S the coefficients Snm. + * @param[in] N the maximum degree and order of the sum + * @param[in] a the reference radius appearing in the definition of the + * sum. + * @param[in] norm the normalization for the associated Legendre + * polynomials, either SphericalHarmonic::FULL (the default) or + * SphericalHarmonic::SCHMIDT. + * @exception GeographicErr if \e N does not satisfy \e N ≥ −1. + * @exception GeographicErr if \e C or \e S is not big enough to hold the + * coefficients. + * + * The coefficients Cnm and + * Snm 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: + * C00, + * C10, + * C20, + * C30, + * C11, + * C21, + * C31, + * C22, + * C32, + * C33. + * In general the (\e n,\e m) element is at index \e m \e N − \e m + * (\e m − 1)/2 + \e n. The layout of \e S is the same except that + * the first column is omitted (since the \e m = 0 terms never contribute + * to the sum) and the 0th element is S11 + * + * The class stores pointers 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& C, + const std::vector& 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 Cnm. + * @param[in] S the coefficients Snm. + * @param[in] N the degree used to determine the layout of \e C and \e S. + * @param[in] nmx the maximum degree used in the sum. The sum over \e n is + * from 0 thru \e nmx. + * @param[in] mmx the maximum order used in the sum. The sum over \e m is + * from 0 thru min(\e n, \e mmx). + * @param[in] a the reference radius appearing in the definition of the + * sum. + * @param[in] norm the normalization for the associated Legendre + * polynomials, either SphericalHarmonic::FULL (the default) or + * SphericalHarmonic::SCHMIDT. + * @exception GeographicErr if \e N, \e nmx, and \e mmx do not satisfy + * \e N ≥ \e nmx ≥ \e mmx ≥ −1. + * @exception GeographicErr if \e C or \e S is not big enough to hold the + * coefficients. + * + * The class stores pointers 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& C, + const std::vector& 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 + (_c, f, x, y, z, _a, dummy, dummy, dummy); + break; + case SCHMIDT: + default: // To avoid compiler warnings + v = SphericalEngine::Value + (_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 + (_c, f, x, y, z, _a, gradx, grady, gradz); + break; + case SCHMIDT: + default: // To avoid compiler warnings + v = SphericalEngine::Value + (_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., ∑n = 0..N + * ∑m = 0..n becomes ∑m = + * 0..Nn = m..N. + * SphericalHarmonic::Circle performs the inner sum over degree \e n (which + * entails about N2 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(), + z = r * sin(phi), p = r * cos(phi); + for (int i = 0; i <= 100; ++i) { + real + lon = lon0 + i * dlon, + lam = lon * Math::degree(); + 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 N/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(), + 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 + (_c, f, p, z, _a) : + SphericalEngine::Circle + (_c, f, p, z, _a); + break; + case SCHMIDT: + default: // To avoid compiler warnings + return gradp ? + SphericalEngine::Circle + (_c, f, p, z, _a) : + SphericalEngine::Circle + (_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 diff --git a/external/include/GeographicLib/SphericalHarmonic1.hpp b/external/include/GeographicLib/SphericalHarmonic1.hpp new file mode 100644 index 0000000..8cedf28 --- /dev/null +++ b/external/include/GeographicLib/SphericalHarmonic1.hpp @@ -0,0 +1,283 @@ +/** + * \file SphericalHarmonic1.hpp + * \brief Header for GeographicLib::SphericalHarmonic1 class + * + * Copyright (c) Charles Karney (2011) 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 +#include +#include +#include + +namespace GeographicLib { + + /** + * \brief Spherical harmonic series with a correction to the coefficients + * + * This classes is similar to SphericalHarmonic, except that the coefficients + * Cnm are replaced by + * Cnm + \e tau C'nm (and + * similarly for Snm). + * + * 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 Cnm. + * @param[in] S the coefficients Snm. + * @param[in] N the maximum degree and order of the sum + * @param[in] C1 the coefficients C'nm. + * @param[in] S1 the coefficients S'nm. + * @param[in] N1 the maximum degree and order of the correction + * coefficients C'nm and + * S'nm. + * @param[in] a the reference radius appearing in the definition of the + * sum. + * @param[in] norm the normalization for the associated Legendre + * polynomials, either SphericalHarmonic1::FULL (the default) or + * SphericalHarmonic1::SCHMIDT. + * @exception GeographicErr if \e N and \e N1 do not satisfy \e N ≥ + * \e N1 ≥ −1. + * @exception GeographicErr if any of the vectors of coefficients is not + * large enough. + * + * See SphericalHarmonic for the way the coefficients should be stored. + * + * The class stores pointers 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& C, + const std::vector& S, + int N, + const std::vector& C1, + const std::vector& 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 Cnm. + * @param[in] S the coefficients Snm. + * @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 C'nm. + * @param[in] S1 the coefficients S'nm. + * @param[in] N1 the degree used to determine the layout of \e C' and \e + * S'. + * @param[in] nmx1 the maximum degree used for \e C' and \e S'. + * @param[in] mmx1 the maximum order used for \e C' and \e S'. + * @param[in] a the reference radius appearing in the definition of the + * sum. + * @param[in] norm the normalization for the associated Legendre + * polynomials, either SphericalHarmonic1::FULL (the default) or + * SphericalHarmonic1::SCHMIDT. + * @exception GeographicErr if the parameters do not satisfy \e N ≥ \e + * nmx ≥ \e mmx ≥ −1; \e N1 ≥ \e nmx1 ≥ \e mmx1 ≥ + * −1; \e N ≥ \e N1; \e nmx ≥ \e nmx1; \e mmx ≥ \e mmx1. + * @exception GeographicErr if any of the vectors of coefficients is not + * large enough. + * + * The class stores pointers 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& C, + const std::vector& S, + int N, int nmx, int mmx, + const std::vector& C1, + const std::vector& 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 + (_c, f, x, y, z, _a, dummy, dummy, dummy); + break; + case SCHMIDT: + default: // To avoid compiler warnings + v = SphericalEngine::Value + (_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 + (_c, f, x, y, z, _a, gradx, grady, gradz); + break; + case SCHMIDT: + default: // To avoid compiler warnings + v = SphericalEngine::Value + (_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., ∑n = 0..N + * ∑m = 0..n becomes ∑m = + * 0..Nn = m..N. + * SphericalHarmonic1::Circle performs the inner sum over degree \e n + * (which entails about N2 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 + (_c, f, p, z, _a) : + SphericalEngine::Circle + (_c, f, p, z, _a); + break; + case SCHMIDT: + default: // To avoid compiler warnings + return gradp ? + SphericalEngine::Circle + (_c, f, p, z, _a) : + SphericalEngine::Circle + (_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 diff --git a/external/include/GeographicLib/SphericalHarmonic2.hpp b/external/include/GeographicLib/SphericalHarmonic2.hpp new file mode 100644 index 0000000..a0de495 --- /dev/null +++ b/external/include/GeographicLib/SphericalHarmonic2.hpp @@ -0,0 +1,320 @@ +/** + * \file SphericalHarmonic2.hpp + * \brief Header for GeographicLib::SphericalHarmonic2 class + * + * Copyright (c) Charles Karney (2011-2012) 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 +#include +#include +#include + +namespace GeographicLib { + + /** + * \brief Spherical harmonic series with two corrections to the coefficients + * + * This classes is similar to SphericalHarmonic, except that the coefficients + * Cnm are replaced by + * Cnm + \e tau' C'nm + \e + * tau'' C''nm (and similarly for + * Snm). + * + * 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 Cnm. + * @param[in] S the coefficients Snm. + * @param[in] N the maximum degree and order of the sum + * @param[in] C1 the coefficients C'nm. + * @param[in] S1 the coefficients S'nm. + * @param[in] N1 the maximum degree and order of the first correction + * coefficients C'nm and + * S'nm. + * @param[in] C2 the coefficients C''nm. + * @param[in] S2 the coefficients S''nm. + * @param[in] N2 the maximum degree and order of the second correction + * coefficients C'nm and + * S'nm. + * @param[in] a the reference radius appearing in the definition of the + * sum. + * @param[in] norm the normalization for the associated Legendre + * polynomials, either SphericalHarmonic2::FULL (the default) or + * SphericalHarmonic2::SCHMIDT. + * @exception GeographicErr if \e N and \e N1 do not satisfy \e N ≥ + * \e N1 ≥ −1, and similarly for \e N2. + * @exception GeographicErr if any of the vectors of coefficients is not + * large enough. + * + * See SphericalHarmonic for the way the coefficients should be stored. \e + * N1 and \e N2 should satisfy \e N1 ≤ \e N and \e N2 ≤ \e N. + * + * The class stores pointers 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& C, + const std::vector& S, + int N, + const std::vector& C1, + const std::vector& S1, + int N1, + const std::vector& C2, + const std::vector& 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 Cnm. + * @param[in] S the coefficients Snm. + * @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 C'nm. + * @param[in] S1 the coefficients S'nm. + * @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 C''nm. + * @param[in] S2 the coefficients S''nm. + * @param[in] N2 the degree used to determine the layout of \e C'' and \e + * S''. + * @param[in] nmx2 the maximum degree used for \e C'' and \e S''. + * @param[in] mmx2 the maximum order used for \e C'' and \e S''. + * @param[in] a the reference radius appearing in the definition of the + * sum. + * @param[in] norm the normalization for the associated Legendre + * polynomials, either SphericalHarmonic2::FULL (the default) or + * SphericalHarmonic2::SCHMIDT. + * @exception GeographicErr if the parameters do not satisfy \e N ≥ \e + * nmx ≥ \e mmx ≥ −1; \e N1 ≥ \e nmx1 ≥ \e mmx1 ≥ + * −1; \e N ≥ \e N1; \e nmx ≥ \e nmx1; \e mmx ≥ \e mmx1; + * and similarly for \e N2, \e nmx2, and \e mmx2. + * @exception GeographicErr if any of the vectors of coefficients is not + * large enough. + * + * The class stores pointers 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& C, + const std::vector& S, + int N, int nmx, int mmx, + const std::vector& C1, + const std::vector& S1, + int N1, int nmx1, int mmx1, + const std::vector& C2, + const std::vector& 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 + (_c, f, x, y, z, _a, dummy, dummy, dummy); + break; + case SCHMIDT: + default: // To avoid compiler warnings + v = SphericalEngine::Value + (_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 + (_c, f, x, y, z, _a, gradx, grady, gradz); + break; + case SCHMIDT: + default: // To avoid compiler warnings + v = SphericalEngine::Value + (_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., ∑n = 0..N + * ∑m = 0..n becomes ∑m = + * 0..Nn = m..N.. + * SphericalHarmonic2::Circle performs the inner sum over degree \e n + * (which entails about N2 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 + (_c, f, p, z, _a) : + SphericalEngine::Circle + (_c, f, p, z, _a); + break; + case SCHMIDT: + default: // To avoid compiler warnings + return gradp ? + SphericalEngine::Circle + (_c, f, p, z, _a) : + SphericalEngine::Circle + (_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 diff --git a/external/include/GeographicLib/TransverseMercator.hpp b/external/include/GeographicLib/TransverseMercator.hpp new file mode 100644 index 0000000..2eed3dc --- /dev/null +++ b/external/include/GeographicLib/TransverseMercator.hpp @@ -0,0 +1,206 @@ +/** + * \file TransverseMercator.hpp + * \brief Header for GeographicLib::TransverseMercator class + * + * Copyright (c) Charles Karney (2008-2020) 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 + +#if !defined(GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER) +/** + * The order of the series approximation used in TransverseMercator. + * GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER can be set to any integer in [4, 8]. + **********************************************************************/ +# define GEOGRAPHICLIB_TRANSVERSEMERCATOR_ORDER \ + (GEOGRAPHICLIB_PRECISION == 2 ? 6 : \ + (GEOGRAPHICLIB_PRECISION == 1 ? 4 : 8)) +#endif + +namespace GeographicLib { + + /** + * \brief Transverse Mercator projection + * + * This uses Krüger's method which evaluates the projection and its + * inverse in terms of a series. See + * - L. Krüger, + * Konforme + * Abbildung des Erdellipsoids in der Ebene (Conformal mapping of the + * ellipsoidal earth to the plane), Royal Prussian Geodetic Institute, New + * Series 52, 172 pp. (1912). + * - C. F. F. Karney, + * + * Transverse Mercator with an accuracy of a few nanometers, + * J. Geodesy 85(8), 475--485 (Aug. 2011); + * preprint + * arXiv:1002.1417. + * + * Krüger's method has been extended from 4th to 6th order. The maximum + * error is 5 nm (5 nanometers), ground distance, for all positions within 35 + * degrees of the central meridian. The error in the convergence is 2 + * × 10−15" and the relative error in the scale + * is 6 × 10−12%%. See Sec. 4 of + * arXiv:1002.1417 for details. + * The speed penalty in going to 6th order is only about 1%. + * + * There's a singularity in the projection at φ = 0°, λ + * − λ0 = ±(1 − \e e)90° (≈ + * ±82.6° for the WGS84 ellipsoid), where \e e is the + * eccentricity. Beyond this point, the series ceases to converge and the + * results from this method will be garbage. To be on the safe side, don't + * use this method if the angular distance from the central meridian exceeds + * (1 − 2e)90° (≈ 75° for the WGS84 ellipsoid) + * + * TransverseMercatorExact is an alternative implementation of the projection + * using exact formulas which yield accurate (to 8 nm) results over the + * entire ellipsoid. + * + * 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 ( + * EPSG:7405) 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 + * + * TransverseMercatorProj 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 − \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 + * [−90°, 90°]. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y, real& gamma, real& k) const; + + /** + * Reverse projection, from transverse Mercator to geographic. + * + * @param[in] lon0 central meridian of the projection (degrees). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * No false easting or northing is added. The value of \e lon returned is + * in the range [−180°, 180°]. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon, real& gamma, real& k) const; + + /** + * TransverseMercator::Forward without returning the convergence and scale. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y) const { + real gamma, k; + Forward(lon0, lat, lon, x, y, gamma, k); + } + + /** + * TransverseMercator::Reverse without returning the convergence and scale. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon) const { + real gamma, k; + Reverse(lon0, x, y, lat, lon, gamma, k); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _a; } + + /** + * @return \e f the flattening of the ellipsoid. This is the value used in + * the constructor. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * @return \e k0 central scale for the projection. This is the value of \e + * k0 used in the constructor and is the scale on the central meridian. + **********************************************************************/ + Math::real CentralScale() const { return _k0; } + + /** + * \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 diff --git a/external/include/GeographicLib/TransverseMercatorExact.hpp b/external/include/GeographicLib/TransverseMercatorExact.hpp new file mode 100644 index 0000000..d81f0c8 --- /dev/null +++ b/external/include/GeographicLib/TransverseMercatorExact.hpp @@ -0,0 +1,264 @@ +/** + * \file TransverseMercatorExact.hpp + * \brief Header for GeographicLib::TransverseMercatorExact class + * + * Copyright (c) Charles Karney (2008-2020) 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 +#include + +namespace GeographicLib { + + /** + * \brief An exact implementation of the transverse Mercator projection + * + * Implementation of the Transverse Mercator Projection given in + * - L. P. Lee, + * Conformal + * Projections Based On Jacobian Elliptic Functions, 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, + * + * Transverse Mercator with an accuracy of a few nanometers, + * J. Geodesy 85(8), 475--485 (Aug. 2011); + * preprint + * arXiv:1002.1417. + * + * Lee gives the correct results for forward and reverse transformations + * subject to the branch cut rules (see the description of the \e extendp + * argument to the constructor). The maximum error is about 8 nm (8 + * nanometers), ground distance, for the forward and reverse transformations. + * The error in the convergence is 2 × 10−15", + * the relative error in the scale is 7 × 10−12%%. + * See Sec. 3 of + * arXiv:1002.1417 for details. + * The method is "exact" in the sense that the errors are close to the + * round-off limit and that no changes are needed in the algorithms for them + * to be used with reals of a higher precision. Thus the errors using long + * double (with a 64-bit fraction) are about 2000 times smaller than using + * double (with a 53-bit fraction). + * + * This algorithm is about 4.5 times slower than the 6th-order Krüger + * method, TransverseMercator, taking about 11 us for a combined forward and + * reverse projection on a 2.66 GHz Intel machine (g++, version 4.3.0, -O3). + * + * The ellipsoid parameters and the central scale are set in the constructor. + * The central meridian (which is a trivial shift of the longitude) is + * specified as the \e lon0 argument of the TransverseMercatorExact::Forward + * and TransverseMercatorExact::Reverse functions. The latitude of origin is + * taken to be the equator. See the documentation on TransverseMercator for + * how to include a false easting, false northing, or a latitude of origin. + * + * See tm-grid.kmz, 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 + * + * TransverseMercatorProj 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 − \e lon0 = 90 (1 − \e e) or (for + * TransverseMercatorExact::UTM) x = 18381 km, y = 0m. The \e extendp + * argument governs where the branch cut is placed. With \e extendp = + * false, the "standard" convention is followed, namely the cut is placed + * along \e x > 18381 km, \e y = 0m. Forward can be called with any \e lat + * and \e lon then produces the transformation shown in Lee, Fig 46. + * Reverse analytically continues this in the ± \e x direction. As + * a consequence, Reverse may map multiple points to the same geographic + * location; for example, for TransverseMercatorExact::UTM, \e x = + * 22051449.037349 m, \e y = −7131237.022729 m and \e x = + * 29735142.378357 m, \e y = 4235043.607933 m both map to \e lat = + * −2°, \e lon = 88°. + * + * With \e extendp = true, the branch cut is moved to the lower left + * quadrant. The various symmetries of the transverse Mercator projection + * can be used to explore the projection on any sheet. In this mode the + * domains of \e lat, \e lon, \e x, and \e y are restricted to + * - the union of + * - \e lat in [0, 90] and \e lon − \e lon0 in [0, 90] + * - \e lat in (-90, 0] and \e lon − \e lon0 in [90 (1 − \e + e), 90] + * - the union of + * - x/(\e k0 \e a) in [0, ∞) and + * y/(\e k0 \e a) in [0, E(e2)] + * - x/(\e k0 \e a) in [K(1 − e2) − + * E(1 − e2), ∞) and y/(\e k0 \e + * a) in (−∞, 0] + * . + * See Sec. 5 of + * arXiv:1002.1417 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::epsilon(). However, TransverseMercator treats the + * sphere exactly. + **********************************************************************/ + TransverseMercatorExact(real a, real f, real k0, bool extendp = false); + + /** + * Forward projection, from geographic to transverse Mercator. + * + * @param[in] lon0 central meridian of the projection (degrees). + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * No false easting or northing is added. \e lat should be in the range + * [−90°, 90°]. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y, real& gamma, real& k) const; + + /** + * Reverse projection, from transverse Mercator to geographic. + * + * @param[in] lon0 central meridian of the projection (degrees). + * @param[in] x easting of point (meters). + * @param[in] y northing of point (meters). + * @param[out] lat latitude of point (degrees). + * @param[out] lon longitude of point (degrees). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * + * No false easting or northing is added. The value of \e lon returned is + * in the range [−180°, 180°]. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon, real& gamma, real& k) const; + + /** + * TransverseMercatorExact::Forward without returning the convergence and + * scale. + **********************************************************************/ + void Forward(real lon0, real lat, real lon, + real& x, real& y) const { + real gamma, k; + Forward(lon0, lat, lon, x, y, gamma, k); + } + + /** + * TransverseMercatorExact::Reverse without returning the convergence and + * scale. + **********************************************************************/ + void Reverse(real lon0, real x, real y, + real& lat, real& lon) const { + real gamma, k; + Reverse(lon0, x, y, lat, lon, gamma, k); + } + + /** \name Inspector functions + **********************************************************************/ + ///@{ + /** + * @return \e a the equatorial radius of the ellipsoid (meters). This is + * the value used in the constructor. + **********************************************************************/ + Math::real EquatorialRadius() const { return _a; } + + /** + * @return \e f the flattening of the ellipsoid. This is the value used in + * the constructor. + **********************************************************************/ + Math::real Flattening() const { return _f; } + + /** + * @return \e k0 central scale for the projection. This is the value of \e + * k0 used in the constructor and is the scale on the central meridian. + **********************************************************************/ + Math::real CentralScale() const { return _k0; } + + /** + * \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 diff --git a/external/include/GeographicLib/UTMUPS.hpp b/external/include/GeographicLib/UTMUPS.hpp new file mode 100644 index 0000000..e949ee9 --- /dev/null +++ b/external/include/GeographicLib/UTMUPS.hpp @@ -0,0 +1,428 @@ +/** + * \file UTMUPS.hpp + * \brief Header for GeographicLib::UTMUPS class + * + * Copyright (c) Charles Karney (2008-2020) 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 + +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, + * + * The Universal Grids: Universal Transverse Mercator (UTM) and Universal + * Polar Stereographic (UPS), 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 + * - + * The Universal Grids and the Transverse Mercator and Polar Stereographic + * Map Projections, 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 NGA software package + * geotrans + * 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 −80° and 84° + * into 60 zones numbered 1 thru 60. Zone assign zone number 0 to the UPS + * regions, covering the two poles. Within UTMUPS, 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°, 48°). The rules include the + * Norway and Svalbard exceptions. + **********************************************************************/ + UTM = -2, + /** + * Apply the standard rules for zone assignment to give a zone number in + * [0, 60]. If the latitude is not in [−80°, 84°), then + * use UTMUPS::UPS = 0, otherwise apply the rules for UTMUPS::UTM. The + * tests on latitudes and longitudes are all closed on the lower end open + * on the upper. Thus for UTM zone 38, latitude is in [−80°, + * 84°) and longitude is in [42°, 48°). + **********************************************************************/ + STANDARD = -1, + /** + * The largest pseudo-zone number. + **********************************************************************/ + MAXPSEUDOZONE = -1, + /** + * The smallest physical zone number. + **********************************************************************/ + MINZONE = 0, + /** + * The zone number used for UPS + **********************************************************************/ + UPS = 0, + /** + * The smallest UTM zone number. + **********************************************************************/ + MINUTMZONE = 1, + /** + * The largest UTM zone number. + **********************************************************************/ + MAXUTMZONE = 60, + /** + * The largest physical zone number. + **********************************************************************/ + MAXZONE = 60, + }; + + /** + * The standard zone. + * + * @param[in] lat latitude (degrees). + * @param[in] lon longitude (degrees). + * @param[in] setzone zone override (optional). If omitted, use the + * standard rules for picking the zone. If \e setzone is given then use + * that zone if it is non-negative, otherwise apply the rules given in + * UTMUPS::zonespec. + * @exception GeographicErr if \e setzone is outside the range + * [UTMUPS::MINPSEUDOZONE, UTMUPS::MAXZONE] = [−4, 60]. + * + * This is exact. + **********************************************************************/ + static int StandardZone(real lat, real lon, int setzone = STANDARD); + + /** + * Forward projection, from geographic to UTM/UPS. + * + * @param[in] lat latitude of point (degrees). + * @param[in] lon longitude of point (degrees). + * @param[out] zone the UTM zone (zero means UPS). + * @param[out] northp hemisphere (true means north, false means south). + * @param[out] x easting of point (meters). + * @param[out] y northing of point (meters). + * @param[out] gamma meridian convergence at point (degrees). + * @param[out] k scale of projection at point. + * @param[in] setzone zone override (optional). + * @param[in] mgrslimits if true enforce the stricter MGRS limits on the + * coordinates (default = false). + * @exception GeographicErr if \e lat is not in [−90°, + * 90°]. + * @exception GeographicErr if the resulting \e x or \e y is out of allowed + * range (see Reverse); in this case, these arguments are unchanged. + * + * If \e setzone is omitted, use the standard rules for picking the zone. + * If \e setzone is given then use that zone if it is 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 ≥ 0. + * @exception GeographicErr if \e zonein is out of range (see below). + * @exception GeographicErr if \e zoneout is out of range (see below). + * @exception GeographicErr if \e xin or \e yin fall outside their allowed + * ranges (see UTMUPS::Reverse). + * @exception GeographicErr if \e xout or \e yout fall outside their + * allowed ranges (see UTMUPS::Reverse). + * + * \e zonein must be in the range [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0, + * 60] with \e zonein = UTMUPS::UPS, 0, indicating UPS. \e zonein may + * also be UTMUPS::INVALID. + * + * \e zoneout must be in the range [UTMUPS::MINPSEUDOZONE, UTMUPS::MAXZONE] + * = [-4, 60]. If \e zoneout < UTMUPS::MINZONE then the rules give in + * the documentation of UTMUPS::zonespec are applied, and \e zone is set to + * the actual zone used for output. + * + * (\e xout, \e yout) can overlap with (\e xin, \e yin). + **********************************************************************/ + static void Transfer(int zonein, bool northpin, real xin, real yin, + int zoneout, bool northpout, real& xout, real& yout, + int& zone); + + /** + * Decode a UTM/UPS zone string. + * + * @param[in] zonestr string representation of zone and hemisphere. + * @param[out] zone the UTM zone (zero means UPS). + * @param[out] northp hemisphere (true means north, false means south). + * @exception GeographicErr if \e zonestr is malformed. + * + * For UTM, \e zonestr has the form of a zone number in the range + * [UTMUPS::MINUTMZONE, UTMUPS::MAXUTMZONE] = [1, 60] followed by a + * hemisphere letter, n or s (or "north" or "south" spelled out). For UPS, + * it consists just of the hemisphere letter (or the spelled out + * hemisphere). The returned value of \e zone is UTMUPS::UPS = 0 for UPS. + * Note well that "38s" indicates the southern hemisphere of zone 38 and + * not latitude band S, 32° ≤ \e lat < 40°. n, 01s, 2n, 38s, + * south, 3north are legal. 0n, 001s, +3n, 61n, 38P are illegal. INV is a + * special value for which the returned value of \e is UTMUPS::INVALID. + **********************************************************************/ + static void DecodeZone(const std::string& zonestr, + int& zone, bool& northp); + + /** + * Encode a UTM/UPS zone string. + * + * @param[in] zone the UTM zone (zero means UPS). + * @param[in] northp hemisphere (true means north, false means south). + * @param[in] abbrev if true (the default) use abbreviated (n/s) notation + * for hemisphere; otherwise spell out the hemisphere (north/south) + * @exception GeographicErr if \e zone is out of range (see below). + * @exception std::bad_alloc if memoy for the string can't be allocated. + * @return string representation of zone and hemisphere. + * + * \e zone must be in the range [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0, + * 60] with \e zone = UTMUPS::UPS, 0, indicating UPS (but the resulting + * string does not contain "0"). \e zone may also be UTMUPS::INVALID, in + * which case the returned string is "inv". This reverses + * UTMUPS::DecodeZone. + **********************************************************************/ + static std::string EncodeZone(int zone, bool northp, bool abbrev = true); + + /** + * Decode EPSG. + * + * @param[in] epsg the EPSG code. + * @param[out] zone the UTM zone (zero means UPS). + * @param[out] northp hemisphere (true means north, false means south). + * + * EPSG (European Petroleum Survery Group) codes are a way to refer to many + * different projections. DecodeEPSG decodes those referring to UTM or UPS + * projections for the WGS84 ellipsoid. If the code does not refer to one + * of these projections, \e zone is set to UTMUPS::INVALID. See + * https://www.spatialreference.org/ref/epsg/ + **********************************************************************/ + static void DecodeEPSG(int epsg, int& zone, bool& northp); + + /** + * Encode zone as EPSG. + * + * @param[in] zone the UTM zone (zero means UPS). + * @param[in] northp hemisphere (true means north, false means south). + * @return EPSG code (or -1 if \e zone is not in the range + * [UTMUPS::MINZONE, UTMUPS::MAXZONE] = [0, 60]) + * + * Convert \e zone and \e northp to the corresponding EPSG (European + * Petroleum Survery Group) codes + **********************************************************************/ + static int EncodeEPSG(int zone, bool northp); + + /** + * @return shift (meters) necessary to align north and south halves of a + * UTM zone (107). + **********************************************************************/ + 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 diff --git a/external/include/GeographicLib/Utility.hpp b/external/include/GeographicLib/Utility.hpp new file mode 100644 index 0000000..21322bd --- /dev/null +++ b/external/include/GeographicLib/Utility.hpp @@ -0,0 +1,733 @@ +/** + * \file Utility.hpp + * \brief Header for GeographicLib::Utility class + * + * Copyright (c) Charles Karney (2011-2020) 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 +#include +#include +#include +#include +#include +#include + +#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 March–December, + // January, February as 0–9, 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(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(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(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(s.substr(p1, p2 - p1)); + if (++p2 == s.size()) + throw GeographicErr("Empty day field in date " + s); + d1 = val(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 static T fractionalyear(const std::string& s) { + try { + return val(s); + } + catch (const std::exception&) {} + int y, m, d; + date(s, y, m, d); + int t = day(y, m, d, true); + return T(y) + T(t - day(y)) / T(day(y + 1) - day(y)); + } + + /** + * Convert a object of type T to a string. + * + * @tparam T the type of the argument. + * @param[in] x the value to be converted. + * @param[in] p the precision used (default −1). + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return the string representation. + * + * If \e p ≥ 0, then the number fixed format is used with p bits of + * precision. With p < 0, there is no manipulation of the format. + **********************************************************************/ + template 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 −1). + * @exception std::bad_alloc if memory for the string can't be allocated. + * @return the string representation. + * + * If \e p ≥ 0, then the number fixed format is used with p bits of + * precision. With p < 0, there is no manipulation of the format. This is + * an overload of str 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 static T val(const std::string& s) { + // If T is bool, then the specialization val() 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::is_integer ? 0 : nummatch(t); + if (x == 0) + throw GeographicErr(errmsg); + return x; + } + /** + * \deprecated An old name for val(s). + **********************************************************************/ + template + GEOGRAPHICLIB_DEPRECATED("Use Utility::val(s)") + static T num(const std::string& s) { + return val(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 (±∞, nan) or 0 if none is + * found. + * + * White space is not allowed at the beginning or end of \e s. + **********************************************************************/ + template 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(); + else if (t == "INF" || t == "1.#INF") + return sign * Math::infinity(); + 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 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(s) : + // delim in [1, size() - 2] + val(s.substr(0, delim)) / val(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 + * −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 + * −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 + static void readarray(std::istream& str, IntT array[], size_t num) { +#if GEOGRAPHICLIB_PRECISION < 4 + if (sizeof(IntT) == sizeof(ExtT) && + std::numeric_limits::is_integer == + std::numeric_limits::is_integer) + { + // Data is compatible (aside from the issue of endian-ness). + str.read(reinterpret_cast(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(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(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(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 + static void readarray(std::istream& str, std::vector& array) { + if (array.size() > 0) + readarray(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 + static void writearray(std::ostream& str, const IntT array[], size_t num) + { +#if GEOGRAPHICLIB_PRECISION < 4 + if (sizeof(IntT) == sizeof(ExtT) && + std::numeric_limits::is_integer == + std::numeric_limits::is_integer && + bigendp == Math::bigendian) + { + // Data is compatible (including endian-ness). + str.write(reinterpret_cast(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(array[i++])); + str.write(reinterpret_cast(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 + static void writearray(std::ostream& str, std::vector& array) { + if (array.size() > 0) + writearray(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() for strings. + **********************************************************************/ + template<> inline std::string Utility::val(const std::string& s) + { return trim(s); } + + /** + * The specialization of Utility::val() for bools. + **********************************************************************/ + template<> inline bool Utility::val(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 diff --git a/external/lib/Geographic.lib b/external/lib/Geographic.lib new file mode 100644 index 0000000..3a0599b Binary files /dev/null and b/external/lib/Geographic.lib differ diff --git a/external/lib/Geographic_d.lib b/external/lib/Geographic_d.lib new file mode 100644 index 0000000..a01f691 Binary files /dev/null and b/external/lib/Geographic_d.lib differ