SGGeodesy.cxx

00001 // Copyright (C) 2006  Mathias Froehlich - Mathias.Froehlich@web.de
00002 //
00003 // This library is free software; you can redistribute it and/or
00004 // modify it under the terms of the GNU Library General Public
00005 // License as published by the Free Software Foundation; either
00006 // version 2 of the License, or (at your option) any later version.
00007 //
00008 // This library is distributed in the hope that it will be useful,
00009 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011 // Library General Public License for more details.
00012 //
00013 // You should have received a copy of the GNU General Public License
00014 // along with this program; if not, write to the Free Software
00015 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00016 //
00017 
00018 #ifdef HAVE_CONFIG_H
00019 #  include <simgear_config.h>
00020 #endif
00021 
00022 #include <cmath>
00023 
00024 #include "SGMath.hxx"
00025 
00026 // These are hard numbers from the WGS84 standard.  DON'T MODIFY
00027 // unless you want to change the datum.
00028 #define _EQURAD 6378137.0
00029 #define _FLATTENING 298.257223563
00030 
00031 // These are derived quantities more useful to the code:
00032 #if 0
00033 #define _SQUASH (1 - 1/_FLATTENING)
00034 #define _STRETCH (1/_SQUASH)
00035 #define _POLRAD (EQURAD * _SQUASH)
00036 #else
00037 // High-precision versions of the above produced with an arbitrary
00038 // precision calculator (the compiler might lose a few bits in the FPU
00039 // operations).  These are specified to 81 bits of mantissa, which is
00040 // higher than any FPU known to me:
00041 #define _SQUASH    0.9966471893352525192801545
00042 #define _STRETCH   1.0033640898209764189003079
00043 #define _POLRAD    6356752.3142451794975639668
00044 #endif
00045 
00046 // The constants from the WGS84 standard
00047 const double SGGeodesy::EQURAD = _EQURAD;
00048 const double SGGeodesy::iFLATTENING = _FLATTENING;
00049 const double SGGeodesy::SQUASH = _SQUASH;
00050 const double SGGeodesy::STRETCH = _STRETCH;
00051 const double SGGeodesy::POLRAD = _POLRAD;
00052 
00053 // additional derived and precomputable ones
00054 // for the geodetic conversion algorithm
00055 
00056 #define E2 fabs(1 - _SQUASH*_SQUASH)
00057 static double a = _EQURAD;
00058 static double ra2 = 1/(_EQURAD*_EQURAD);
00059 static double e = sqrt(E2);
00060 static double e2 = E2;
00061 static double e4 = E2*E2;
00062 
00063 #undef _EQURAD
00064 #undef _FLATTENING
00065 #undef _SQUASH
00066 #undef _STRETCH
00067 #undef _POLRAD
00068 #undef E2
00069 
00070 void
00071 SGGeodesy::SGCartToGeod(const SGVec3<double>& cart, SGGeod& geod)
00072 {
00073   // according to
00074   // H. Vermeille,
00075   // Direct transformation from geocentric to geodetic ccordinates,
00076   // Journal of Geodesy (2002) 76:451-454
00077   double X = cart(0);
00078   double Y = cart(1);
00079   double Z = cart(2);
00080   double XXpYY = X*X+Y*Y;
00081   double sqrtXXpYY = sqrt(XXpYY);
00082   double p = XXpYY*ra2;
00083   double q = Z*Z*(1-e2)*ra2;
00084   double r = 1/6.0*(p+q-e4);
00085   double s = e4*p*q/(4*r*r*r);
00086   double t = pow(1+s+sqrt(s*(2+s)), 1/3.0);
00087   double u = r*(1+t+1/t);
00088   double v = sqrt(u*u+e4*q);
00089   double w = e2*(u+v-q)/(2*v);
00090   double k = sqrt(u+v+w*w)-w;
00091   double D = k*sqrtXXpYY/(k+e2);
00092   geod.setLongitudeRad(2*atan2(Y, X+sqrtXXpYY));
00093   double sqrtDDpZZ = sqrt(D*D+Z*Z);
00094   geod.setLatitudeRad(2*atan2(Z, D+sqrtDDpZZ));
00095   geod.setElevationM((k+e2-1)*sqrtDDpZZ/k);
00096 }
00097 
00098 void
00099 SGGeodesy::SGGeodToCart(const SGGeod& geod, SGVec3<double>& cart)
00100 {
00101   // according to
00102   // H. Vermeille,
00103   // Direct transformation from geocentric to geodetic ccordinates,
00104   // Journal of Geodesy (2002) 76:451-454
00105   double lambda = geod.getLongitudeRad();
00106   double phi = geod.getLatitudeRad();
00107   double h = geod.getElevationM();
00108   double sphi = sin(phi);
00109   double n = a/sqrt(1-e2*sphi*sphi);
00110   double cphi = cos(phi);
00111   double slambda = sin(lambda);
00112   double clambda = cos(lambda);
00113   cart(0) = (h+n)*cphi*clambda;
00114   cart(1) = (h+n)*cphi*slambda;
00115   cart(2) = (h+n-e2*n)*sphi;
00116 }
00117 
00118 double
00119 SGGeodesy::SGGeodToSeaLevelRadius(const SGGeod& geod)
00120 {
00121   // this is just a simplified version of the SGGeodToCart function above,
00122   // substitute h = 0, take the 2-norm of the cartesian vector and simplify
00123   double phi = geod.getLatitudeRad();
00124   double sphi = sin(phi);
00125   double sphi2 = sphi*sphi;
00126   return a*sqrt((1 + (e4 - 2*e2)*sphi2)/(1 - e2*sphi2));
00127 }
00128 
00129 void
00130 SGGeodesy::SGCartToGeoc(const SGVec3<double>& cart, SGGeoc& geoc)
00131 {
00132   double minVal = SGLimits<double>::min();
00133   if (fabs(cart(0)) < minVal && fabs(cart(1)) < minVal)
00134     geoc.setLongitudeRad(0);
00135   else
00136     geoc.setLongitudeRad(atan2(cart(1), cart(0)));
00137 
00138   double nxy = sqrt(cart(0)*cart(0) + cart(1)*cart(1));
00139   if (fabs(nxy) < minVal && fabs(cart(2)) < minVal)
00140     geoc.setLatitudeRad(0);
00141   else
00142     geoc.setLatitudeRad(atan2(cart(2), nxy));
00143 
00144   geoc.setRadiusM(norm(cart));
00145 }
00146 
00147 void
00148 SGGeodesy::SGGeocToCart(const SGGeoc& geoc, SGVec3<double>& cart)
00149 {
00150   double lat = geoc.getLatitudeRad();
00151   double lon = geoc.getLongitudeRad();
00152   double slat = sin(lat);
00153   double clat = cos(lat);
00154   double slon = sin(lon);
00155   double clon = cos(lon);
00156   cart = geoc.getRadiusM()*SGVec3<double>(clat*clon, clat*slon, slat);
00157 }

Generated on Mon Dec 17 09:30:55 2007 for SimGear by  doxygen 1.5.1