00001 /** 00002 * \file TransverseMercator.hpp 00003 * \brief Header for GeographicLib::TransverseMercator class 00004 * 00005 * Copyright (c) Charles Karney (2008) <charles@karney.com> 00006 * and licensed under the LGPL. 00007 **********************************************************************/ 00008 00009 #if !defined(TRANSVERSEMERCATOR_HPP) 00010 #define TRANSVERSEMERCATOR_HPP "$Id: TransverseMercator.hpp 6556 2009-02-26 15:44:49Z ckarney $" 00011 00012 #include <cmath> 00013 00014 #if !defined(TM_TX_MAXPOW) 00015 /** 00016 * The order of the series approximation used in 00017 * GeographicLib::TransverseMercator. TM_TX_MAXPOW can be set to any integer 00018 * in [4, 8]. 00019 **********************************************************************/ 00020 #define TM_TX_MAXPOW 6 00021 #endif 00022 00023 namespace GeographicLib { 00024 00025 /** 00026 * \brief Transverse Mercator Projection 00027 * 00028 * This uses Krüger's method which evaluates the projection and its 00029 * inverse in terms of a series. See 00030 * - L. Krüger, 00031 * <a href="http://dx.doi.org/10.2312/GFZ.b103-krueger28"> Konforme 00032 * Abbildung des Erdellipsoids in der Ebene</a> (Conformal mapping of the 00033 * ellipsoidal earth to the plane), Royal Prussian Geodetic Institute, New 00034 * Series 52, 172 pp. (1912). 00035 * 00036 * Krüger's method has been extended from 4th to 6th order. The maximum 00037 * errors is 5 nm (ground distance) for all positions within 35 degrees of 00038 * the central meridian. The error in the convergence is 2e-15" and the 00039 * relative error in the scale is 6e-12%%. (See \ref tmerrors for the weasel 00040 * words.) The speed penalty in going to 6th order is only about 1%. 00041 * GeographicLib::TransverseMercatorExact is an alternative implementation of 00042 * the projection using exact formulas which yield accurate (to 8 nm) 00043 * results over the entire ellipsoid. 00044 * 00045 * See TransverseMercator.cpp for more information on the implementation. 00046 * 00047 * See \ref transversemercator for a discussion of this projection. 00048 **********************************************************************/ 00049 00050 class TransverseMercator { 00051 private: 00052 static const int maxpow = 00053 TM_TX_MAXPOW > 8 ? 8 : (TM_TX_MAXPOW < 4 ? 4 : TM_TX_MAXPOW); 00054 static const double tol; 00055 static const int numit = 5; 00056 const double _a, _f, _k0, _e2, _e, _e2m, _n; 00057 double _a1, _b1, _h[maxpow], _hp[maxpow]; 00058 static inline double sq(double x) throw() { return x * x; } 00059 #if defined(_MSC_VER) 00060 static inline double hypot(double x, double y) throw() 00061 { return _hypot(x, y); } 00062 // These have poor relative accuracy near x = 0. However, for mapping 00063 // applications, we only need good absolute accuracy. 00064 // For small arguments we would write 00065 // 00066 // asinh(x) = asin(x) -x^3/3-5*x^7/56-63*x^11/1408-143*x^15/5120 ... 00067 // atanh(x) = atan(x) +2*x^3/3+2*x^7/7+2*x^11/11+2*x^15/15 00068 // 00069 // The accuracy of asinh is also bad for large negative arguments. This is 00070 // easy to fix in the definition of asinh. Instead we call these functions 00071 // with positive arguments and enforce the correct parity separately. 00072 static inline double asinh(double x) throw() { 00073 return std::log(x + std::sqrt(1 + sq(x))); 00074 } 00075 static inline double atanh(double x) throw() { 00076 return std::log((1 + x)/(1 - x))/2; 00077 } 00078 #else 00079 static inline double hypot(double x, double y) throw() 00080 { return ::hypot(x, y); } 00081 static inline double asinh(double x) throw() { return ::asinh(x); } 00082 static inline double atanh(double x) throw() { return ::atanh(x); } 00083 #endif 00084 public: 00085 00086 /** 00087 * Constructor for a ellipsoid radius \e a (meters), inverse flattening \e 00088 * invf, and central scale factor \e k0. Setting \e invf <= 0 implies \e 00089 * invf = inf or flattening = 0 (i.e., a sphere). 00090 **********************************************************************/ 00091 TransverseMercator(double a, double invf, double k0) throw(); 00092 00093 /** 00094 * Convert from latitude \e lat (degrees) and longitude \e lon (degrees) to 00095 * transverse Mercator easting \e x (meters) and northing \e y (meters). 00096 * The central meridian of the transformation is \e lon0 (degrees). Also 00097 * return the meridian convergence \e gamma (degrees) and the scale \e k. 00098 * No false easting or northing is added. \e lat should be in the range 00099 * [-90, 90]; \e lon and \e lon0 should be in the range [-180, 360]. 00100 **********************************************************************/ 00101 void Forward(double lon0, double lat, double lon, 00102 double& x, double& y, 00103 double& gamma, double& k) const throw(); 00104 00105 /** 00106 * Convert from transverse Mercator easting \e x (meters) and northing \e y 00107 * (meters) to latitude \e lat (degrees) and longitude \e lon (degrees) . 00108 * The central meridian of the transformation is \e lon0 (degrees). Also 00109 * return the meridian convergence \e gamma (degrees) and the scale \e k. 00110 * No false easting or northing is added. The value of \e lon returned is 00111 * in the range [-180, 180). 00112 **********************************************************************/ 00113 void Reverse(double lon0, double x, double y, 00114 double& lat, double& lon, 00115 double& gamma, double& k) const throw(); 00116 00117 /** 00118 * A global instantiation of TransverseMercator with the WGS84 ellipsoid 00119 * and the UTM scale factor. However, unlike UTM, no false easting or 00120 * northing is added. 00121 **********************************************************************/ 00122 const static TransverseMercator UTM; 00123 }; 00124 00125 } // namespace GeographicLib 00126 00127 #endif