MOOS 0.2375
|
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