GeographicLib  2.6
AlbersEqualArea.hpp
Go to the documentation of this file.
1 /**
2  * \file AlbersEqualArea.hpp
3  * \brief Header for GeographicLib::AlbersEqualArea class
4  *
5  * Copyright (c) Charles Karney (2010-2023) <karney@alum.mit.edu> and licensed
6  * under the MIT/X11 License. For more information, see
7  * https://geographiclib.sourceforge.io/
8  **********************************************************************/
9 
10 #if !defined(GEOGRAPHICLIB_ALBERSEQUALAREA_HPP)
11 #define GEOGRAPHICLIB_ALBERSEQUALAREA_HPP 1
12 
14 
15 namespace GeographicLib {
16 
17  /**
18  * \brief Albers equal area conic projection
19  *
20  * Implementation taken from the report,
21  * - J. P. Snyder,
22  * <a href="https://pubs.usgs.gov/publication/pp1395"> Map Projections: A
23  * Working Manual</a>, USGS Professional Paper 1395 (1987),
24  * pp. 101--102.
25  *
26  * This is a implementation of the equations in Snyder except that divided
27  * differences will be [have been] used to transform the expressions into
28  * ones which may be evaluated accurately. [In this implementation, the
29  * projection correctly becomes the cylindrical equal area or the azimuthal
30  * equal area projection when the standard latitude is the equator or a
31  * pole.]
32  *
33  * The ellipsoid parameters, the standard parallels, and the scale on the
34  * standard parallels are set in the constructor. Internally, the case with
35  * two standard parallels is converted into a single standard parallel, the
36  * latitude of minimum azimuthal scale, with an azimuthal scale specified on
37  * this parallel. This latitude is also used as the latitude of origin which
38  * is returned by AlbersEqualArea::OriginLatitude. The azimuthal scale on
39  * the latitude of origin is given by AlbersEqualArea::CentralScale. The
40  * case with two standard parallels at opposite poles is singular and is
41  * disallowed. The central meridian (which is a trivial shift of the
42  * longitude) is specified as the \e lon0 argument of the
43  * AlbersEqualArea::Forward and AlbersEqualArea::Reverse functions.
44  * AlbersEqualArea::Forward and AlbersEqualArea::Reverse also return the
45  * meridian convergence, &gamma;, and azimuthal scale, \e k. A small square
46  * aligned with the cardinal directions is projected to a rectangle with
47  * dimensions \e k (in the E-W direction) and 1/\e k (in the N-S direction).
48  * The E-W sides of the rectangle are oriented &gamma; degrees
49  * counter-clockwise from the \e x axis. There is no provision in this class
50  * for specifying a false easting or false northing or a different latitude
51  * of origin.
52  *
53  * Example of use:
54  * \include example-AlbersEqualArea.cpp
55  *
56  * <a href="ConicProj.1.html">ConicProj</a> is a command-line utility
57  * providing access to the functionality of LambertConformalConic and
58  * AlbersEqualArea.
59  **********************************************************************/
61  private:
62  typedef Math::real real;
63  real eps_, epsx_, epsx2_, tol_, tol0_;
64  real _a, _f, _fm, _e2, _e, _e2m, _qZ, _qx;
65  real _sign, _lat0, _k0;
66  real _n0, _m02, _nrho0, _k2, _txi0, _scxi0, _sxi0;
67  static const int numit_ = 5; // Newton iterations in Reverse
68  static const int numit0_ = 20; // Newton iterations in Init
69  static real hyp(real x) {
70  using std::hypot;
71  return hypot(real(1), x);
72  }
73  // atanh( e * x)/ e if f > 0
74  // atan (sqrt(-e2) * x)/sqrt(-e2) if f < 0
75  // x if f = 0
76  real atanhee(real x) const {
77  using std::atan, std::atanh;
78  return _f > 0 ? atanh(_e * x)/_e : (_f < 0 ? (atan(_e * x)/_e) : x);
79  }
80  // return atanh(sqrt(x))/sqrt(x) - 1, accurate for small x
81  static real atanhxm1(real x);
82 
83  // Divided differences
84  // Definition: Df(x,y) = (f(x)-f(y))/(x-y)
85  // See:
86  // W. M. Kahan and R. J. Fateman,
87  // Symbolic computation of divided differences,
88  // SIGSAM Bull. 33(2), 7-28 (1999)
89  // https://doi.org/10.1145/334714.334716
90  // http://www.cs.berkeley.edu/~fateman/papers/divdiff.pdf
91  //
92  // General rules
93  // h(x) = f(g(x)): Dh(x,y) = Df(g(x),g(y))*Dg(x,y)
94  // h(x) = f(x)*g(x):
95  // Dh(x,y) = Df(x,y)*g(x) + Dg(x,y)*f(y)
96  // = Df(x,y)*g(y) + Dg(x,y)*f(x)
97  // = Df(x,y)*(g(x)+g(y))/2 + Dg(x,y)*(f(x)+f(y))/2
98  //
99  // sn(x) = x/sqrt(1+x^2): Dsn(x,y) = (x+y)/((sn(x)+sn(y))*(1+x^2)*(1+y^2))
100  static real Dsn(real x, real y, real sx, real sy) {
101  // sx = x/hyp(x)
102  real t = x * y;
103  return t > 0 ? (x + y) * Math::sq( (sx * sy)/t ) / (sx + sy) :
104  (x - y != 0 ? (sx - sy) / (x - y) : 1);
105  }
106  // Datanhee(x,y) = (atanee(x)-atanee(y))/(x-y)
107  // = atanhee((x-y)/(1-e^2*x*y))/(x-y)
108  real Datanhee(real x, real y) const {
109  real t = x - y, d = 1 - _e2 * x * y;
110  return t == 0 ? 1 / d :
111  (x*y < 0 ? atanhee(x) - atanhee(y) : atanhee(t / d)) / t;
112  }
113  // DDatanhee(x,y) = (Datanhee(1,y) - Datanhee(1,x))/(y-x)
114  real DDatanhee(real x, real y) const;
115  real DDatanhee0(real x, real y) const;
116  real DDatanhee1(real x, real y) const;
117  real DDatanhee2(real x, real y) const;
118  void Init(real sphi1, real cphi1, real sphi2, real cphi2, real k1);
119  real txif(real tphi) const;
120  real tphif(real txi) const;
121  public:
122 
123  /**
124  * Constructor with a single standard parallel.
125  *
126  * @param[in] a equatorial radius of ellipsoid (meters).
127  * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
128  * Negative \e f gives a prolate ellipsoid.
129  * @param[in] stdlat standard parallel (degrees), the circle of tangency.
130  * @param[in] k0 azimuthal scale on the standard parallel.
131  * @exception GeographicErr if \e a, (1 &minus; \e f) \e a, or \e k0 is
132  * not positive.
133  * @exception GeographicErr if \e stdlat is not in [&minus;90&deg;,
134  * 90&deg;].
135  **********************************************************************/
136  AlbersEqualArea(real a, real f, real stdlat, real k0);
137 
138  /**
139  * Constructor with two standard parallels.
140  *
141  * @param[in] a equatorial radius of ellipsoid (meters).
142  * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
143  * Negative \e f gives a prolate ellipsoid.
144  * @param[in] stdlat1 first standard parallel (degrees).
145  * @param[in] stdlat2 second standard parallel (degrees).
146  * @param[in] k1 azimuthal scale on the standard parallels.
147  * @exception GeographicErr if \e a, (1 &minus; \e f) \e a, or \e k1 is
148  * not positive.
149  * @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in
150  * [&minus;90&deg;, 90&deg;], or if \e stdlat1 and \e stdlat2 are
151  * opposite poles.
152  **********************************************************************/
153  AlbersEqualArea(real a, real f, real stdlat1, real stdlat2, real k1);
154 
155  /**
156  * Constructor with two standard parallels specified by sines and cosines.
157  *
158  * @param[in] a equatorial radius of ellipsoid (meters).
159  * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
160  * Negative \e f gives a prolate ellipsoid.
161  * @param[in] sinlat1 sine of first standard parallel.
162  * @param[in] coslat1 cosine of first standard parallel.
163  * @param[in] sinlat2 sine of second standard parallel.
164  * @param[in] coslat2 cosine of second standard parallel.
165  * @param[in] k1 azimuthal scale on the standard parallels.
166  * @exception GeographicErr if \e a, (1 &minus; \e f) \e a, or \e k1 is
167  * not positive.
168  * @exception GeographicErr if \e stdlat1 or \e stdlat2 is not in
169  * [&minus;90&deg;, 90&deg;], or if \e stdlat1 and \e stdlat2 are
170  * opposite poles.
171  *
172  * This allows parallels close to the poles to be specified accurately.
173  * This routine computes the latitude of origin and the azimuthal scale at
174  * this latitude. If \e dlat = abs(\e lat2 &minus; \e lat1) &le; 160&deg;,
175  * then the error in the latitude of origin is less than 4.5 &times;
176  * 10<sup>&minus;14</sup>d;.
177  **********************************************************************/
178  AlbersEqualArea(real a, real f,
179  real sinlat1, real coslat1,
180  real sinlat2, real coslat2,
181  real k1);
182 
183  /**
184  * Set the azimuthal scale for the projection.
185  *
186  * @param[in] lat (degrees).
187  * @param[in] k azimuthal scale at latitude \e lat (default 1).
188  * @exception GeographicErr \e k is not positive.
189  * @exception GeographicErr if \e lat is not in (&minus;90&deg;,
190  * 90&deg;).
191  *
192  * This allows a "latitude of conformality" to be specified.
193  **********************************************************************/
194  void SetScale(real lat, real k = real(1));
195 
196  /**
197  * Forward projection, from geographic to Lambert conformal conic.
198  *
199  * @param[in] lon0 central meridian longitude (degrees).
200  * @param[in] lat latitude of point (degrees).
201  * @param[in] lon longitude of point (degrees).
202  * @param[out] x easting of point (meters).
203  * @param[out] y northing of point (meters).
204  * @param[out] gamma meridian convergence at point (degrees).
205  * @param[out] k azimuthal scale of projection at point; the radial
206  * scale is the 1/\e k.
207  *
208  * The latitude origin is given by AlbersEqualArea::LatitudeOrigin(). No
209  * false easting or northing is added and \e lat should be in the range
210  * [&minus;90&deg;, 90&deg;]. The values of \e x and \e y returned for
211  * points which project to infinity (i.e., one or both of the poles) will
212  * be large but finite.
213  **********************************************************************/
214  void Forward(real lon0, real lat, real lon,
215  real& x, real& y, real& gamma, real& k) const;
216 
217  /**
218  * Reverse projection, from Lambert conformal conic to geographic.
219  *
220  * @param[in] lon0 central meridian longitude (degrees).
221  * @param[in] x easting of point (meters).
222  * @param[in] y northing of point (meters).
223  * @param[out] lat latitude of point (degrees).
224  * @param[out] lon longitude of point (degrees).
225  * @param[out] gamma meridian convergence at point (degrees).
226  * @param[out] k azimuthal scale of projection at point; the radial
227  * scale is the 1/\e k.
228  *
229  * The latitude origin is given by AlbersEqualArea::LatitudeOrigin(). No
230  * false easting or northing is added. The value of \e lon returned is in
231  * the range [&minus;180&deg;, 180&deg;]. The value of \e lat returned is
232  * in the range [&minus;90&deg;, 90&deg;]. If the input point is outside
233  * the legal projected space the nearest pole is returned.
234  **********************************************************************/
235  void Reverse(real lon0, real x, real y,
236  real& lat, real& lon, real& gamma, real& k) const;
237 
238  /**
239  * AlbersEqualArea::Forward without returning the convergence and
240  * scale.
241  **********************************************************************/
242  void Forward(real lon0, real lat, real lon,
243  real& x, real& y) const {
244  real gamma, k;
245  Forward(lon0, lat, lon, x, y, gamma, k);
246  }
247 
248  /**
249  * AlbersEqualArea::Reverse without returning the convergence and
250  * scale.
251  **********************************************************************/
252  void Reverse(real lon0, real x, real y,
253  real& lat, real& lon) const {
254  real gamma, k;
255  Reverse(lon0, x, y, lat, lon, gamma, k);
256  }
257 
258  /** \name Inspector functions
259  **********************************************************************/
260  ///@{
261  /**
262  * @return \e a the equatorial radius of the ellipsoid (meters). This is
263  * the value used in the constructor.
264  **********************************************************************/
265  Math::real EquatorialRadius() const { return _a; }
266 
267  /**
268  * @return \e f the flattening of the ellipsoid. This is the value used in
269  * the constructor.
270  **********************************************************************/
271  Math::real Flattening() const { return _f; }
272 
273  /**
274  * @return latitude of the origin for the projection (degrees).
275  *
276  * This is the latitude of minimum azimuthal scale and equals the \e stdlat
277  * in the 1-parallel constructor and lies between \e stdlat1 and \e stdlat2
278  * in the 2-parallel constructors.
279  **********************************************************************/
280  Math::real OriginLatitude() const { return _lat0; }
281 
282  /**
283  * @return central scale for the projection. This is the azimuthal scale
284  * on the latitude of origin.
285  **********************************************************************/
286  Math::real CentralScale() const { return _k0; }
287  ///@}
288 
289  /**
290  * A global instantiation of AlbersEqualArea with the WGS84 ellipsoid, \e
291  * stdlat = 0, and \e k0 = 1. This degenerates to the cylindrical equal
292  * area projection.
293  **********************************************************************/
294  static const AlbersEqualArea& CylindricalEqualArea();
295 
296  /**
297  * A global instantiation of AlbersEqualArea with the WGS84 ellipsoid, \e
298  * stdlat = 90&deg;, and \e k0 = 1. This degenerates to the
299  * Lambert azimuthal equal area projection.
300  **********************************************************************/
301  static const AlbersEqualArea& AzimuthalEqualAreaNorth();
302 
303  /**
304  * A global instantiation of AlbersEqualArea with the WGS84 ellipsoid, \e
305  * stdlat = &minus;90&deg;, and \e k0 = 1. This degenerates to the
306  * Lambert azimuthal equal area projection.
307  **********************************************************************/
308  static const AlbersEqualArea& AzimuthalEqualAreaSouth();
309  };
310 
311 } // namespace GeographicLib
312 
313 #endif // GEOGRAPHICLIB_ALBERSEQUALAREA_HPP
#define GEOGRAPHICLIB_EXPORT
Definition: Constants.hpp:59
Albers equal area conic projection.
static T sq(T x)
Definition: Math.hpp:209
Namespace for GeographicLib.
Definition: Accumulator.cpp:12
void Forward(real lon0, real lat, real lon, real &x, real &y) const
GeographicLib::Math::real real
Definition: Geod3Solve.cpp:25
Header for GeographicLib::Constants class.
void Reverse(real lon0, real x, real y, real &lat, real &lon) const