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