00001 #pragma ident "$Id: Position.cpp 669 2007-07-05 16:52:08Z pben $"
00002
00003
00004
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "Position.hpp"
00037 #include "WGS84Geoid.hpp"
00038 #include "icd_200_constants.hpp"
00039 #include "geometry.hpp"
00040 #include "MiscMath.hpp"
00041
00042 namespace gpstk
00043 {
00044
00045 using namespace std;
00046 using namespace StringUtils;
00047
00048
00049
00050 static const char *SystemNames[] = {
00051 "Unknown",
00052 "Geodetic",
00053 "Geocentric",
00054 "Cartesian",
00055 "Spherical"};
00056
00057
00058 string Position::getSystemName()
00059 throw()
00060 { return SystemNames[system]; }
00061
00062
00063
00064 const double Position::ONE_MM_TOLERANCE = 0.001;
00065
00066 const double Position::ONE_CM_TOLERANCE = 0.01;
00067
00068 const double Position::ONE_UM_TOLERANCE = 0.000001;
00069
00070
00071 double Position::POSITION_TOLERANCE = Position::ONE_MM_TOLERANCE;
00072
00073
00074
00075
00076
00077 Position& Position::setTolerance(const double tol)
00078 throw()
00079 {
00080 tolerance = tol;
00081 return *this;
00082 }
00083
00084
00085
00086
00087 Position::Position()
00088 throw()
00089 {
00090 WGS84Geoid WGS84;
00091 initialize(0.0,0.0,0.0,Unknown,&WGS84);
00092 }
00093
00094 Position::Position(const double& a,
00095 const double& b,
00096 const double& c,
00097 Position::CoordinateSystem s,
00098 GeoidModel *geoid)
00099 throw(GeometryException)
00100 {
00101 try {
00102 initialize(a,b,c,s,geoid);
00103 }
00104 catch(GeometryException& ge) {
00105 GPSTK_RETHROW(ge);
00106 }
00107 }
00108
00109 Position::Position(const double ABC[3],
00110 CoordinateSystem s,
00111 GeoidModel *geoid)
00112 throw(GeometryException)
00113 {
00114 double a=ABC[0];
00115 double b=ABC[1];
00116 double c=ABC[2];
00117 try {
00118 initialize(a,b,c,s,geoid);
00119 }
00120 catch(GeometryException& ge) {
00121 GPSTK_RETHROW(ge);
00122 }
00123 }
00124
00125 Position::Position(const Triple& ABC,
00126 CoordinateSystem s,
00127 GeoidModel *geoid)
00128 throw(GeometryException)
00129 {
00130 double a=ABC[0];
00131 double b=ABC[1];
00132 double c=ABC[2];
00133 try {
00134 initialize(a,b,c,s,geoid);
00135 }
00136 catch(GeometryException& ge) {
00137 GPSTK_RETHROW(ge);
00138 }
00139 }
00140
00141 Position::Position(const Xvt& xvt)
00142 throw()
00143 {
00144 double a=xvt.x[0];
00145 double b=xvt.x[1];
00146 double c=xvt.x[2];
00147 initialize(a,b,c,Cartesian);
00148 }
00149
00150
00151
00152 Position& Position::operator-=(const Position& right)
00153 throw()
00154 {
00155 Position r(right);
00156 CoordinateSystem savesys=system;
00157
00158
00159 transformTo(Cartesian);
00160 r.transformTo(Cartesian);
00161
00162 for(int i=0; i<3; i++)
00163 theArray[i] -= r.theArray[i];
00164
00165 transformTo(savesys);
00166 return *this;
00167 }
00168
00169 Position& Position::operator+=(const Position& right)
00170 throw()
00171 {
00172 Position r(right);
00173 CoordinateSystem savesys=system;
00174
00175
00176 transformTo(Cartesian);
00177 r.transformTo(Cartesian);
00178
00179 for(int i=0; i<3; i++)
00180 theArray[i] += r.theArray[i];
00181
00182 transformTo(savesys);
00183 return *this;
00184 }
00185
00186 Position operator-(const Position& left,
00187 const Position& right)
00188 throw()
00189 {
00190 Position l(left),r(right);
00191
00192 l.transformTo(Position::Cartesian);
00193 r.transformTo(Position::Cartesian);
00194
00195 l -= r;
00196
00197 return l;
00198 }
00199
00200 Position operator+(const Position& left,
00201 const Position& right)
00202 throw()
00203 {
00204 Position l(left),r(right);
00205
00206 l.transformTo(Position::Cartesian);
00207 r.transformTo(Position::Cartesian);
00208
00209 l += r;
00210
00211 return l;
00212 }
00213
00214
00215
00216
00217 bool Position::operator==(const Position &right) const
00218 throw()
00219 {
00220 if(AEarth != right.AEarth || eccSquared != right.eccSquared)
00221 return false;
00222 if(range(*this,right) < tolerance)
00223 return true;
00224 else
00225 return false;
00226 }
00227
00228
00229 bool Position::operator!=(const Position &right) const
00230 throw()
00231 {
00232 return !(operator==(right));
00233 }
00234
00235
00236
00237
00238
00239
00240
00241 Position Position::transformTo(CoordinateSystem sys)
00242 throw()
00243 {
00244 if(sys == Unknown || sys == system) return *this;
00245
00246
00247 Position target(*this);
00248
00249
00250 switch(system) {
00251 case Unknown:
00252 return *this;
00253 case Geodetic:
00254
00255 switch(sys) {
00256 case Unknown: case Geodetic: return *this;
00257 case Geocentric:
00258 convertGeodeticToGeocentric(*this,target,AEarth,eccSquared);
00259 target.system = Geocentric;
00260 break;
00261 case Cartesian:
00262 convertGeodeticToCartesian(*this,target,AEarth,eccSquared);
00263 target.system = Cartesian;
00264 break;
00265 case Spherical:
00266 convertGeodeticToGeocentric(*this,target,AEarth,eccSquared);
00267 target.theArray[0] = 90 - target.theArray[0];
00268 target.system = Spherical;
00269 break;
00270 }
00271 break;
00272 case Geocentric:
00273
00274 switch(sys) {
00275 case Unknown: case Geocentric: return *this;
00276 case Geodetic:
00277 convertGeocentricToGeodetic(*this,target,AEarth,eccSquared);
00278 target.system = Geodetic;
00279 break;
00280 case Cartesian:
00281 convertGeocentricToCartesian(*this,target);
00282 target.system = Cartesian;
00283 break;
00284 case Spherical:
00285 target.theArray[0] = 90 - target.theArray[0];
00286 target.system = Spherical;
00287 break;
00288 }
00289 break;
00290 case Cartesian:
00291
00292 switch(sys) {
00293 case Unknown: case Cartesian: return *this;
00294 case Geodetic:
00295 convertCartesianToGeodetic(*this,target,AEarth,eccSquared);
00296 target.system = Geodetic;
00297 break;
00298 case Geocentric:
00299 convertCartesianToGeocentric(*this,target);
00300 target.system = Geocentric;
00301 break;
00302 case Spherical:
00303 convertCartesianToSpherical(*this,target);
00304 target.system = Spherical;
00305 break;
00306 }
00307 break;
00308 case Spherical:
00309
00310 switch(sys) {
00311 case Unknown: case Spherical: return *this;
00312 case Geodetic:
00313 theArray[0] = 90 - theArray[0];
00314 convertGeocentricToGeodetic(*this,target,AEarth,eccSquared);
00315 target.system = Geodetic;
00316 break;
00317 case Geocentric:
00318 target.theArray[0] = 90 - target.theArray[0];
00319 target.system = Geocentric;
00320 break;
00321 case Cartesian:
00322 convertSphericalToCartesian(*this,target);
00323 target.system = Cartesian;
00324 break;
00325 }
00326 break;
00327 }
00328
00329 *this = target;
00330 return *this;
00331 }
00332
00333
00334
00335
00336
00337
00338
00339
00340 double Position::X() const
00341 throw()
00342 {
00343 if(system == Cartesian)
00344 return theArray[0];
00345 Position t(*this);
00346 t.transformTo(Cartesian);
00347 return t.theArray[0];
00348 }
00349
00350
00351 double Position::Y() const
00352 throw()
00353 {
00354 if(system == Cartesian)
00355 return theArray[1];
00356 Position t(*this);
00357 t.transformTo(Cartesian);
00358 return t.theArray[1];
00359 }
00360
00361
00362 double Position::Z() const
00363 throw()
00364 {
00365 if(system == Cartesian)
00366 return theArray[2];
00367 Position t(*this);
00368 t.transformTo(Cartesian);
00369 return t.theArray[2];
00370 }
00371
00372
00373 double Position::geodeticLatitude() const
00374 throw()
00375 {
00376 if(system == Geodetic)
00377 return theArray[0];
00378 Position t(*this);
00379 t.transformTo(Geodetic);
00380 return t.theArray[0];
00381 }
00382
00383
00384
00385 double Position::geocentricLatitude() const
00386 throw()
00387 {
00388 if(system == Geocentric)
00389 return theArray[0];
00390 Position t(*this);
00391 t.transformTo(Geocentric);
00392 return t.theArray[0];
00393 }
00394
00395
00396 double Position::theta() const
00397 throw()
00398 {
00399 if(system == Spherical)
00400 return theArray[0];
00401 Position t(*this);
00402 t.transformTo(Spherical);
00403 return t.theArray[0];
00404 }
00405
00406
00407 double Position::phi() const
00408 throw()
00409 {
00410 if(system == Spherical)
00411 return theArray[1];
00412 Position t(*this);
00413 t.transformTo(Spherical);
00414 return t.theArray[1];
00415 }
00416
00417
00418
00419 double Position::longitude() const
00420 throw()
00421 {
00422 if(system != Cartesian)
00423 return theArray[1];
00424 Position t(*this);
00425 t.transformTo(Spherical);
00426 return t.theArray[1];
00427 }
00428
00429
00430
00431 double Position::radius() const
00432 throw()
00433 {
00434 if(system == Spherical || system == Geocentric)
00435 return theArray[2];
00436 Position t(*this);
00437 t.transformTo(Spherical);
00438 return t.theArray[2];
00439 }
00440
00441
00442 double Position::height() const
00443 throw()
00444 {
00445 if(system == Geodetic)
00446 return theArray[2];
00447 Position t(*this);
00448 t.transformTo(Geodetic);
00449 return t.theArray[2];
00450 }
00451
00452
00453
00454
00455
00456
00457 void Position::setGeoidModel(const GeoidModel *geoid)
00458 throw(GeometryException)
00459 {
00460 if(!geoid)
00461 {
00462 GeometryException ge("Given GeoidModel pointer is NULL.");
00463 GPSTK_THROW(ge);
00464 }
00465 AEarth = geoid->a();
00466 eccSquared = geoid->eccSquared();
00467 }
00468
00469
00470
00471
00472
00473
00474
00475 Position& Position::setGeodetic(const double lat,
00476 const double lon,
00477 const double ht,
00478 const GeoidModel *geoid)
00479 throw(GeometryException)
00480 {
00481 if(lat > 90 || lat < -90)
00482 {
00483 GeometryException ge("Invalid latitude in setGeodetic: "
00484 + StringUtils::asString(lat));
00485 GPSTK_THROW(ge);
00486 }
00487 theArray[0] = lat;
00488
00489 theArray[1] = lon;
00490 if(theArray[1] < 0)
00491 theArray[1] += 360*(1+(unsigned long)(theArray[1]/360));
00492 else if(theArray[1] >= 360)
00493 theArray[1] -= 360*(unsigned long)(theArray[1]/360);
00494
00495 theArray[2] = ht;
00496
00497 if(geoid) {
00498 AEarth = geoid->a();
00499 eccSquared = geoid->eccSquared();
00500 }
00501 system = Geodetic;
00502
00503 return *this;
00504 }
00505
00506
00507
00508
00509
00510
00511
00512 Position& Position::setGeocentric(const double lat,
00513 const double lon,
00514 const double rad)
00515 throw(GeometryException)
00516 {
00517 if(lat > 90 || lat < -90)
00518 {
00519 GeometryException ge("Invalid latitude in setGeocentric: "
00520 + StringUtils::asString(lat));
00521 GPSTK_THROW(ge);
00522 }
00523 if(rad < 0)
00524 {
00525 GeometryException ge("Invalid radius in setGeocentric: "
00526 + StringUtils::asString(rad));
00527 GPSTK_THROW(ge);
00528 }
00529 theArray[0] = lat;
00530 theArray[1] = lon;
00531 theArray[2] = rad;
00532
00533 if(theArray[1] < 0)
00534 theArray[1] += 360*(1+(unsigned long)(theArray[1]/360));
00535 else if(theArray[1] >= 360)
00536 theArray[1] -= 360*(unsigned long)(theArray[1]/360);
00537 system = Geocentric;
00538
00539 return *this;
00540 }
00541
00542
00543
00544
00545
00546
00547
00548 Position& Position::setSpherical(const double theta,
00549 const double phi,
00550 const double rad)
00551 throw(GeometryException)
00552 {
00553 if(theta < 0 || theta > 180)
00554 {
00555 GeometryException ge("Invalid theta in setSpherical: "
00556 + StringUtils::asString(theta));
00557 GPSTK_THROW(ge);
00558 }
00559 if(rad < 0)
00560 {
00561 GeometryException ge("Invalid radius in setSpherical: "
00562 + StringUtils::asString(rad));
00563 GPSTK_THROW(ge);
00564 }
00565
00566 theArray[0] = theta;
00567 theArray[1] = phi;
00568 theArray[2] = rad;
00569
00570 if(theArray[1] < 0)
00571 theArray[1] += 360*(1+(unsigned long)(theArray[1]/360));
00572 else if(theArray[1] >= 360)
00573 theArray[1] -= 360*(unsigned long)(theArray[1]/360);
00574 system = Spherical;
00575
00576 return *this;
00577 }
00578
00579
00580
00581
00582
00583
00584 Position& Position::setECEF(const double X,
00585 const double Y,
00586 const double Z)
00587 throw()
00588 {
00589 theArray[0] = X;
00590 theArray[1] = Y;
00591 theArray[2] = Z;
00592 system = Cartesian;
00593 return *this;
00594 }
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642 Position& Position::setToString(const std::string& str,
00643 const std::string& fmt)
00644 throw(GeometryException,
00645 DayTime::FormatException,
00646 StringUtils::StringException)
00647 {
00648 try {
00649
00650
00651 Position toReturn;
00652
00653
00654 bool hX=false, hY=false, hZ=false;
00655 bool hglat=false, hlon=false, hht=false;
00656 bool hclat=false, hrad=false;
00657 bool htheta=false, hphi=false;
00658
00659 double x,y,z,glat,lon,ht,clat,rad,theta,phi;
00660
00661 string f = fmt;
00662 string s = str;
00663
00664
00665
00666 while ( (s.size() > 0) && (f.size() > 0) )
00667 {
00668
00669
00670
00671 while ( (s.length() != 0) && (f.length() != 0) && (f[0] != '%') )
00672 {
00673
00674 s.erase(0,1);
00675 f.erase(0,1);
00676 stripLeading(s);
00677 stripLeading(f);
00678 }
00679
00680
00681 if ( (s.length() == 0) || (f.length() == 0) )
00682 break;
00683
00684
00685 f.erase(0,1);
00686
00687
00688
00689 string::size_type fieldLength = string::npos;
00690
00691 if (!isalpha(f[0]))
00692 {
00693 fieldLength = asInt(f);
00694
00695
00696
00697 while ((!f.empty()) && (!isalpha(f[0])))
00698 f.erase(0,1);
00699 if (f.empty())
00700 break;
00701 }
00702
00703
00704 char delimiter = 0;
00705 if (f.size() > 1)
00706 {
00707 if (f[1] != '%')
00708 {
00709 delimiter = f[1];
00710
00711 if (fieldLength == string::npos)
00712 fieldLength = s.find(delimiter,0);
00713 }
00714
00715
00716
00717 else if (fieldLength == string::npos)
00718 {
00719 fieldLength = 1;
00720 }
00721 }
00722
00723
00724
00725 string toBeRemoved = s.substr(0, fieldLength);
00726
00727
00728 switch (f[0])
00729 {
00730
00731
00732
00733
00734
00735
00736 case 'X':
00737 x = asDouble(toBeRemoved) * 1000;
00738 hX = true;
00739 break;
00740 case 'x':
00741 x = asDouble(toBeRemoved);
00742 hX = true;
00743 break;
00744 case 'Y':
00745 y = asDouble(toBeRemoved) * 1000;
00746 hY = true;
00747 break;
00748 case 'y':
00749 y = asDouble(toBeRemoved);
00750 hY = true;
00751 break;
00752 case 'Z':
00753 z = asDouble(toBeRemoved) * 1000;
00754 hZ = true;
00755 break;
00756 case 'z':
00757 z = asDouble(toBeRemoved);
00758 hZ = true;
00759 break;
00760
00761
00762 case 'A':
00763 glat = asDouble(toBeRemoved);
00764 if(glat > 90. || glat < -90.) {
00765 DayTime::FormatException f(
00766 "Invalid geodetic latitude for setTostring: "
00767 + toBeRemoved);
00768 GPSTK_THROW(f);
00769 }
00770 hglat = true;
00771 break;
00772 case 'a':
00773 clat = asDouble(toBeRemoved);
00774 if(clat > 90. || clat < -90.) {
00775 DayTime::FormatException f(
00776 "Invalid geocentric latitude for setTostring: "
00777 + toBeRemoved);
00778 GPSTK_THROW(f);
00779 }
00780 hclat = true;
00781 break;
00782
00783
00784
00785
00786 case 'L':
00787 case 'l':
00788 lon = asDouble(toBeRemoved);
00789 if(lon < 0)
00790 lon += 360*(1+(unsigned long)(lon/360));
00791 else if(lon >= 360)
00792 lon -= 360*(unsigned long)(lon/360);
00793 hlon = true;
00794 break;
00795 case 'w':
00796 case 'W':
00797 lon = 360.0 - asDouble(toBeRemoved);
00798 if(lon < 0)
00799 lon += 360*(1+(unsigned long)(lon/360));
00800 else if(lon >= 360)
00801 lon -= 360*(unsigned long)(lon/360);
00802 hlon = true;
00803 break;
00804
00805
00806 case 't':
00807 theta = asDouble(toBeRemoved);
00808 if(theta > 180. || theta < 0.) {
00809 DayTime::FormatException f("Invalid theta for setTostring: "
00810 + toBeRemoved);
00811 GPSTK_THROW(f);
00812 }
00813 htheta = true;
00814 break;
00815 case 'T':
00816 theta = asDouble(toBeRemoved) * RAD_TO_DEG;
00817 if(theta > 90. || theta < -90.) {
00818 DayTime::FormatException f("Invalid theta for setTostring: "
00819 + toBeRemoved);
00820 GPSTK_THROW(f);
00821 }
00822 htheta = true;
00823 break;
00824
00825
00826 case 'p':
00827 phi = asDouble(toBeRemoved);
00828 if(phi < 0)
00829 phi += 360*(1+(unsigned long)(phi/360));
00830 else if(phi >= 360)
00831 phi -= 360*(unsigned long)(phi/360);
00832 hphi = true;
00833 break;
00834 case 'P':
00835 phi = asDouble(toBeRemoved) * RAD_TO_DEG;
00836 if(phi < 0)
00837 phi += 360*(1+(unsigned long)(phi/360));
00838 else if(phi >= 360)
00839 phi -= 360*(unsigned long)(phi/360);
00840 hphi = true;
00841 break;
00842
00843
00844
00845
00846 case 'r':
00847 rad = asDouble(toBeRemoved);
00848 if(rad < 0.0) {
00849 DayTime::FormatException f("Invalid radius for setTostring: "
00850 + toBeRemoved);
00851 GPSTK_THROW(f);
00852 }
00853 hrad = true;
00854 break;
00855 case 'R':
00856 rad = asDouble(toBeRemoved) * 1000;
00857 if(rad < 0.0) {
00858 DayTime::FormatException f("Invalid radius for setTostring: "
00859 + toBeRemoved);
00860 GPSTK_THROW(f);
00861 }
00862 hrad = true;
00863 break;
00864 case 'h':
00865 ht = asDouble(toBeRemoved);
00866 hht = true;
00867 break;
00868 case 'H':
00869 ht = asDouble(toBeRemoved) * 1000;
00870 hht = true;
00871 break;
00872 default:
00873 break;
00874 }
00875
00876 stripLeading(s,toBeRemoved,1);
00877
00878
00879 f.erase(0,1);
00880
00881
00882 stripLeading(f);
00883 stripLeading(s);
00884
00885 }
00886
00887 if ( s.length() != 0 )
00888 {
00889
00890 DayTime::FormatException fe(
00891 "Processing error - parts of strings left unread - " + s);
00892 GPSTK_THROW(fe);
00893 }
00894
00895 if (f.length() != 0)
00896 {
00897
00898 DayTime::FormatException fe(
00899 "Processing error - parts of strings left unread - " + f);
00900 GPSTK_THROW(fe);
00901 }
00902
00903
00904 if ( !(hX && hY && hZ) && !(hglat && hlon && hht) &&
00905 !(hclat && hlon && hrad) && !(htheta && hphi && hrad)) {
00906 DayTime::FormatException fe("Incomplete specification for setToString");
00907 GPSTK_THROW(fe);
00908 }
00909
00910
00911 if(hX && hY && hZ)
00912 toReturn.setECEF(x,y,z);
00913 else if(hglat && hlon && hht)
00914 toReturn.setGeodetic(glat,lon,ht);
00915 else if(hclat && hlon && hrad)
00916 toReturn.setGeocentric(clat,lon,rad);
00917 else if(htheta && hphi && hrad)
00918 toReturn.setSpherical(theta,phi,rad);
00919
00920 *this = toReturn;
00921 return *this;
00922 }
00923 catch(gpstk::Exception& exc)
00924 {
00925 GeometryException ge(exc);
00926 ge.addText("Failed to convert string to Position");
00927 GPSTK_THROW(ge);
00928 }
00929 catch(std::exception& exc)
00930 {
00931 GeometryException ge(exc.what());
00932 ge.addText("Failed to convert string to Position");
00933 GPSTK_THROW(ge);
00934 }
00935 }
00936
00937
00938
00939
00940
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950
00951
00952
00953
00954
00955
00956
00957
00958
00959
00960
00961
00962
00963
00964
00965
00966 std::string Position::printf(const char *fmt) const
00967 throw(StringUtils::StringException)
00968 {
00969 string rv = fmt;
00970 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?x"),
00971 string("xf"), X());
00972 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?y"),
00973 string("yf"), Y());
00974 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?z"),
00975 string("zf"), Z());
00976 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?X"),
00977 string("Xf"), X()/1000);
00978 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?Y"),
00979 string("Yf"), Y()/1000);
00980 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?Z"),
00981 string("Zf"), Z()/1000);
00982
00983 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?A"),
00984 string("Af"), geodeticLatitude());
00985 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?a"),
00986 string("af"), geocentricLatitude());
00987 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?L"),
00988 string("Lf"), longitude());
00989 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?l"),
00990 string("lf"), longitude());
00991 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?w"),
00992 string("wf"), 360-longitude());
00993 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?W"),
00994 string("Wf"), 360-longitude());
00995
00996 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?t"),
00997 string("tf"), theta());
00998 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?T"),
00999 string("Tf"), theta()*DEG_TO_RAD);
01000 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?p"),
01001 string("pf"), phi());
01002 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?P"),
01003 string("Pf"), phi()*DEG_TO_RAD);
01004 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?r"),
01005 string("rf"), radius());
01006 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?R"),
01007 string("Rf"), radius()/1000);
01008 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?h"),
01009 string("hf"), height());
01010 rv = formattedPrint(rv, string("%[ 0-]?[[:digit:]]*(\\.[[:digit:]]+)?H"),
01011 string("Hf"), height()/1000);
01012 return rv;
01013 }
01014
01015
01016 string Position::asString() const
01017 throw(StringUtils::StringException)
01018 {
01019 ostringstream o;
01020 o << *this;
01021 return o.str();
01022 }
01023
01024
01025
01026
01027
01028
01029
01030 void Position::convertSphericalToCartesian(const Triple& tpr,
01031 Triple& xyz)
01032 throw()
01033 {
01034 double st=sin(tpr[0]*DEG_TO_RAD);
01035 xyz[0] = tpr[2]*st*cos(tpr[1]*DEG_TO_RAD);
01036 xyz[1] = tpr[2]*st*sin(tpr[1]*DEG_TO_RAD);
01037 xyz[2] = tpr[2]*cos(tpr[0]*DEG_TO_RAD);
01038 }
01039
01040
01041
01042
01043
01044 void Position::convertCartesianToSpherical(const Triple& xyz,
01045 Triple& tpr)
01046 throw()
01047 {
01048 tpr[2] = RSS(xyz[0],xyz[1],xyz[2]);
01049 if(tpr[2] <= Position::POSITION_TOLERANCE/5)
01050 {
01051
01052 tpr[0] = 90;
01053 tpr[1] = 0;
01054 return;
01055 }
01056 tpr[0] = acos(xyz[2]/tpr[2]);
01057 tpr[0] *= RAD_TO_DEG;
01058 if(RSS(xyz[0],xyz[1]) < Position::POSITION_TOLERANCE/5) {
01059 tpr[1] = 0;
01060 return;
01061 }
01062 tpr[1] = atan2(xyz[1],xyz[0]);
01063 tpr[1] *= RAD_TO_DEG;
01064 if(tpr[1] < 0) tpr[1] += 360;
01065 }
01066
01067
01068
01069
01070
01071
01072
01073
01074
01075 void Position::convertCartesianToGeodetic(const Triple& xyz,
01076 Triple& llh,
01077 const double A,
01078 const double eccSq)
01079 throw()
01080 {
01081 double p,slat,N,htold,latold;
01082 p = SQRT(xyz[0]*xyz[0]+xyz[1]*xyz[1]);
01083 if(p < Position::POSITION_TOLERANCE/5) {
01084 llh[0] = llh[1] = 0;
01085 llh[2] = fabs(xyz[2]) - A;
01086 return;
01087 }
01088 llh[0] = atan2(xyz[2], p*(1.0-eccSq));
01089 llh[2] = 0;
01090 for(int i=0; i<5; i++) {
01091 slat = sin(llh[0]);
01092 N = A / SQRT(1.0 - eccSq*slat*slat);
01093 htold = llh[2];
01094 llh[2] = p/cos(llh[0]) - N;
01095 latold = llh[0];
01096 llh[0] = atan2(xyz[2], p*(1.0-eccSq*(N/(N+llh[2]))));
01097 if(fabs(llh[0]-latold) < 1.0e-9 && fabs(llh[2]-htold) < 1.0e-9 * A) break;
01098 }
01099 llh[1] = atan2(xyz[1],xyz[0]);
01100 if(llh[1] < 0.0) llh[1] += TWO_PI;
01101 llh[0] *= RAD_TO_DEG;
01102 llh[1] *= RAD_TO_DEG;
01103 }
01104
01105
01106
01107
01108
01109
01110
01111
01112
01113 void Position::convertGeodeticToCartesian(const Triple& llh,
01114 Triple& xyz,
01115 const double A,
01116 const double eccSq)
01117 throw()
01118 {
01119 double slat = sin(llh[0]*DEG_TO_RAD);
01120 double clat = cos(llh[0]*DEG_TO_RAD);
01121 double N = A/SQRT(1.0-eccSq*slat*slat);
01122 xyz[0] = (N+llh[2])*clat*cos(llh[1]*DEG_TO_RAD);
01123 xyz[1] = (N+llh[2])*clat*sin(llh[1]*DEG_TO_RAD);
01124 xyz[2] = (N*(1.0-eccSq)+llh[2])*slat;
01125 }
01126
01127
01128
01129
01130
01131 void Position::convertCartesianToGeocentric(const Triple& xyz,
01132 Triple& llr)
01133 throw()
01134 {
01135 convertCartesianToSpherical(xyz, llr);
01136 llr[0] = 90 - llr[0];
01137 }
01138
01139
01140
01141
01142 void Position::convertGeocentricToCartesian(const Triple& llr,
01143 Triple& xyz)
01144 throw()
01145 {
01146 Triple llh(llr);
01147 llh[0] = 90 - llh[0];
01148 convertSphericalToCartesian(llh, xyz);
01149 }
01150
01151
01152
01153
01154
01155
01156
01157 void Position::convertGeocentricToGeodetic(const Triple& llr,
01158 Triple& llh,
01159 const double A,
01160 const double eccSq)
01161 throw()
01162 {
01163 double cl,p,sl,slat,N,htold,latold;
01164 cl = sin((90-llr[0])*DEG_TO_RAD);
01165 sl = cos((90-llr[0])*DEG_TO_RAD);
01166 if(llr[2] <= Position::POSITION_TOLERANCE/5) {
01167
01168
01169 llh[0] = llh[1] = 0;
01170 llh[2] = -A;
01171 return;
01172 }
01173 else if(cl < 1.e-10) {
01174
01175 if(llr[0] < 0) llh[0] = -90;
01176 else llh[0] = 90;
01177 llh[1] = 0;
01178 llh[2] = llr[2] - A*SQRT(1-eccSq);
01179 return;
01180 }
01181 llh[0] = atan2(sl, cl*(1.0-eccSq));
01182 p = cl*llr[2];
01183 llh[2] = 0;
01184 for(int i=0; i<5; i++) {
01185 slat = sin(llh[0]);
01186 N = A / SQRT(1.0 - eccSq*slat*slat);
01187 htold = llh[2];
01188 llh[2] = p/cos(llh[0]) - N;
01189 latold = llh[0];
01190 llh[0] = atan2(sl, cl*(1.0-eccSq*(N/(N+llh[2]))));
01191 if(fabs(llh[0]-latold) < 1.0e-9 && fabs(llh[2]-htold) < 1.0e-9 * A) break;
01192 }
01193 llh[0] *= RAD_TO_DEG;
01194 }
01195
01196
01197
01198
01199
01200
01201
01202 void Position::convertGeodeticToGeocentric(const Triple& llh,
01203 Triple& llr,
01204 const double A,
01205 const double eccSq)
01206 throw()
01207 {
01208 double slat = sin(llh[0]*DEG_TO_RAD);
01209 double N = A/SQRT(1.0-eccSq*slat*slat);
01210
01211 llr[2] = SQRT((N+llh[2])*(N+llh[2]) + N*eccSq*(N*eccSq-2*(N+llh[2]))*slat*slat);
01212 if(llr[2] <= Position::POSITION_TOLERANCE/5)
01213 {
01214
01215
01216 llr[0] = llr[1] = llr[2] = 0;
01217 return;
01218 }
01219 if(1-fabs(slat) < 1.e-10) {
01220 if(slat < 0) llr[0] = -90;
01221 else llr[0] = 90;
01222 return;
01223 }
01224
01225 llr[0] = acos((N*(1-eccSq)+llh[2])*slat/llr[2]);
01226 llr[0] *= RAD_TO_DEG;
01227 llr[0] = 90 - llr[0];
01228 }
01229
01230
01231
01232
01233
01234
01235
01236 ostream& operator<<(ostream& s, const Position& p)
01237 {
01238 if(p.system == Position::Cartesian)
01239 s << p.printf("%.4x m %.4y m %.4z m");
01240 else if(p.system == Position::Geodetic)
01241 s << p.printf("%.8A degN %.8L degE %.4h m");
01242 else if(p.system == Position::Geocentric)
01243 s << p.printf("%.8a degN %.8L degE %.4r m");
01244 else if(p.system == Position::Spherical)
01245 s << p.printf("%.8t deg %.8p deg %.4r m");
01246 else
01247 s << " Unknown system! : " << p[0] << " " << p[1] << " " << p[2];
01248
01249 return s;
01250 }
01251
01252
01253
01254
01255
01256
01257 double range(const Position& A,
01258 const Position& B)
01259 throw(GeometryException)
01260 {
01261 if(A.AEarth != B.AEarth || A.eccSquared != B.eccSquared)
01262 {
01263 GeometryException ge("Unequal geoids");
01264 GPSTK_THROW(ge);
01265 }
01266
01267 Position L(A),R(B);
01268 L.transformTo(Position::Cartesian);
01269 R.transformTo(Position::Cartesian);
01270 double dif = RSS(L.X()-R.X(),L.Y()-R.Y(),L.Z()-R.Z());
01271 return dif;
01272 }
01273
01274
01275
01276
01277 double Position::radiusEarth(const double geolat,
01278 const double A,
01279 const double eccSq)
01280 throw()
01281 {
01282 double slat=sin(DEG_TO_RAD*geolat);
01283 double e=(1.0-eccSq);
01284 double f=(1.0+(e*e-1.0)*slat*slat)/(1.0-eccSq*slat*slat);
01285 return (A * SQRT(f));
01286 }
01287
01288
01289
01290
01291
01292
01293 double Position::elevation(const Position& Target) const
01294 throw(GeometryException)
01295 {
01296 Position R(*this),S(Target);
01297 R.transformTo(Cartesian);
01298 S.transformTo(Cartesian);
01299
01300 double elevation;
01301 try {
01302 elevation = R.elvAngle(S);
01303 }
01304 catch(GeometryException& ge)
01305 {
01306 GPSTK_RETHROW(ge);
01307 }
01308 return elevation;
01309 }
01310
01311
01312
01313
01314
01315
01316
01317 double Position::elevationGeodetic(const Position& Target) const
01318 throw(GeometryException)
01319 {
01320 Position R(*this),S(Target);
01321 double latGeodetic = R.getGeodeticLatitude()*DEG_TO_RAD;
01322 double longGeodetic = R.getLongitude()*DEG_TO_RAD;
01323 double localUp;
01324 double cosUp;
01325 R.transformTo(Cartesian);
01326 S.transformTo(Cartesian);
01327 Triple z;
01328
01329 z = S.theArray - R.theArray;
01330
01331 if (z.mag()<=1e-4)
01332 {
01333 GeometryException ge("Positions are within .1 millimeter");
01334 GPSTK_THROW(ge);
01335 }
01336
01337
01338 Triple kVector(cos(latGeodetic)*cos(longGeodetic), cos(latGeodetic)*sin(longGeodetic), sin(latGeodetic));
01339
01340 localUp = z.dot(kVector);
01341
01342 cosUp = localUp/z.mag();
01343
01344 return 90.0 - ((::acos(cosUp))*RAD_TO_DEG);
01345 }
01346
01347
01348
01349
01350
01351
01352 double Position::azimuth(const Position& Target) const
01353 throw(GeometryException)
01354 {
01355 Position R(*this),S(Target);
01356 R.transformTo(Cartesian);
01357 S.transformTo(Cartesian);
01358
01359 double az;
01360 try
01361 {
01362 az = R.azAngle(S);
01363
01364 }
01365 catch(GeometryException& ge)
01366 {
01367 GPSTK_RETHROW(ge);
01368 }
01369
01370 return az;
01371 }
01372
01373
01374
01375
01376
01377
01378
01379 double Position::azimuthGeodetic(const Position& Target) const
01380 throw(GeometryException)
01381 {
01382 Position R(*this),S(Target);
01383 double latGeodetic = R.getGeodeticLatitude()*DEG_TO_RAD;
01384 double longGeodetic = R.getLongitude()*DEG_TO_RAD;
01385 double localN, localE;
01386 R.transformTo(Cartesian);
01387 S.transformTo(Cartesian);
01388 Triple z;
01389
01390 z = S.theArray - R.theArray;
01391
01392 if (z.mag()<=1e-4)
01393 {
01394 GeometryException ge("Positions are within .1 millimeter");
01395 GPSTK_THROW(ge);
01396 }
01397
01398
01399 Triple iVector(-sin(latGeodetic)*cos(longGeodetic), -sin(latGeodetic)*sin(longGeodetic), cos(latGeodetic));
01400
01401 Triple jVector(-sin(longGeodetic), cos(longGeodetic), 0);
01402
01403
01404 localN = (z.dot(iVector))/z.mag();
01405 localE = (z.dot(jVector))/z.mag();
01406
01407
01408 double test = fabs(localN) + fabs(localE);
01409
01410
01411 if (test < 1.0e-16) return 0.0;
01412
01413 double alpha = ((::atan2(localE, localN)) * RAD_TO_DEG);
01414 if (alpha < 0.0)
01415 {
01416 return alpha + 360.0;
01417 }
01418 else
01419 {
01420 return alpha;
01421 }
01422 }
01423
01424
01425
01426
01427
01428
01429
01430
01431
01432
01433
01434
01435
01436
01437
01438
01439 Position Position::getIonosphericPiercePoint(const double elev,
01440 const double azim,
01441 const double ionoht) const
01442 throw()
01443 {
01444 Position Rx(*this);
01445
01446
01447 Rx.transformTo(Geocentric);
01448
01449
01450 Position IPP(Rx);
01451 double el = elev * DEG_TO_RAD;
01452
01453 double p = PI/2.0 - el - asin(AEarth*cos(el)/(AEarth+ionoht));
01454 double lat = Rx.theArray[0] * DEG_TO_RAD;
01455 double az = azim * DEG_TO_RAD;
01456 IPP.theArray[0] = asin(sin(lat)*cos(p) + cos(lat)*sin(p)*cos(az));
01457 IPP.theArray[1] = Rx.theArray[1]*DEG_TO_RAD
01458 + asin(sin(p)*sin(az)/cos(IPP.theArray[0]));
01459
01460 IPP.theArray[0] *= RAD_TO_DEG;
01461 IPP.theArray[1] *= RAD_TO_DEG;
01462 IPP.theArray[2] = AEarth + ionoht;
01463
01464
01465 IPP.transformTo(system);
01466
01467 return IPP;
01468 }
01469
01470
01471
01472
01473
01474
01475
01476
01477
01478
01479 void Position::initialize(const double a,
01480 const double b,
01481 const double c,
01482 Position::CoordinateSystem s,
01483 GeoidModel *geoid)
01484 throw(GeometryException)
01485 {
01486 double bb(b);
01487 if(s == Geodetic || s==Geocentric)
01488 {
01489 if(a > 90 || a < -90)
01490 {
01491 GeometryException ge("Invalid latitude in constructor: "
01492 + StringUtils::asString(a));
01493 GPSTK_THROW(ge);
01494 }
01495 if(bb < 0)
01496 bb += 360*(1+(unsigned long)(bb/360));
01497 else if(bb >= 360)
01498 bb -= 360*(unsigned long)(bb/360);
01499 }
01500 if(s==Geocentric || s==Spherical)
01501 {
01502 if(c < 0)
01503 {
01504 GeometryException ge("Invalid radius in constructor: "
01505 + StringUtils::asString(c));
01506 GPSTK_THROW(ge);
01507 }
01508 }
01509 if(s==Spherical)
01510 {
01511 if(a < 0 || a > 180)
01512 {
01513 GeometryException ge("Invalid theta in constructor: "
01514 + StringUtils::asString(a));
01515 GPSTK_THROW(ge);
01516 }
01517 if(bb < 0)
01518 bb += 360*(1+(unsigned long)(bb/360));
01519 else if(bb >= 360)
01520 bb -= 360*(unsigned long)(bb/360);
01521 }
01522
01523 theArray[0] = a;
01524 theArray[1] = bb;
01525 theArray[2] = c;
01526
01527 if(geoid) {
01528 AEarth = geoid->a();
01529 eccSquared = geoid->eccSquared();
01530 }
01531 else {
01532 WGS84Geoid WGS84;
01533 AEarth = WGS84.a();
01534 eccSquared = WGS84.eccSquared();
01535 }
01536 system = s;
01537 tolerance = POSITION_TOLERANCE;
01538 }
01539
01540 }