Geodesic.hpp
Go to the documentation of this file.
00001 /**
00002  * \file Geodesic.hpp
00003  * \brief Header for GeographicLib::Geodesic class
00004  *
00005  * Copyright (c) Charles Karney (2008) <charles@karney.com>
00006  * and licensed under the LGPL.
00007  **********************************************************************/
00008 
00009 #if !defined(GEODESIC_HPP)
00010 #define GEODESIC_HPP "$Id: Geodesic.hpp 6559 2009-02-28 16:49:53Z ckarney $"
00011 
00012 #include <cmath>
00013 
00014 namespace GeographicLib {
00015 
00016   class GeodesicLine;
00017 
00018   /**
00019    * \brief %Geodesic calculations
00020    *
00021    * The shortest path between two points on the ellipsoid at (\e lat1, \e
00022    * lon1) and (\e lat2, \e lon2) is called the geodesic.  Its length is \e s12
00023    * and the geodesic from point 1 to point 2 has azimuths \e azi1 and \e azi2
00024    * at the two end points.  (The azimuth is the heading measured clockwise
00025    * from north.  \e azi2 is the "forward" azimuth, i.e., the heading that
00026    * takes you beyond point 2 not back to point 1.)
00027    *
00028    * Given \e lat1, \e lon1, \e azi1, and \e s12, we can determine \e lat2, \e
00029    * lon2, \e azi2.  This is the \e direct geodesic problem.  (If \e s12 is
00030    * sufficiently large that the geodesic wraps more than halfway around the
00031    * earth, there will be a true geodesic between the points with a smaller \e
00032    * s12.)
00033    *
00034    * Given \e lat1, \e lon1, \e lat2, and \e lon2, we can determine \e azi1, \e
00035    * azi2, \e s12.  This is the \e inverse geodesic problem.  Usually, the
00036    * solution to the inverse problem is unique.  In cases where there are
00037    * muliple solutions (all with the same \e s12, of course), all the solutions
00038    * can be easily generated once a particular solution is provided.
00039    *
00040    * The calculations are accurate to better than 12 nm.  (See \ref geoderrors
00041    * for details.)
00042    **********************************************************************/
00043 
00044   class Geodesic {
00045   private:
00046     friend class GeodesicLine;
00047     static const int maxpow = 8, azi2sense = 1;
00048 
00049     static inline double sq(double x) throw() { return x * x; }
00050 #if defined(_MSC_VER)
00051     static inline double hypot(double x, double y) throw()
00052     { return _hypot(x, y); }
00053 #else
00054     static inline double hypot(double x, double y) throw()
00055     { return ::hypot(x, y); }
00056 #endif
00057     double Chi12(double sbet1, double cbet1, double sbet2, double cbet2,
00058                  double salp1, double calp1, double& salp2, double& calp2,
00059                  double& sig12,
00060                  double& ssig1, double& csig1, double& ssig2, double& csig2,
00061                  double& u2, bool diffp, double& dchi12, double c[])
00062       const throw();
00063 
00064     static const double eps2, tol;
00065     const double _a, _f, _f1, _e2, _ep2, _b;
00066     static double SinSeries(double sinx, double cosx, const double c[], int n)
00067       throw();
00068 
00069     static inline double AngNormalize(double x) throw() {
00070       // Place angle in [-180, 180).  Assumes x is in [-540, 540).
00071       return x >= 180 ? x - 360 : x < -180 ? x + 360 : x;
00072     }
00073     static inline double AngRound(double x) throw() {
00074       // The makes the smallest gap in x = 1/16 - nextafter(1/16, 0) = 1/2^57
00075       // for doubles = 0.7 pm on the earth if x is an angle in degrees.  (This
00076       // is about 1000 times more resolution than we get with angles around 90
00077       // degrees.)  We use this to avoid having to deal with near singular
00078       // cases when x is non-zero but tiny (e.g., 1.0e-200).
00079       const double z = 0.0625;  // 1/16
00080       double y = std::abs(x);
00081       // The compiler mustn't "simplify" z - (z - y) to y
00082       y = y < z ? z - (z - y) : y;
00083       return x < 0 ? -y : y;
00084     }
00085     static inline void SinCosNorm(double& sinx, double& cosx) throw() {
00086       double r = hypot(sinx, cosx);
00087       sinx /= r;
00088       cosx /= r;
00089     }
00090 
00091     static double tauScale(double u2) throw();
00092     static void tauCoeff(double u2, double c[]) throw();
00093     static void sigCoeff(double u2, double c[]) throw();
00094     static double dlamScale(double f, double mu) throw();
00095     static void dlamCoeff(double f, double mu, double e[]) throw();
00096     static double dlamScalemu(double f, double mu) throw();
00097     static void dlamCoeffmu(double f, double mu, double e[]) throw();
00098 
00099   public:
00100 
00101     /**
00102      * Constructor for a ellipsoid radius \e a (meters) and inverse flattening
00103      * \e invf.  Setting \e invf <= 0 implies \e invf = inf or flattening = 0
00104      * (i.e., a sphere).
00105      **********************************************************************/
00106     Geodesic(double a, double invf) throw();
00107 
00108     /**
00109      * Perform the direct geodesic calculation.  Given a latitude, \e lat1,
00110      * longitude, \e lon1, and azimuth \e azi1 (in degrees) for point 1 and a
00111      * range, \e s12 (in meters) from point 1 to point 2, return the latitude,
00112      * \e lat2, longitude, \e lon2, and forward azimuth, \e azi2 (in degees)
00113      * for point 2.
00114      **********************************************************************/
00115     void Direct(double lat1, double lon1, double azi1, double s12,
00116                 double& lat2, double& lon2, double& azi2) const throw();
00117 
00118     /**
00119      * Set up to do a series of ranges.  This returns a GeodesicLine object
00120      * with point 1 given by latitude, \e lat1, longitude, \e lon1, and azimuth
00121      * \e azi1 (in degrees).  Calls to GeodesicLine::Position return the
00122      * position and azimuth for point 2 a specified distance away.  Using
00123      * GeodesicLine::Position is approximately 2.5 faster than calling
00124      * Geodesic::Direct.
00125      **********************************************************************/
00126     GeodesicLine Line(double lat1, double lon1, double azi1) const throw();
00127 
00128     /**
00129      * Perform the inverse geodesic calculation.  Given a latitude, \e lat1,
00130      * longitude, \e lon1, for point 1 and a latitude, \e lat2, longitude, \e
00131      * lon2, for point 2 (all in degrees), return the geodesic distance, \e s12
00132      * (in meters), and the forward azimuths, \e azi1 and \e azi2 (in
00133      * degrees), at points 1 and 2.
00134      **********************************************************************/
00135     void Inverse(double lat1, double lon1, double lat2, double lon2,
00136                  double& s12, double& azi1, double& azi2) const throw();
00137 
00138 
00139     /**
00140      * A global instantiation of Geodesic with the parameters for the WGS84
00141      * ellipsoid.
00142      **********************************************************************/
00143     const static Geodesic WGS84;
00144   };
00145 
00146   /**
00147    * \brief A geodesic line.
00148    *
00149    * GeodesicLine facilitates the determination of a series of points on a
00150    * single geodesic.  Geodesic.Line returns a GeodesicLine object with the
00151    * geodesic defined by by \e lat1, \e lon1, and \e azi1.
00152    * GeodesicLine.Position returns the \e lat2, \e lon2, and \e azi2 given \e
00153    * s12.  An example of use of this class is:
00154    \verbatim
00155    // Print positions on a geodesic going through latitude 30,
00156    // longitude 10 at azimuth 80.  Points at intervals of 10km
00157    // in the range [-1000km, 1000km] are given.
00158    GeodesicLine line(Geodesic::WGS84.Line(30.0, 10.0, 80.0));
00159    double step = 10e3;
00160    for (int s = -100; s <= 100; ++s) {
00161      double lat2, lon2, azi2;
00162      double s12 = s * step;
00163      line.Position(s12, lat2, lon2, azi2);
00164      cout << s12 << " " << lat2 << " " << lon2 << " " << azi2 << "\n";
00165    }
00166    \endverbatim
00167    * The default copy constructor and assignment operators work with this
00168    * class, so that, for example, the previous example could start
00169    \verbatim
00170    GeodesicLine line;
00171    line = Geodesic::WGS84.Line(30.0, 10.0, 80.0);
00172    ...
00173    \endverbatim
00174    * Similarly, a vector can be used to hold GeodesicLine objects.
00175    *
00176    * The calculations are accurate to better than 12 nm.  (See \ref geoderrors
00177    * for details.)
00178    **********************************************************************/
00179 
00180   class GeodesicLine {
00181   private:
00182     friend class Geodesic;
00183     static const int maxpow = 8;
00184 
00185     int _bsign;
00186     double _lat1, _lon1, _azi1;
00187     double  _f1, _salp0, _calp0,
00188       _ssig1, _csig1, _stau1, _ctau1, _slam1, _clam1,
00189       _sScale, _dlamScale, _dtau1, _dchi1;
00190     double _sigCoeff[maxpow], _dlamCoeff[maxpow];
00191 
00192     GeodesicLine(const Geodesic& g, double lat1, double lon1, double azi1)
00193       throw();
00194   public:
00195 
00196     /**
00197      * A default constructor.  If GeodesicLine::Position is called on the
00198      * resulting object, it returns immediately (without doing any
00199      * calculations).  The object should be set with a call to Geodesic::Line.
00200      * Use Init() to test whether object is still in this uninitialized state.
00201      **********************************************************************/
00202     GeodesicLine() throw() : _sScale(0) {};
00203 
00204     /**
00205      * Return the latitude, \e lat2, longitude, \e lon2, and forward azimuth,
00206      * \e azi2 (in degrees) of the point 2 which is a distance, \e s12
00207      * (meters), from point 1.  \e s12 can be signed.
00208      **********************************************************************/
00209     void Position(double s12, double& lat2, double& lon2, double& azi2)
00210       const throw();
00211 
00212     /**
00213      * Has this object been initialize so that Position can be called?
00214      **********************************************************************/
00215     bool Init() const throw() { return _sScale > 0; }
00216 
00217     /**
00218      * Return the latitude of point 1 (in degrees).
00219      **********************************************************************/
00220     double Latitude() const throw() { return _lat1; }
00221 
00222     /**
00223      * Return the longitude of point 1 (in degrees).
00224      **********************************************************************/
00225     double Longitude() const throw() { return _lon1; }
00226 
00227     /**
00228      * Return the azimuth of the geodesic line as it passes through point 1.
00229      **********************************************************************/
00230     double Azimuth() const throw() { return _bsign * _azi1; }
00231   };
00232 
00233 } //namespace GeographicLib
00234 #endif