TransverseMercatorExact.hpp
Go to the documentation of this file.
00001 /**
00002  * \file TransverseMercatorExact.hpp
00003  * \brief Header for GeographicLib::TransverseMercatorExact class
00004  *
00005  * Copyright (c) Charles Karney (2008) <charles@karney.com>
00006  * http://charles.karney.info/geographic
00007  * and licensed under the LGPL.
00008  **********************************************************************/
00009 
00010 #if !defined(TRANSVERSEMERCATOREXACT_HPP)
00011 #define TRANSVERSEMERCATOREXACT_HPP "$Id: TransverseMercatorExact.hpp 6559 2009-02-28 16:49:53Z ckarney $"
00012 
00013 #include <cmath>
00014 #include "GeographicLib/EllipticFunction.hpp"
00015 
00016 namespace GeographicLib {
00017 
00018   /**
00019    * \brief An exact implementation of the Transverse Mercator Projection
00020    *
00021    * Implementation of the Transverse Mercator Projection given in
00022    *  - L. P. Lee,
00023    *    <a href="http://dx.doi.org/10.3138/X687-1574-4325-WM62"> Conformal
00024    *    Projections Based On Jacobian Elliptic Functions</a>, Part V of
00025    *    Conformal Projections Based on Elliptic Functions,
00026    *    (B. V. Gutsell, Toronto, 1976), 128pp.,
00027    *    ISBN: 0919870163
00028    *    (also appeared as:
00029    *    Monograph 16, Suppl. No. 1 to Canadian Cartographer, Vol 13).
00030    *
00031    * This method gives the correct results for forward and reverse
00032    * transformations subject to the branch cut rules (see the description of
00033    * the \e extendp argument to the constructor)..  The maximum error is about
00034    * 8 nm (ground distance) for the forward and reverse transformations.  The
00035    * error in the convergence is 2e-15", the relative error in the scale is
00036    * 7e-12%%.  (See \ref tmerrors for the weasel words.)  The method is "exact"
00037    * in the sense that the errors are close to the round-off limit and that no
00038    * changes are needed in the algorithms for them to be used with reals of a
00039    * higher precision.  Thus the errors using long double (with a 64-bit
00040    * fraction) are about 2000 times smaller than using double (with a 53-bit
00041    * fraction).
00042    *
00043    * This algorithm is about 4.5 times slower than the 6th-order Kr&uuml;ger
00044    * method, GeographicLib::TransverseMercator, taking about 11 us for a
00045    * combined forward and reverse projection on a 2.6 GHz Intel machine (g++,
00046    * version 4.3.0, -O3).
00047    *
00048    * See TransverseMercatorExact.cpp for more information on the
00049    * implementation.
00050    *
00051    * See \ref transversemercator for a discussion of this projection.
00052    **********************************************************************/
00053 
00054   class TransverseMercatorExact {
00055   private:
00056     static const double tol, tol1, tol2, taytol, ahypover;
00057     static const int numit = 10;
00058     const double _a, _f, _k0, _mu, _mv, _e, _ep2;
00059     const bool _extendp;
00060     const EllipticFunction _Eu, _Ev;
00061     static inline double sq(double x) throw() { return x * x; }
00062 #if defined(_MSC_VER)
00063     static inline double hypot(double x, double y) throw()
00064     { return _hypot(x, y); }
00065     // These have poor relative accuracy near x = 0.  However, for mapping
00066     // applications, we only need good absolute accuracy.
00067     // For small arguments we would write
00068     //
00069     // asinh(x) = asin(x) -x^3/3-5*x^7/56-63*x^11/1408-143*x^15/5120 ...
00070     // atanh(x) = atan(x) +2*x^3/3+2*x^7/7+2*x^11/11+2*x^15/15
00071     //
00072     // The accuracy of asinh is also bad for large negative arguments.  This is
00073     // easy to fix in the definition of asinh.  Instead we call these functions
00074     // with positive arguments and enforce the correct parity separately.
00075     static inline double asinh(double x) throw() {
00076       return std::log(x + std::sqrt(1 + sq(x)));
00077     }
00078     static inline double atanh(double x) throw() {
00079       return std::log((1 + x)/(1 - x))/2;
00080     }
00081 #else
00082     static inline double hypot(double x, double y) throw()
00083     { return ::hypot(x, y); }
00084     static inline double asinh(double x) throw() { return ::asinh(x); }
00085     static inline double atanh(double x) throw() { return ::atanh(x); }
00086 #endif
00087     double psi(double phi) const throw();
00088     double psiinv(double psi) const throw();
00089 
00090     void zeta(double u, double snu, double cnu, double dnu,
00091               double v, double snv, double cnv, double dnv,
00092               double& psi, double& lam) const throw();
00093 
00094     void dwdzeta(double u, double snu, double cnu, double dnu,
00095                  double v, double snv, double cnv, double dnv,
00096                  double& du, double& dv) const throw();
00097 
00098     bool zetainv0(double psi, double lam, double& u, double& v) const throw();
00099     void zetainv(double psi, double lam, double& u, double& v) const throw();
00100 
00101     void sigma(double u, double snu, double cnu, double dnu,
00102                double v, double snv, double cnv, double dnv,
00103                double& xi, double& eta) const throw();
00104 
00105     void dwdsigma(double u, double snu, double cnu, double dnu,
00106                   double v, double snv, double cnv, double dnv,
00107                   double& du, double& dv) const throw();
00108 
00109     bool sigmainv0(double xi, double eta, double& u, double& v) const throw();
00110     void sigmainv(double xi, double eta, double& u, double& v) const throw();
00111 
00112     void Scale(double phi, double lam,
00113                double snu, double cnu, double dnu,
00114                double snv, double cnv, double dnv,
00115                double& gamma, double& k) const throw();
00116 
00117   public:
00118 
00119     /**
00120      * Constructor for a ellipsoid radius \e a (meters), inverse flattening \e
00121      * invf, and central scale factor \e k0.  The transverse Mercator
00122      * projection has a branch point singularity at \e lat = 0 and \e lon - \e
00123      * lon0 = 90 (1 - \e e) or (for TransverseMercatorExact::UTM) x = 18381 km
00124      * , y = 0m.  The \e extendp argument governs where the branch cut is
00125      * placed.  With \e extendp = false, the "standard" convention is followed,
00126      * namely the cut is placed along x > 18381 km, y = 0m.  Forward can be
00127      * called with any \e lat and \e lon then produces the transformation shown
00128      * in Lee, Fig 46.  Reverse analytically continues this in the +/- \e x
00129      * direction.  As a consequence, Reverse may map multiple points to the
00130      * same geographic location; for example, for TransverseMercatorExact::UTM,
00131      * \e x = 22051449.037349 m, \e y = -7131237.022729 m and \e x =
00132      * 29735142.378357 m, \e y = 4235043.607933 m both map to \e lat = -2 deg,
00133      * \e lon = 88 deg.
00134      *
00135      * With \e extendp = true, the branch cut is moved to the lower left
00136      * quadrant.  The various symmetries of the transverse Mercator projection
00137      * can be used to explore the projection on any sheet.  In this mode the
00138      * domains of \e lat, \e lon, \e x, and \e y are restricted to
00139      * - the union of
00140      *   - \e lat in [0, 90] and \e lon - \e lon0 in [0, 90]
00141      *   - \e lat in (-90, 0] and \e lon - \e lon0 in [90 (1 - \e e), 90]
00142      * - the union of
00143      *   - \e x/(\e k0 \e a) in [0, inf) and
00144      *     \e y/(\e k0 \e a) in [0, E(\e e^2)]
00145      *   - \e x/(\e k0 \e a) in [K(1 - \e e^2) - E(1 - \e e^2), inf) and
00146      *     \e y/(\e k0 \e a) in (-inf, 0]
00147      * .
00148      * See \ref extend for a full discussion of the treatment of the branch
00149      * cut.
00150      *
00151      * The method will work for all ellipsoids used in terrestial geodesy.  The
00152      * method cannot be applied directly to the case of a sphere (\e invf =
00153      * inf) because some the constants characterizing this method diverge in
00154      * that limit.  However, GeographicLib::TransverseMercator treats the
00155      * sphere exactly.
00156      **********************************************************************/
00157     TransverseMercatorExact(double a, double invf, double k0,
00158                             bool extendp = false) throw();
00159 
00160     /**
00161      * Convert from latitude \e lat (degrees) and longitude \e lon (degrees) to
00162      * transverse Mercator easting \e x (meters) and northing \e y (meters).
00163      * The central meridian of the transformation is \e lon0 (degrees).  Also
00164      * return the meridian convergence \e gamma (degrees) and the scale \e k.
00165      * No false easting or northing is added.  \e lat should be in the range
00166      * [-90, 90]; \e lon and \e lon0 should be in the range [-180, 360].
00167      **********************************************************************/
00168     void Forward(double lon0, double lat, double lon,
00169                  double& x, double& y,
00170                  double& gamma, double& k) const throw();
00171 
00172     /**
00173      * Convert from transverse Mercator easting \e x (meters) and northing \e y
00174      * (meters) to latitude \e lat (degrees) and longitude \e lon (degrees) .
00175      * The central meridian of the transformation is \e lon0 (degrees).  Also
00176      * return the meridian convergence \e gamma (degrees) and the scale \e k.
00177      * No false easting or northing is added.  The value of \e lon returned is
00178      * in the range [-180, 180).
00179      **********************************************************************/
00180     void Reverse(double lon0, double x, double y,
00181                  double& lat, double& lon,
00182                  double& gamma, double& k) const throw();
00183 
00184     /**
00185      * A global instantiation of TransverseMercatorExact with the WGS84
00186      * ellipsoid and the UTM scale factor.  However, unlike UTM, no false
00187      * easting or northing is added.
00188      **********************************************************************/
00189     const static TransverseMercatorExact UTM;
00190   };
00191 
00192 } // namespace GeographicLib
00193 
00194 #endif