MOOS 0.2375
/home/toby/moos-ivp/MOOS-2375-Oct0611/Essentials/MOOSUtilityLib/MOOSGeodesy.cpp
Go to the documentation of this file.
00001 
00002 //
00003 //   MOOS - Mission Oriented Operating Suite 
00004 //  
00005 //   A suit of Applications and Libraries for Mobile Robotics Research 
00006 //   Copyright (C) 2001-2005 Massachusetts Institute of Technology and 
00007 //   Oxford University. 
00008 //    
00009 //   This software was written by Paul Newman at MIT 2001-2002 and Oxford 
00010 //   University 2003-2005. email: pnewman@robots.ox.ac.uk. 
00011 //      
00012 //   This file is part of a  MOOS Core Component. 
00013 //        
00014 //   This program is free software; you can redistribute it and/or 
00015 //   modify it under the terms of the GNU General Public License as 
00016 //   published by the Free Software Foundation; either version 2 of the 
00017 //   License, or (at your option) any later version. 
00018 //          
00019 //   This program is distributed in the hope that it will be useful, 
00020 //   but WITHOUT ANY WARRANTY; without even the implied warranty of 
00021 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
00022 //   General Public License for more details. 
00023 //            
00024 //   You should have received a copy of the GNU General Public License 
00025 //   along with this program; if not, write to the Free Software 
00026 //   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 
00027 //   02111-1307, USA. 
00028 //
00030 // MOOSGeodesy.cpp: implementation of the CMOOSGeodesy class.
00031 //
00033 
00034 #include "MOOSGeodesy.h"
00035 #include <math.h>
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038 #include <cstring>
00039 
00040 #ifdef _WIN32
00041 #include <float.h>
00042 #define isnan _isnan
00043 #endif
00044 
00045 
00046 
00047 /*Reference ellipsoids derived from Peter H. Dana's website- 
00048 http://www.utexas.edu/depts/grg/gcraft/notes/datum/elist.html
00049 Department of Geography, University of Texas at Austin
00050 Internet: pdana@mail.utexas.edu
00051 3/22/95
00052 
00053 Source
00054 Defense Mapping Agency. 1987b. DMA Technical Report: Supplement to Department of Defense World Geodetic System
00055 1984 Technical Report. Part I and II. Washington, DC: Defense Mapping Agency
00056 */
00057 
00059 // Construction/Destruction
00061 
00062 CMOOSGeodesy::CMOOSGeodesy()
00063 {
00064     //create the variables by Zeroing
00065     SetMetersNorth(0.0);
00066     SetMetersEast(0.0);
00067     SetLocalGridX(0.0);
00068     SetLocalGridY(0.0);
00069     SetOriginNorthing(0.0);
00070     SetOriginEasting(0.0);
00071     SetOriginLongitude(0.0);
00072     SetOriginLatitude(0.0);
00073     
00074     // Initialize member variables (mikerb 3/17/11)
00075     m_iRefEllipsoid    = 23;
00076     m_dOriginEasting   = 0;
00077     m_dOriginNorthing  = 0;
00078     m_dEast            = 0;
00079     m_dNorth           = 0;    
00080     m_dOriginLongitude = 0;
00081     m_dOriginLatitude  = 0;
00082     m_dLocalGridX      = 0;
00083     m_dLocalGridY      = 0;
00084 }
00085 
00086 CMOOSGeodesy::~CMOOSGeodesy()
00087 {
00088 
00089 }
00090 
00104 bool CMOOSGeodesy::Initialise(double lat, double lon)
00105 {
00106     //We will use the WGS-84 standard Reference Ellipsoid
00107     //This can be modified by a client if they desire with 
00108     //@see MOOSGeodesy.h
00109     SetRefEllipsoid(23);
00110 
00111     //Set the Origin of the local Grid Coordinate System
00112     SetOriginLatitude(lat);
00113     SetOriginLongitude(lon);
00114 
00115     //Translate the Origin coordinates into Northings and Eastings 
00116     double tempNorth = 0.0, tempEast = 0.0;
00117     char utmZone[4];
00118     LLtoUTM(m_iRefEllipsoid, m_dOriginLatitude, 
00119             m_dOriginLongitude, tempNorth, 
00120             tempEast, utmZone);
00121 
00122     //Then set the Origin for the Northing/Easting coordinate frame
00123     //Also make a note of the UTM Zone we are operating in
00124     SetOriginNorthing(tempNorth);
00125     SetOriginEasting(tempEast);
00126     SetUTMZone(utmZone);
00127 
00128     //We set this flag to indicate that the first calculation of distance
00129     //traveled is with respect to the origin coordinates.
00130     m_bSTEP_AFTER_INIT = true;
00131 
00132     return true;
00133 }
00134 
00135 double CMOOSGeodesy::GetOriginLongitude()
00136 {
00137     return m_dOriginLongitude;
00138 }
00139 
00140 double CMOOSGeodesy::GetOriginLatitude()
00141 {
00142     return m_dOriginLatitude;    
00143 }
00144 
00145 void CMOOSGeodesy::SetOriginLongitude(double lon)
00146 {
00147     m_dOriginLongitude = lon;
00148 }
00149 
00150 void CMOOSGeodesy::SetOriginLatitude(double lat)
00151 {
00152     m_dOriginLatitude = lat;
00153 }
00154 
00155 
00156 double CMOOSGeodesy::GetMetersNorth()
00157 {
00158     return m_dNorth;
00159 }
00160 
00161 double CMOOSGeodesy::GetMetersEast()
00162 {
00163     return m_dEast;
00164 }
00165 
00166 
00167 bool CMOOSGeodesy::LLtoUTM(int ReferenceEllipsoid, const double Lat, 
00168                             const double Long, double &UTMNorthing, 
00169                             double &UTMEasting, char *UTMZone)
00170 {
00171     //converts lat/long to UTM coords.  Equations from USGS Bulletin 1532 
00172     //East Longitudes are positive, West longitudes are negative. 
00173     //North latitudes are positive, South latitudes are negative
00174     //Lat and Long are in decimal degrees
00175     //Written by Chuck Gantz- chuck.gantz@globalstar.com
00176 
00177     double a = ellipsoid[ReferenceEllipsoid].EquatorialRadius;
00178     double eccSquared = ellipsoid[ReferenceEllipsoid].eccentricitySquared;
00179     double k0 = 0.9996;
00180 
00181     double LongOrigin;
00182     double eccPrimeSquared;
00183     double N, T, C, A, M;
00184     
00185     //Make sure the longitude is between -180.00 .. 179.9
00186     double LongTemp = (Long+180)-int((Long+180)/360)*360-180; // -180.00 .. 179.9;
00187 
00188     double LatRad = Lat*deg2rad;
00189     double LongRad = LongTemp*deg2rad;
00190     double LongOriginRad;
00191     int    ZoneNumber;
00192 
00193     ZoneNumber = int((LongTemp + 180)/6) + 1;
00194   
00195     if( Lat >= 56.0 && Lat < 64.0 && LongTemp >= 3.0 && LongTemp < 12.0 )
00196         ZoneNumber = 32;
00197 
00198     // Special zones for Svalbard
00199     if( Lat >= 72.0 && Lat < 84.0 ) 
00200     {
00201       if(      LongTemp >= 0.0  && LongTemp <  9.0 ) ZoneNumber = 31;
00202       else if( LongTemp >= 9.0  && LongTemp < 21.0 ) ZoneNumber = 33;
00203       else if( LongTemp >= 21.0 && LongTemp < 33.0 ) ZoneNumber = 35;
00204       else if( LongTemp >= 33.0 && LongTemp < 42.0 ) ZoneNumber = 37;
00205      }
00206     LongOrigin = (ZoneNumber - 1)*6 - 180 + 3;  //+3 puts origin in middle of zone
00207     LongOriginRad = LongOrigin * deg2rad;
00208 
00209     //compute the UTM Zone from the latitude and longitude
00210     if(UTMZone) // mikerb
00211       sprintf(UTMZone, "%d%c", ZoneNumber, UTMLetterDesignator(Lat));
00212 
00213     eccPrimeSquared = (eccSquared)/(1-eccSquared);
00214 
00215     N = a/sqrt(1-eccSquared*sin(LatRad)*sin(LatRad));
00216     T = tan(LatRad)*tan(LatRad);
00217     C = eccPrimeSquared*cos(LatRad)*cos(LatRad);
00218     A = cos(LatRad)*(LongRad-LongOriginRad);
00219 
00220     M = a*((1    - eccSquared/4        - 3*eccSquared*eccSquared/64    - 5*eccSquared*eccSquared*eccSquared/256)*LatRad 
00221                 - (3*eccSquared/8    + 3*eccSquared*eccSquared/32    + 45*eccSquared*eccSquared*eccSquared/1024)*sin(2*LatRad)
00222                                     + (15*eccSquared*eccSquared/256 + 45*eccSquared*eccSquared*eccSquared/1024)*sin(4*LatRad) 
00223                                     - (35*eccSquared*eccSquared*eccSquared/3072)*sin(6*LatRad));
00224     
00225     UTMEasting = (double)(k0*N*(A+(1-T+C)*A*A*A/6
00226                     + (5-18*T+T*T+72*C-58*eccPrimeSquared)*A*A*A*A*A/120)
00227                     + 500000.0);
00228 
00229     UTMNorthing = (double)(k0*(M+N*tan(LatRad)*(A*A/2+(5-T+9*C+4*C*C)*A*A*A*A/24
00230                  + (61-58*T+T*T+600*C-330*eccPrimeSquared)*A*A*A*A*A*A/720)));
00231     if(Lat < 0)
00232         UTMNorthing += 10000000.0; //10000000 meter offset for southern hemisphere
00233 
00234     return true;
00235 }
00236 
00237 
00238 
00239 char CMOOSGeodesy::UTMLetterDesignator(double Lat)
00240 {
00241     //This routine determines the correct UTM letter designator for the given latitude
00242     //returns 'Z' if latitude is outside the UTM limits of 84N to 80S
00243     //Written by Chuck Gantz- chuck.gantz@globalstar.com
00244     char LetterDesignator;
00245 
00246     if((84 >= Lat) && (Lat >= 72)) LetterDesignator = 'X';
00247     else if((72 > Lat) && (Lat >= 64)) LetterDesignator = 'W';
00248     else if((64 > Lat) && (Lat >= 56)) LetterDesignator = 'V';
00249     else if((56 > Lat) && (Lat >= 48)) LetterDesignator = 'U';
00250     else if((48 > Lat) && (Lat >= 40)) LetterDesignator = 'T';
00251     else if((40 > Lat) && (Lat >= 32)) LetterDesignator = 'S';
00252     else if((32 > Lat) && (Lat >= 24)) LetterDesignator = 'R';
00253     else if((24 > Lat) && (Lat >= 16)) LetterDesignator = 'Q';
00254     else if((16 > Lat) && (Lat >= 8)) LetterDesignator = 'P';
00255     else if(( 8 > Lat) && (Lat >= 0)) LetterDesignator = 'N';
00256     else if(( 0 > Lat) && (Lat >= -8)) LetterDesignator = 'M';
00257     else if((-8> Lat) && (Lat >= -16)) LetterDesignator = 'L';
00258     else if((-16 > Lat) && (Lat >= -24)) LetterDesignator = 'K';
00259     else if((-24 > Lat) && (Lat >= -32)) LetterDesignator = 'J';
00260     else if((-32 > Lat) && (Lat >= -40)) LetterDesignator = 'H';
00261     else if((-40 > Lat) && (Lat >= -48)) LetterDesignator = 'G';
00262     else if((-48 > Lat) && (Lat >= -56)) LetterDesignator = 'F';
00263     else if((-56 > Lat) && (Lat >= -64)) LetterDesignator = 'E';
00264     else if((-64 > Lat) && (Lat >= -72)) LetterDesignator = 'D';
00265     else if((-72 > Lat) && (Lat >= -80)) LetterDesignator = 'C';
00266     else LetterDesignator = 'Z'; //This is here as an error flag to show that the Latitude is outside the UTM limits
00267 
00268     return LetterDesignator;
00269 }
00270 
00271 void CMOOSGeodesy::SetMetersNorth(double North)
00272 {
00273     m_dNorth = North;
00274 }
00275 
00276 void CMOOSGeodesy::SetMetersEast(double East)
00277 {
00278     m_dEast = East;
00279 }
00280 
00281 void CMOOSGeodesy::SetOriginNorthing(double North)
00282 {
00283     m_dOriginNorthing = North;
00284 }
00285 
00286 void CMOOSGeodesy::SetOriginEasting(double East)
00287 {
00288     m_dOriginEasting = East;
00289 }
00290 
00291 int CMOOSGeodesy::GetRefEllipsoid()
00292 {
00293     return m_iRefEllipsoid;
00294 }
00295 
00296 void CMOOSGeodesy::SetRefEllipsoid(int refEllipsoid)
00297 {
00298     m_iRefEllipsoid = refEllipsoid;
00299 }
00300 
00301 void CMOOSGeodesy::SetUTMZone(const char * utmZone)
00302 {
00303     // Better hope the input string is no longer than 4...
00304     strcpy(m_sUTMZone, utmZone);
00305 }
00306 
00307 char * CMOOSGeodesy::GetUTMZone()
00308 {
00309     return m_sUTMZone;
00310 }
00311 
00334 bool CMOOSGeodesy::LatLong2LocalUTM(double lat,
00335                                     double lon, 
00336                                     double &MetersNorth,
00337                                     double &MetersEast)
00338 {
00339     //first turn the lat/lon into UTM
00340     double tmpNorth = 0.0, tmpEast = 0.0;
00341     double dN = 0.0, dE = 0.0; 
00342     char tmpUTM[4];
00343 
00344     // LLtoUTM(m_iRefEllipsoid,lat,lon,tmpNorth,tmpEast,tmpUTM);
00345     LLtoUTM(m_iRefEllipsoid,lat,lon,tmpNorth,tmpEast,0); // mikerb
00346 
00347     //could check for the UTMZone differing, and if so, return false
00348 
00349     //If this is the first time through the loop,then
00350     //compare the returned Northing & Easting values with the origin.
00351     //This does not need to be split like into before and after first reading, but
00352     //makes the calculation clearer.  Plus, can add other features like logging
00353     //or publishing of value
00354     if(m_bSTEP_AFTER_INIT){
00355         dN = tmpNorth - GetOriginNorthing();
00356         dE = tmpEast - GetOriginEasting();
00357         SetMetersNorth(dN);
00358         SetMetersEast(dE);
00359 
00360         m_bSTEP_AFTER_INIT = !m_bSTEP_AFTER_INIT;
00361     }else{
00362         double totalNorth = tmpNorth - GetOriginNorthing();
00363         dN = totalNorth - GetMetersNorth();
00364         //add the increment to the current North value
00365         SetMetersNorth(dN + GetMetersNorth());
00366 
00367         double totalEast = tmpEast - GetOriginEasting();
00368         dE = totalEast - GetMetersEast();
00369         //add the increment to the current East value
00370         SetMetersEast(dE + GetMetersEast());
00371     }
00372 
00373     //This is the total distance traveled thus far, either North or East
00374     MetersNorth = GetMetersNorth();
00375     MetersEast = GetMetersEast();
00376     
00377     return true;
00378 }
00379 
00380 double CMOOSGeodesy::GetOriginEasting()
00381 {
00382     return m_dOriginEasting;
00383 }
00384 
00385 double CMOOSGeodesy::GetOriginNorthing()
00386 {
00387     return m_dOriginNorthing;
00388 }
00389 
00390 double CMOOSGeodesy::DMS2DecDeg(double dfVal)
00391 {
00392     int nDeg = (int)(dfVal/100.0);
00393 
00394     double dfTmpDeg = (100.0*(dfVal/100.0-nDeg))/60.0;
00395     
00396     return  dfTmpDeg+nDeg;
00397 
00398 
00399 }
00400 
00401 bool CMOOSGeodesy::LatLong2LocalGrid(double lat, 
00402                                      double lon, 
00403                                      double &MetersNorth, 
00404                                      double &MetersEast)
00405 {
00406     
00407     //(semimajor axis)
00408     double dfa  = 6378137;
00409     // (semiminor axis)
00410     double dfb = 6356752;
00411 
00412     double dftanlat2 = pow(tan(lat*deg2rad),2);
00413     double dfRadius = dfb*sqrt(1+dftanlat2) / sqrt( ( pow(dfb,2)/pow(dfa,2) )+dftanlat2);
00414 
00415 
00416 
00417     //the decimal degrees conversion should take place elsewhere
00418     double dXArcDeg  = (lon - GetOriginLongitude()) * deg2rad;
00419     double dX = dfRadius * sin(dXArcDeg)*cos(lat*deg2rad);
00420     
00421     double dYArcDeg  = (lat - GetOriginLatitude()) * deg2rad;
00422     double dY = dfRadius * sin(dYArcDeg);
00423 
00424     SetLocalGridX(dX);
00425     SetLocalGridY(dY);
00426 
00427     //This is the total distance traveled thus far, either North or East
00428     MetersNorth = GetLocalGridY();
00429     MetersEast  = GetLocalGridX();
00430     
00431     return true;
00432 }
00433 
00434 void CMOOSGeodesy::SetLocalGridX(double X)
00435 {
00436     m_dLocalGridX = X;
00437 }
00438 
00439 void CMOOSGeodesy::SetLocalGridY(double Y)
00440 {
00441     m_dLocalGridY = Y;
00442 }
00443 
00444 double CMOOSGeodesy::GetLocalGridX()
00445 {
00446     return m_dLocalGridX;
00447 }
00448 
00449 double CMOOSGeodesy::GetLocalGridY()
00450 {
00451     return m_dLocalGridY;
00452 }
00453 
00464 bool CMOOSGeodesy::LocalGrid2LatLong(double dfEast, double dfNorth, double &dfLat, double &dfLon) 
00465 {
00466     //(semimajor axis)
00467     double dfa  = 6378137;
00468     // (semiminor axis)
00469     double dfb = 6356752;
00470 
00471     double dftanlat2 = pow( tan( GetOriginLatitude()*deg2rad ), 2 );
00472     double dfRadius = dfb*sqrt( 1+dftanlat2 ) / sqrt( ( pow(dfb,2)/pow(dfa,2) )+dftanlat2 );
00473 
00474     //first calculate lat arc
00475     double dfYArcRad = asin( dfNorth/dfRadius ); //returns result in rad
00476     double dfYArcDeg = dfYArcRad * rad2deg;
00477  
00478     double dfXArcRad = asin( dfEast/( dfRadius*cos( GetOriginLatitude()*deg2rad ) ) );
00479     double dfXArcDeg = dfXArcRad * rad2deg;
00480         
00481     //add the origin to these arc lengths
00482     dfLat = dfYArcDeg + GetOriginLatitude();
00483        dfLon = dfXArcDeg + GetOriginLongitude();
00484 
00485     return true;
00486 }
00487 
00488 
00489 bool CMOOSGeodesy::UTM2LatLong(double dfX, double dfY, double& dfLat, double& dfLong)
00490 {
00491     //written by Henrik Schmidt henrik@mit.edu
00492     
00493     double err = 1e20;
00494     double dfx=dfX;
00495     double dfy=dfY;
00496     double eps = 1.0; // accuracy in m
00497     
00498     while (err > eps)
00499     {
00500         double dflat, dflon, dfnew_x, dfnew_y ;
00501 
00502         // first guess geodesic
00503         if (!LocalGrid2LatLong(dfx,dfy,dflat,dflon))
00504             return(false);
00505         
00506         // now convert latlong to UTM
00507         if (!LatLong2LocalUTM(dflat,dflon,dfnew_y,dfnew_x))
00508             return(false);
00509         
00510                 // fix to segfault issue if you get diverging values
00511                 if(isnan(dflat) || isnan(dflon))
00512                 {
00513                         dflat = 91;
00514                         dflon = 181;
00515                         return(false);
00516                 }
00517                 
00518         // how different
00519         double dfdiff_x = dfnew_x -dfX;
00520         double dfdiff_y = dfnew_y -dfY;
00521         
00522         // subtract difference and reconvert        
00523         dfx -= dfdiff_x;
00524         dfy -= dfdiff_y;
00525         
00526         err = hypot(dfnew_x-dfX,dfnew_y-dfY);
00527         
00528         //MOOSTrace("UTM2LatLong: error = %f\n",err); 
00529     }
00530     
00531     if (!LocalGrid2LatLong(dfx, dfy, dfLat, dfLong))
00532         return(false);
00533     
00534     
00535         return true;
00536 }
00537 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines