GeographicLib 2.3
Rhumb.hpp
Go to the documentation of this file.
1/**
2 * \file Rhumb.hpp
3 * \brief Header for GeographicLib::Rhumb and GeographicLib::RhumbLine classes
4 *
5 * Copyright (c) Charles Karney (2014-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_RHUMB_HPP)
11#define GEOGRAPHICLIB_RHUMB_HPP 1
12
15#include <vector>
16
17#if !defined(GEOGRAPHICLIB_RHUMBAREA_ORDER)
18/**
19 * The order of the series approximation used in rhumb area calculations.
20 * GEOGRAPHICLIB_RHUMBAREA_ORDER can be set to one of [4, 5, 6, 7, 8].
21 **********************************************************************/
22# define GEOGRAPHICLIB_RHUMBAREA_ORDER \
23 (GEOGRAPHICLIB_PRECISION == 2 ? 6 : \
24 (GEOGRAPHICLIB_PRECISION == 1 ? 4 : 8))
25#endif
26
27#if defined(_MSC_VER)
28// Squelch warnings about dll vs vector
29# pragma warning (push)
30# pragma warning (disable: 4251)
31#endif
32
33namespace GeographicLib {
34
35 class RhumbLine;
36
37 /**
38 * \brief Solve of the direct and inverse rhumb problems.
39 *
40 * The path of constant azimuth between two points on an ellipsoid at (\e
41 * lat1, \e lon1) and (\e lat2, \e lon2) is called the rhumb line (also
42 * called the loxodrome). Its length is \e s12 and its azimuth is \e azi12.
43 * (The azimuth is the heading measured clockwise from north.)
44 *
45 * Given \e lat1, \e lon1, \e azi12, and \e s12, we can determine \e lat2,
46 * and \e lon2. This is the \e direct rhumb problem and its solution is
47 * given by the function Rhumb::Direct.
48 *
49 * Given \e lat1, \e lon1, \e lat2, and \e lon2, we can determine \e azi12
50 * and \e s12. This is the \e inverse rhumb problem, whose solution is given
51 * by Rhumb::Inverse. This finds the shortest such rhumb line, i.e., the one
52 * that wraps no more than half way around the earth. If the end points are
53 * on opposite meridians, there are two shortest rhumb lines and the
54 * east-going one is chosen.
55 *
56 * These routines also optionally calculate the area under the rhumb line, \e
57 * S12. This is the area, measured counter-clockwise, of the rhumb line
58 * quadrilateral with corners (<i>lat1</i>,<i>lon1</i>), (0,<i>lon1</i>),
59 * (0,<i>lon2</i>), and (<i>lat2</i>,<i>lon2</i>).
60 *
61 * Note that rhumb lines may be appreciably longer (up to 50%) than the
62 * corresponding Geodesic. For example the distance between London Heathrow
63 * and Tokyo Narita via the rhumb line is 11400 km which is 18% longer than
64 * the geodesic distance 9600 km.
65 *
66 * This implementation is described in
67 * - C. F. F. Karney,<br>
68 * <a href="https://arxiv.org/abs/2303.03219">The area of rhumb
69 * polygons</a>,<br>
70 * Technical Report, SRI International, March 2023.<br>
71 * <a href="https://arxiv.org/abs/2303.03219">arxiv:2303.03219</a>
72 * .
73 * For more information on rhumb lines see \ref rhumb.
74 *
75 * Example of use:
76 * \include example-Rhumb.cpp
77 **********************************************************************/
78
80 private:
81 typedef Math::real real;
82 friend class RhumbLine;
83 template<class T> friend class PolygonAreaT;
84 DAuxLatitude _aux;
85 bool _exact;
86 real _a, _f, _n, _rm, _c2;
87 int _lL; // N.B. names of the form _[A-Z].* are reserved in C++
88 std::vector<real> _pP; // The Fourier coefficients P_l
89 static const int Lmax_ = GEOGRAPHICLIB_RHUMBAREA_ORDER;
90 void AreaCoeffs();
91 class qIntegrand {
92 const AuxLatitude& _aux;
93 public:
94 qIntegrand(const AuxLatitude& aux);
95 real operator()(real chi) const;
96 };
97
98 real MeanSinXi(const AuxAngle& chix, const AuxAngle& chiy) const;
99
100 // The following two functions (with lots of ignored arguments) mimic the
101 // interface to the corresponding Geodesic function. These are needed by
102 // PolygonAreaT.
103 void GenDirect(real lat1, real lon1, real azi12,
104 bool, real s12, unsigned outmask,
105 real& lat2, real& lon2, real&, real&, real&, real&, real&,
106 real& S12) const {
107 GenDirect(lat1, lon1, azi12, s12, outmask, lat2, lon2, S12);
108 }
109 void GenInverse(real lat1, real lon1, real lat2, real lon2,
110 unsigned outmask, real& s12, real& azi12,
111 real&, real& , real& , real& , real& S12) const {
112 GenInverse(lat1, lon1, lat2, lon2, outmask, s12, azi12, S12);
113 }
114
115 public:
116 /**
117 * Bit masks for what calculations to do. They specify which results to
118 * return in the general routines Rhumb::GenDirect and Rhumb::GenInverse
119 * routines. RhumbLine::mask is a duplication of this enum.
120 **********************************************************************/
121 enum mask {
122 /**
123 * No output.
124 * @hideinitializer
125 **********************************************************************/
126 NONE = 0U,
127 /**
128 * Calculate latitude \e lat2.
129 * @hideinitializer
130 **********************************************************************/
131 LATITUDE = 1U<<7,
132 /**
133 * Calculate longitude \e lon2.
134 * @hideinitializer
135 **********************************************************************/
136 LONGITUDE = 1U<<8,
137 /**
138 * Calculate azimuth \e azi12.
139 * @hideinitializer
140 **********************************************************************/
141 AZIMUTH = 1U<<9,
142 /**
143 * Calculate distance \e s12.
144 * @hideinitializer
145 **********************************************************************/
146 DISTANCE = 1U<<10,
147 /**
148 * Calculate area \e S12.
149 * @hideinitializer
150 **********************************************************************/
151 AREA = 1U<<14,
152 /**
153 * Unroll \e lon2 in the direct calculation.
154 * @hideinitializer
155 **********************************************************************/
156 LONG_UNROLL = 1U<<15,
157 /**
158 * Calculate everything. (LONG_UNROLL is not included in this mask.)
159 * @hideinitializer
160 **********************************************************************/
161 ALL = 0x7F80U,
162 };
163
164 /**
165 * Constructor for an ellipsoid with
166 *
167 * @param[in] a equatorial radius (meters).
168 * @param[in] f flattening of ellipsoid. Setting \e f = 0 gives a sphere.
169 * Negative \e f gives a prolate ellipsoid.
170 * @param[in] exact if true use the exact expressions for the auxiliary
171 * latitudes; otherwise use series expansion (accurate for |<i>f</i>| <
172 * 0.01) [default false].
173 * @exception GeographicErr if \e a or (1 &minus; \e f) \e a is not
174 * positive.
175 **********************************************************************/
176 Rhumb(real a, real f, bool exact = false);
177
178 /**
179 * Solve the direct rhumb problem returning also the area.
180 *
181 * @param[in] lat1 latitude of point 1 (degrees).
182 * @param[in] lon1 longitude of point 1 (degrees).
183 * @param[in] azi12 azimuth of the rhumb line (degrees).
184 * @param[in] s12 distance between point 1 and point 2 (meters); it can be
185 * negative.
186 * @param[out] lat2 latitude of point 2 (degrees).
187 * @param[out] lon2 longitude of point 2 (degrees).
188 * @param[out] S12 area under the rhumb line (meters<sup>2</sup>).
189 *
190 * \e lat1 should be in the range [&minus;90&deg;, 90&deg;]. The value of
191 * \e lon2 returned is in the range [&minus;180&deg;, 180&deg;].
192 *
193 * If point 1 is a pole, the cosine of its latitude is taken to be
194 * 1/&epsilon;<sup>2</sup> (where &epsilon; is 2<sup>-52</sup>). This
195 * position, which is extremely close to the actual pole, allows the
196 * calculation to be carried out in finite terms. If \e s12 is large
197 * enough that the rhumb line crosses a pole, the longitude of point 2
198 * is indeterminate (a NaN is returned for \e lon2 and \e S12).
199 **********************************************************************/
200 void Direct(real lat1, real lon1, real azi12, real s12,
201 real& lat2, real& lon2, real& S12) const {
202 GenDirect(lat1, lon1, azi12, s12,
203 LATITUDE | LONGITUDE | AREA, lat2, lon2, S12);
204 }
205
206 /**
207 * Solve the direct rhumb problem without the area.
208 **********************************************************************/
209 void Direct(real lat1, real lon1, real azi12, real s12,
210 real& lat2, real& lon2) const {
211 real t;
212 GenDirect(lat1, lon1, azi12, s12, LATITUDE | LONGITUDE, lat2, lon2, t);
213 }
214
215 /**
216 * The general direct rhumb problem. Rhumb::Direct is defined in terms
217 * of this function.
218 *
219 * @param[in] lat1 latitude of point 1 (degrees).
220 * @param[in] lon1 longitude of point 1 (degrees).
221 * @param[in] azi12 azimuth of the rhumb line (degrees).
222 * @param[in] s12 distance between point 1 and point 2 (meters); it can be
223 * negative.
224 * @param[in] outmask a bitor'ed combination of Rhumb::mask values
225 * specifying which of the following parameters should be set.
226 * @param[out] lat2 latitude of point 2 (degrees).
227 * @param[out] lon2 longitude of point 2 (degrees).
228 * @param[out] S12 area under the rhumb line (meters<sup>2</sup>).
229 *
230 * The Rhumb::mask values possible for \e outmask are
231 * - \e outmask |= Rhumb::LATITUDE for the latitude \e lat2;
232 * - \e outmask |= Rhumb::LONGITUDE for the latitude \e lon2;
233 * - \e outmask |= Rhumb::AREA for the area \e S12;
234 * - \e outmask |= Rhumb::ALL for all of the above;
235 * - \e outmask |= Rhumb::LONG_UNROLL to unroll \e lon2 instead of wrapping
236 * it into the range [&minus;180&deg;, 180&deg;].
237 * .
238 * With the Rhumb::LONG_UNROLL bit set, the quantity \e lon2 &minus;
239 * \e lon1 indicates how many times and in what sense the rhumb line
240 * encircles the ellipsoid.
241 **********************************************************************/
242 void GenDirect(real lat1, real lon1, real azi12, real s12,
243 unsigned outmask, real& lat2, real& lon2, real& S12) const;
244
245 /**
246 * Solve the inverse rhumb problem returning also the area.
247 *
248 * @param[in] lat1 latitude of point 1 (degrees).
249 * @param[in] lon1 longitude of point 1 (degrees).
250 * @param[in] lat2 latitude of point 2 (degrees).
251 * @param[in] lon2 longitude of point 2 (degrees).
252 * @param[out] s12 rhumb distance between point 1 and point 2 (meters).
253 * @param[out] azi12 azimuth of the rhumb line (degrees).
254 * @param[out] S12 area under the rhumb line (meters<sup>2</sup>).
255 *
256 * The shortest rhumb line is found. If the end points are on opposite
257 * meridians, there are two shortest rhumb lines and the east-going one is
258 * chosen. \e lat1 and \e lat2 should be in the range [&minus;90&deg;,
259 * 90&deg;]. The value of \e azi12 returned is in the range
260 * [&minus;180&deg;, 180&deg;].
261 *
262 * If either point is a pole, the cosine of its latitude is taken to be
263 * 1/&epsilon;<sup>2</sup> (where &epsilon; is 2<sup>-52</sup>). This
264 * position, which is extremely close to the actual pole, allows the
265 * calculation to be carried out in finite terms.
266 **********************************************************************/
267 void Inverse(real lat1, real lon1, real lat2, real lon2,
268 real& s12, real& azi12, real& S12) const {
269 GenInverse(lat1, lon1, lat2, lon2,
270 DISTANCE | AZIMUTH | AREA, s12, azi12, S12);
271 }
272
273 /**
274 * Solve the inverse rhumb problem without the area.
275 **********************************************************************/
276 void Inverse(real lat1, real lon1, real lat2, real lon2,
277 real& s12, real& azi12) const {
278 real t;
279 GenInverse(lat1, lon1, lat2, lon2, DISTANCE | AZIMUTH, s12, azi12, t);
280 }
281
282 /**
283 * The general inverse rhumb problem. Rhumb::Inverse is defined in terms
284 * of this function.
285 *
286 * @param[in] lat1 latitude of point 1 (degrees).
287 * @param[in] lon1 longitude of point 1 (degrees).
288 * @param[in] lat2 latitude of point 2 (degrees).
289 * @param[in] lon2 longitude of point 2 (degrees).
290 * @param[in] outmask a bitor'ed combination of Rhumb::mask values
291 * specifying which of the following parameters should be set.
292 * @param[out] s12 rhumb distance between point 1 and point 2 (meters).
293 * @param[out] azi12 azimuth of the rhumb line (degrees).
294 * @param[out] S12 area under the rhumb line (meters<sup>2</sup>).
295 *
296 * The Rhumb::mask values possible for \e outmask are
297 * - \e outmask |= Rhumb::DISTANCE for the latitude \e s12;
298 * - \e outmask |= Rhumb::AZIMUTH for the latitude \e azi12;
299 * - \e outmask |= Rhumb::AREA for the area \e S12;
300 * - \e outmask |= Rhumb::ALL for all of the above;
301 **********************************************************************/
302 void GenInverse(real lat1, real lon1, real lat2, real lon2,
303 unsigned outmask,
304 real& s12, real& azi12, real& S12) const;
305
306 /**
307 * Typedef for the class for computing multiple points on a rhumb line.
308 **********************************************************************/
310
311 /**
312 * Set up to compute several points on a single rhumb line.
313 *
314 * @param[in] lat1 latitude of point 1 (degrees).
315 * @param[in] lon1 longitude of point 1 (degrees).
316 * @param[in] azi12 azimuth of the rhumb line (degrees).
317 * @return a RhumbLine object.
318 *
319 * \e lat1 should be in the range [&minus;90&deg;, 90&deg;].
320 *
321 * If point 1 is a pole, the cosine of its latitude is taken to be
322 * 1/&epsilon;<sup>2</sup> (where &epsilon; is 2<sup>-52</sup>). This
323 * position, which is extremely close to the actual pole, allows the
324 * calculation to be carried out in finite terms.
325 **********************************************************************/
326 RhumbLine Line(real lat1, real lon1, real azi12) const;
327
328 /** \name Inspector functions.
329 **********************************************************************/
330 ///@{
331
332 /**
333 * @return \e a the equatorial radius of the ellipsoid (meters). This is
334 * the value used in the constructor.
335 **********************************************************************/
336 Math::real EquatorialRadius() const { return _a; }
337
338 /**
339 * @return \e f the flattening of the ellipsoid. This is the
340 * value used in the constructor.
341 **********************************************************************/
342 Math::real Flattening() const { return _f; }
343
344 /**
345 * @return total area of ellipsoid in meters<sup>2</sup>. The area of a
346 * polygon encircling a pole can be found by adding
347 * Geodesic::EllipsoidArea()/2 to the sum of \e S12 for each side of the
348 * polygon.
349 **********************************************************************/
351 // _c2 contains a Math::degrees() factor, so 4*pi -> 2*Math::td.
352 return 2 * real(Math::td) * _c2;
353 }
354 ///@}
355
356 /**
357 * A global instantiation of Rhumb with the parameters for the WGS84
358 * ellipsoid.
359 **********************************************************************/
360 static const Rhumb& WGS84();
361 };
362
363 /**
364 * \brief Find a sequence of points on a single rhumb line.
365 *
366 * RhumbLine facilitates the determination of a series of points on a single
367 * rhumb line. The starting point (\e lat1, \e lon1) and the azimuth \e
368 * azi12 are specified in the call to Rhumb::Line which returns a RhumbLine
369 * object. RhumbLine.Position returns the location of point 2 (and,
370 * optionally, the corresponding area, \e S12) a distance \e s12 along the
371 * rhumb line.
372 *
373 * There is no public constructor for this class. (Use Rhumb::Line to create
374 * an instance.) The Rhumb object used to create a RhumbLine must stay in
375 * scope as long as the RhumbLine.
376 *
377 * Example of use:
378 * \include example-RhumbLine.cpp
379 **********************************************************************/
380
382 private:
383 typedef Math::real real;
384 friend class Rhumb;
385 const Rhumb& _rh;
386 real _lat1, _lon1, _azi12, _salp, _calp, _mu1, _psi1;
387 AuxAngle _phi1, _chi1;
388 // copy assignment not allowed
389 RhumbLine& operator=(const RhumbLine&) = delete;
390 RhumbLine(const Rhumb& rh, real lat1, real lon1, real azi12);
391
392 public:
393
394 /**
395 * Construction is via default copy constructor.
396 **********************************************************************/
397 RhumbLine(const RhumbLine&) = default;
398 /**
399 * This is a duplication of Rhumb::mask.
400 **********************************************************************/
401 enum mask {
402 /**
403 * No output.
404 * @hideinitializer
405 **********************************************************************/
407 /**
408 * Calculate latitude \e lat2.
409 * @hideinitializer
410 **********************************************************************/
411 LATITUDE = Rhumb::LATITUDE,
412 /**
413 * Calculate longitude \e lon2.
414 * @hideinitializer
415 **********************************************************************/
416 LONGITUDE = Rhumb::LONGITUDE,
417 /**
418 * Calculate azimuth \e azi12.
419 * @hideinitializer
420 **********************************************************************/
421 AZIMUTH = Rhumb::AZIMUTH,
422 /**
423 * Calculate distance \e s12.
424 * @hideinitializer
425 **********************************************************************/
426 DISTANCE = Rhumb::DISTANCE,
427 /**
428 * Calculate area \e S12.
429 * @hideinitializer
430 **********************************************************************/
432 /**
433 * Unroll \e lon2 in the direct calculation.
434 * @hideinitializer
435 **********************************************************************/
436 LONG_UNROLL = Rhumb::LONG_UNROLL,
437 /**
438 * Calculate everything. (LONG_UNROLL is not included in this mask.)
439 * @hideinitializer
440 **********************************************************************/
442 };
443
444 /**
445 * Typedef for the base class implementing rhumb lines.
446 **********************************************************************/
448
449 /**
450 * Compute the position of point 2 which is a distance \e s12 (meters) from
451 * point 1. The area is also computed.
452 *
453 * @param[in] s12 distance between point 1 and point 2 (meters); it can be
454 * negative.
455 * @param[out] lat2 latitude of point 2 (degrees).
456 * @param[out] lon2 longitude of point 2 (degrees).
457 * @param[out] S12 area under the rhumb line (meters<sup>2</sup>).
458 *
459 * The value of \e lon2 returned is in the range [&minus;180&deg;,
460 * 180&deg;].
461 *
462 * If \e s12 is large enough that the rhumb line crosses a pole, the
463 * longitude of point 2 is indeterminate (a NaN is returned for \e lon2 and
464 * \e S12).
465 **********************************************************************/
466 void Position(real s12, real& lat2, real& lon2, real& S12) const {
467 GenPosition(s12, LATITUDE | LONGITUDE | AREA, lat2, lon2, S12);
468 }
469
470 /**
471 * Compute the position of point 2 which is a distance \e s12 (meters) from
472 * point 1. The area is not computed.
473 **********************************************************************/
474 void Position(real s12, real& lat2, real& lon2) const {
475 real t;
476 GenPosition(s12, LATITUDE | LONGITUDE, lat2, lon2, t);
477 }
478
479 /**
480 * The general position routine. RhumbLine::Position is defined in term so
481 * this function.
482 *
483 * @param[in] s12 distance between point 1 and point 2 (meters); it can be
484 * negative.
485 * @param[in] outmask a bitor'ed combination of RhumbLine::mask values
486 * specifying which of the following parameters should be set.
487 * @param[out] lat2 latitude of point 2 (degrees).
488 * @param[out] lon2 longitude of point 2 (degrees).
489 * @param[out] S12 area under the rhumb line (meters<sup>2</sup>).
490 *
491 * The RhumbLine::mask values possible for \e outmask are
492 * - \e outmask |= RhumbLine::LATITUDE for the latitude \e lat2;
493 * - \e outmask |= RhumbLine::LONGITUDE for the latitude \e lon2;
494 * - \e outmask |= RhumbLine::AREA for the area \e S12;
495 * - \e outmask |= RhumbLine::ALL for all of the above;
496 * - \e outmask |= RhumbLine::LONG_UNROLL to unroll \e lon2 instead of
497 * wrapping it into the range [&minus;180&deg;, 180&deg;].
498 * .
499 * With the RhumbLine::LONG_UNROLL bit set, the quantity \e lon2 &minus; \e
500 * lon1 indicates how many times and in what sense the rhumb line encircles
501 * the ellipsoid.
502 *
503 * If \e s12 is large enough that the rhumb line crosses a pole, the
504 * longitude of point 2 is indeterminate (a NaN is returned for \e lon2 and
505 * \e S12).
506 **********************************************************************/
507 void GenPosition(real s12, unsigned outmask,
508 real& lat2, real& lon2, real& S12) const;
509
510 /** \name Inspector functions
511 **********************************************************************/
512 ///@{
513
514 /**
515 * @return \e lat1 the latitude of point 1 (degrees).
516 **********************************************************************/
517 Math::real Latitude() const { return _lat1; }
518
519 /**
520 * @return \e lon1 the longitude of point 1 (degrees).
521 **********************************************************************/
522 Math::real Longitude() const { return _lon1; }
523
524 /**
525 * @return \e azi12 the azimuth of the rhumb line (degrees).
526 **********************************************************************/
527 Math::real Azimuth() const { return _azi12; }
528
529 /**
530 * @return \e a the equatorial radius of the ellipsoid (meters). This is
531 * the value inherited from the Rhumb object used in the constructor.
532 **********************************************************************/
534
535 /**
536 * @return \e f the flattening of the ellipsoid. This is the value
537 * inherited from the Rhumb object used in the constructor.
538 **********************************************************************/
539 Math::real Flattening() const { return _rh.Flattening(); }
540 };
541
542} // namespace GeographicLib
543
544#endif // GEOGRAPHICLIB_RHUMB_HPP
Header for GeographicLib::Constants class.
#define GEOGRAPHICLIB_EXPORT
Definition: Constants.hpp:67
Header for the GeographicLib::DAuxLatitude class.
GeographicLib::Math::real real
Definition: GeodSolve.cpp:29
#define GEOGRAPHICLIB_RHUMBAREA_ORDER
Definition: Rhumb.hpp:22
An accurate representation of angles.
Definition: AuxAngle.hpp:47
Conversions between auxiliary latitudes.
Definition: AuxLatitude.hpp:75
Divided differences of auxiliary latitudes.
@ td
degrees per turn
Definition: Math.hpp:142
Find a sequence of points on a single rhumb line.
Definition: Rhumb.hpp:381
void Position(real s12, real &lat2, real &lon2) const
Definition: Rhumb.hpp:474
void Position(real s12, real &lat2, real &lon2, real &S12) const
Definition: Rhumb.hpp:466
RhumbLine(const RhumbLine &)=default
Math::real EquatorialRadius() const
Definition: Rhumb.hpp:533
Math::real Latitude() const
Definition: Rhumb.hpp:517
Math::real Azimuth() const
Definition: Rhumb.hpp:527
Math::real Flattening() const
Definition: Rhumb.hpp:539
Math::real Longitude() const
Definition: Rhumb.hpp:522
Solve of the direct and inverse rhumb problems.
Definition: Rhumb.hpp:79
Math::real Flattening() const
Definition: Rhumb.hpp:342
void Direct(real lat1, real lon1, real azi12, real s12, real &lat2, real &lon2, real &S12) const
Definition: Rhumb.hpp:200
void Inverse(real lat1, real lon1, real lat2, real lon2, real &s12, real &azi12, real &S12) const
Definition: Rhumb.hpp:267
void Inverse(real lat1, real lon1, real lat2, real lon2, real &s12, real &azi12) const
Definition: Rhumb.hpp:276
void Direct(real lat1, real lon1, real azi12, real s12, real &lat2, real &lon2) const
Definition: Rhumb.hpp:209
Math::real EquatorialRadius() const
Definition: Rhumb.hpp:336
RhumbLine LineClass
Definition: Rhumb.hpp:309
Math::real EllipsoidArea() const
Definition: Rhumb.hpp:350
Namespace for GeographicLib.
Definition: Accumulator.cpp:12