GeographicLib  1.21
LambertConformalConic.hpp
Go to the documentation of this file.
00001 /**
00002  * \file LambertConformalConic.hpp
00003  * \brief Header for GeographicLib::LambertConformalConic class
00004  *
00005  * Copyright (c) Charles Karney (2010, 2011) <charles@karney.com> and licensed
00006  * under the MIT/X11 License.  For more information, see
00007  * http://geographiclib.sourceforge.net/
00008  **********************************************************************/
00009 
00010 #if !defined(GEOGRAPHICLIB_LAMBERTCONFORMALCONIC_HPP)
00011 #define GEOGRAPHICLIB_LAMBERTCONFORMALCONIC_HPP \
00012   "$Id: 9aef04f77098543818681966f13ef97ea47dedb4 $"
00013 
00014 #include <algorithm>
00015 #include <GeographicLib/Constants.hpp>
00016 
00017 namespace GeographicLib {
00018 
00019   /**
00020    * \brief Lambert Conformal Conic Projection
00021    *
00022    * Implementation taken from the report,
00023    * - J. P. Snyder,
00024    *   <a href="http://pubs.er.usgs.gov/usgspubs/pp/pp1395"> Map Projections: A
00025    *   Working Manual</a>, USGS Professional Paper 1395 (1987),
00026    *   pp. 107&ndash;109.
00027    *
00028    * This is a implementation of the equations in Snyder except that divided
00029    * differences have been used to transform the expressions into ones which
00030    * may be evaluated accurately and that Newton's method is used to invert the
00031    * projection.  In this implementation, the projection correctly becomes the
00032    * Mercator projection or the polar stereographic projection when the
00033    * standard latitude is the equator or a pole.  The accuracy of the
00034    * projections is about 10 nm (10 nanometers).
00035    *
00036    * The ellipsoid parameters, the standard parallels, and the scale on the
00037    * standard parallels are set in the constructor.  Internally, the case with
00038    * two standard parallels is converted into a single standard parallel, the
00039    * latitude of tangency (also the latitude of minimum scale), with a scale
00040    * specified on this parallel.  This latitude is also used as the latitude of
00041    * origin which is returned by LambertConformalConic::OriginLatitude.  The
00042    * scale on the latitude of origin is given by
00043    * LambertConformalConic::CentralScale.  The case with two distinct standard
00044    * parallels where one is a pole is singular and is disallowed.  The central
00045    * meridian (which is a trivial shift of the longitude) is specified as the
00046    * \e lon0 argument of the LambertConformalConic::Forward and
00047    * LambertConformalConic::Reverse functions.  There is no provision in this
00048    * class for specifying a false easting or false northing or a different
00049    * latitude of origin.  However these are can be simply included by the
00050    * calling function.  For example the Pennsylvania South state coordinate
00051    * system (<a href="http://www.spatialreference.org/ref/epsg/3364/">
00052    * EPSG:3364</a>) is obtained by:
00053    * \include example-LambertConformalConic.cpp
00054    *
00055    * <a href="ConicProj.1.html">ConicProj</a> is a command-line utility
00056    * providing access to the functionality of LambertConformalConic and
00057    * AlbersEqualArea.
00058    **********************************************************************/
00059   class GEOGRAPHIC_EXPORT LambertConformalConic {
00060   private:
00061     typedef Math::real real;
00062     real _a, _f, _fm, _e2, _e, _e2m;
00063     real _sign, _n, _nc, _t0nm1, _scale, _lat0, _k0;
00064     real _scbet0, _tchi0, _scchi0, _psi0, _nrho0;
00065     static const real eps_;
00066     static const real epsx_;
00067     static const real tol_;
00068     static const real ahypover_;
00069     static const int numit_ = 5;
00070     static inline real hyp(real x) throw() { return Math::hypot(real(1), x); }
00071     // e * atanh(e * x) = log( ((1 + e*x)/(1 - e*x))^(e/2) ) if f >= 0
00072     // - sqrt(-e2) * atan( sqrt(-e2) * x)                    if f < 0
00073     inline real eatanhe(real x) const throw() {
00074       return _f >= 0 ? _e * Math::atanh(_e * x) : - _e * std::atan(_e * x);
00075     }
00076     // Divided differences
00077     // Definition: Df(x,y) = (f(x)-f(y))/(x-y)
00078     // See: W. M. Kahan and R. J. Fateman,
00079     // Symbolic computation of divided differences,
00080     // SIGSAM Bull. 33(3), 7-28 (1999)
00081     // http://dx.doi.org/10.1145/334714.334716
00082     // http://www.cs.berkeley.edu/~fateman/papers/divdiff.pdf
00083     //
00084     // General rules
00085     // h(x) = f(g(x)): Dh(x,y) = Df(g(x),g(y))*Dg(x,y)
00086     // h(x) = f(x)*g(x):
00087     //        Dh(x,y) = Df(x,y)*g(x) + Dg(x,y)*f(y)
00088     //                = Df(x,y)*g(y) + Dg(x,y)*f(x)
00089     //                = Df(x,y)*(g(x)+g(y))/2 + Dg(x,y)*(f(x)+f(y))/2
00090     //
00091     // hyp(x) = sqrt(1+x^2): Dhyp(x,y) = (x+y)/(hyp(x)+hyp(y))
00092     static inline real Dhyp(real x, real y, real hx, real hy) throw()
00093     // hx = hyp(x)
00094     { return (x + y) / (hx + hy); }
00095     // sn(x) = x/sqrt(1+x^2): Dsn(x,y) = (x+y)/((sn(x)+sn(y))*(1+x^2)*(1+y^2))
00096     static inline real Dsn(real x, real y, real sx, real sy) throw() {
00097       // sx = x/hyp(x)
00098       real t = x * y;
00099       return t > 0 ? (x + y) * Math::sq( (sx * sy)/t ) / (sx + sy) :
00100         (x - y != 0 ? (sx - sy) / (x - y) : 1);
00101     }
00102     // Dlog1p(x,y) = log1p((x-y)/(1+y)/(x-y)
00103     static inline real Dlog1p(real x, real y) throw() {
00104       real t = x - y; if (t < 0) { t = -t; y = x; }
00105       return t != 0 ? Math::log1p(t / (1 + y)) / t : 1 / (1 + x);
00106     }
00107     // Dexp(x,y) = exp((x+y)/2) * 2*sinh((x-y)/2)/(x-y)
00108     static inline real Dexp(real x, real y) throw() {
00109       real t = (x - y)/2;
00110       return (t != 0 ? sinh(t)/t : real(1)) * exp((x + y)/2);
00111     }
00112     // Dsinh(x,y) = 2*sinh((x-y)/2)/(x-y) * cosh((x+y)/2)
00113     //   cosh((x+y)/2) = (c+sinh(x)*sinh(y)/c)/2
00114     //   c=sqrt((1+cosh(x))*(1+cosh(y)))
00115     //   cosh((x+y)/2) = sqrt( (sinh(x)*sinh(y) + cosh(x)*cosh(y) + 1)/2 )
00116     static inline real Dsinh(real x, real y, real sx, real sy, real cx, real cy)
00117       // sx = sinh(x), cx = cosh(x)
00118       throw() {
00119       // real t = (x - y)/2, c = sqrt((1 + cx) * (1 + cy));
00120       // return (t != 0 ? sinh(t)/t : real(1)) * (c + sx * sy / c) /2;
00121       real t = (x - y)/2;
00122       return (t != 0 ? sinh(t)/t : real(1)) * sqrt((sx * sy + cx * cy + 1) /2);
00123     }
00124     // Dasinh(x,y) = asinh((x-y)*(x+y)/(x*sqrt(1+y^2)+y*sqrt(1+x^2)))/(x-y)
00125     //             = asinh((x*sqrt(1+y^2)-y*sqrt(1+x^2)))/(x-y)
00126     static inline real Dasinh(real x, real y, real hx, real hy) throw() {
00127       // hx = hyp(x)
00128       real t = x - y;
00129       return t != 0 ?
00130         Math::asinh(x*y > 0 ? t * (x+y) / (x*hy + y*hx) : x*hy - y*hx) / t :
00131         1/hx;
00132     }
00133     // Deatanhe(x,y) = eatanhe((x-y)/(1-e^2*x*y))/(x-y)
00134     inline real Deatanhe(real x, real y) const throw() {
00135       real t = x - y, d = 1 - _e2 * x * y;
00136       return t != 0 ? eatanhe(t / d) / t : _e2 / d;
00137     }
00138     void Init(real sphi1, real cphi1, real sphi2, real cphi2, real k1) throw();
00139   public:
00140 
00141     /**
00142      * Constructor with a single standard parallel.
00143      *
00144      * @param[in] a equatorial radius of ellipsoid (meters).
00145      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
00146      *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set flattening
00147      *   to 1/\e f.
00148      * @param[in] stdlat standard parallel (degrees), the circle of tangency.
00149      * @param[in] k0 scale on the standard parallel.
00150      *
00151      * An exception is thrown if \e a or \e k0 is not positive or if \e stdlat
00152      * is not in the range [-90, 90].
00153      **********************************************************************/
00154     LambertConformalConic(real a, real f, real stdlat, real k0);
00155 
00156     /**
00157      * Constructor with two standard parallels.
00158      *
00159      * @param[in] a equatorial radius of ellipsoid (meters).
00160      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
00161      *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set flattening
00162      *   to 1/\e f.
00163      * @param[in] stdlat1 first standard parallel (degrees).
00164      * @param[in] stdlat2 second standard parallel (degrees).
00165      * @param[in] k1 scale on the standard parallels.
00166      *
00167      * An exception is thrown if \e a or \e k0 is not positive or if \e stdlat1
00168      * or \e stdlat2 is not in the range [-90, 90].  In addition, if either \e
00169      * stdlat1 or \e stdlat2 is a pole, then an exception is thrown if \e
00170      * stdlat1 is not equal \e stdlat2.
00171      **********************************************************************/
00172     LambertConformalConic(real a, real f, real stdlat1, real stdlat2, real k1);
00173 
00174     /**
00175      * Constructor with two standard parallels specified by sines and cosines.
00176      *
00177      * @param[in] a equatorial radius of ellipsoid (meters).
00178      * @param[in] f flattening of ellipsoid.  Setting \e f = 0 gives a sphere.
00179      *   Negative \e f gives a prolate ellipsoid.  If \e f > 1, set flattening
00180      *   to 1/\e f.
00181      * @param[in] sinlat1 sine of first standard parallel.
00182      * @param[in] coslat1 cosine of first standard parallel.
00183      * @param[in] sinlat2 sine of second standard parallel.
00184      * @param[in] coslat2 cosine of second standard parallel.
00185      * @param[in] k1 scale on the standard parallels.
00186      *
00187      * This allows parallels close to the poles to be specified accurately.
00188      * This routine computes the latitude of origin and the scale at this
00189      * latitude.  In the case where \e lat1 and \e lat2 are different, the
00190      * errors in this routines are as follows: if \e dlat = abs(\e lat2 - \e
00191      * lat1) <= 160<sup>o</sup> and max(abs(\e lat1), abs(\e lat2)) <= 90 -
00192      * min(0.0002, 2.2e-6(180 - \e dlat), 6e-8 <i>dlat</i><sup>2</sup>) (in
00193      * degrees), then the error in the latitude of origin is less than
00194      * 4.5e-14<sup>o</sup> and the relative error in the scale is less than
00195      * 7e-15.
00196      **********************************************************************/
00197     LambertConformalConic(real a, real f,
00198                           real sinlat1, real coslat1,
00199                           real sinlat2, real coslat2,
00200                           real k1);
00201 
00202     /**
00203      * Set the scale for the projection.
00204      *
00205      * @param[in] lat (degrees).
00206      * @param[in] k scale at latitude \e lat (default 1).
00207      *
00208      * This allows a "latitude of true scale" to be specified.  An exception is
00209      * thrown if \e k is not positive or if \e stdlat is not in the range [-90,
00210      * 90]
00211      **********************************************************************/
00212     void SetScale(real lat, real k = real(1));
00213 
00214     /**
00215      * Forward projection, from geographic to Lambert conformal conic.
00216      *
00217      * @param[in] lon0 central meridian longitude (degrees).
00218      * @param[in] lat latitude of point (degrees).
00219      * @param[in] lon longitude of point (degrees).
00220      * @param[out] x easting of point (meters).
00221      * @param[out] y northing of point (meters).
00222      * @param[out] gamma meridian convergence at point (degrees).
00223      * @param[out] k scale of projection at point.
00224      *
00225      * The latitude origin is given by LambertConformalConic::LatitudeOrigin().
00226      * No false easting or northing is added and \e lat should be in the range
00227      * [-90, 90]; \e lon and \e lon0 should be in the range [-180, 360].  The
00228      * error in the projection is less than about 10 nm (10 nanometers), true
00229      * distance, and the errors in the meridian convergence and scale are
00230      * consistent with this.  The values of \e x and \e y returned for points
00231      * which project to infinity (i.e., one or both of the poles) will be large
00232      * but finite.
00233      **********************************************************************/
00234     void Forward(real lon0, real lat, real lon,
00235                  real& x, real& y, real& gamma, real& k) const throw();
00236 
00237     /**
00238      * Reverse projection, from Lambert conformal conic to geographic.
00239      *
00240      * @param[in] lon0 central meridian longitude (degrees).
00241      * @param[in] x easting of point (meters).
00242      * @param[in] y northing of point (meters).
00243      * @param[out] lat latitude of point (degrees).
00244      * @param[out] lon longitude of point (degrees).
00245      * @param[out] gamma meridian convergence at point (degrees).
00246      * @param[out] k scale of projection at point.
00247      *
00248      * The latitude origin is given by LambertConformalConic::LatitudeOrigin().
00249      * No false easting or northing is added.  \e lon0 should be in the range
00250      * [-180, 360].  The value of \e lon returned is in the range [-180, 180).
00251      * The error in the projection is less than about 10 nm (10 nanometers),
00252      * true distance, and the errors in the meridian convergence and scale are
00253      * consistent with this.
00254      **********************************************************************/
00255     void Reverse(real lon0, real x, real y,
00256                  real& lat, real& lon, real& gamma, real& k) const throw();
00257 
00258     /**
00259      * LambertConformalConic::Forward without returning the convergence and
00260      * scale.
00261      **********************************************************************/
00262     void Forward(real lon0, real lat, real lon,
00263                  real& x, real& y) const throw() {
00264       real gamma, k;
00265       Forward(lon0, lat, lon, x, y, gamma, k);
00266     }
00267 
00268     /**
00269      * LambertConformalConic::Reverse without returning the convergence and
00270      * scale.
00271      **********************************************************************/
00272     void Reverse(real lon0, real x, real y,
00273                  real& lat, real& lon) const throw() {
00274       real gamma, k;
00275       Reverse(lon0, x, y, lat, lon, gamma, k);
00276     }
00277 
00278     /** \name Inspector functions
00279      **********************************************************************/
00280     ///@{
00281     /**
00282      * @return \e a the equatorial radius of the ellipsoid (meters).  This is
00283      *   the value used in the constructor.
00284      **********************************************************************/
00285     Math::real MajorRadius() const throw() { return _a; }
00286 
00287     /**
00288      * @return \e f the flattening of the ellipsoid.  This is the
00289      *   value used in the constructor.
00290      **********************************************************************/
00291     Math::real Flattening() const throw() { return _f; }
00292 
00293     /// \cond SKIP
00294     /**
00295      * <b>DEPRECATED</b>
00296      * @return \e r the inverse flattening of the ellipsoid.
00297      **********************************************************************/
00298     Math::real InverseFlattening() const throw() { return 1/_f; }
00299     /// \endcond
00300 
00301     /**
00302      * @return latitude of the origin for the projection (degrees).
00303      *
00304      * This is the latitude of minimum scale and equals the \e stdlat in the
00305      * 1-parallel constructor and lies between \e stdlat1 and \e stdlat2 in the
00306      * 2-parallel constructors.
00307      **********************************************************************/
00308     Math::real OriginLatitude() const throw() { return _lat0; }
00309 
00310     /**
00311      * @return central scale for the projection.  This is the scale on the
00312      *   latitude of origin.
00313      **********************************************************************/
00314     Math::real CentralScale() const throw() { return _k0; }
00315     ///@}
00316 
00317     /**
00318      * A global instantiation of LambertConformalConic with the WGS84
00319      * ellipsoid, \e stdlat = 0, and \e k0 = 1.  This degenerates to the
00320      * Mercator projection.
00321      **********************************************************************/
00322     static const LambertConformalConic Mercator;
00323   };
00324 
00325 } // namespace GeographicLib
00326 
00327 #endif  // GEOGRAPHICLIB_LAMBERTCONFORMALCONIC_HPP