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ü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