GeographicLib
1.21
|
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–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