TransverseMercator.hpp
Go to the documentation of this file.
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&uuml;ger's method which evaluates the projection and its
00029    * inverse in terms of a series.  See
00030    *  - L. Kr&uuml;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&uuml;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