GeoCoords.cpp
Go to the documentation of this file.
00001 /**
00002  * \file GeoCoords.cpp
00003  * \brief Implementation for GeographicLib::GeoCoords class
00004  *
00005  * Copyright (c) Charles Karney (2008) <charles@karney.com>
00006  * http://charles.karney.info/geographic
00007  * and licensed under the LGPL.
00008  **********************************************************************/
00009 
00010 #include <vector>
00011 #include <sstream>
00012 #include <stdexcept>
00013 #include <iomanip>
00014 #include <cerrno>
00015 #include "GeographicLib/GeoCoords.hpp"
00016 #include "GeographicLib/MGRS.hpp"
00017 #include "GeographicLib/DMS.hpp"
00018 
00019 namespace {
00020   char RCSID[] = "$Id: GeoCoords.cpp 6555 2009-02-25 16:04:25Z ckarney $";
00021   char RCSID_H[] = GEOCOORDS_HPP;
00022 }
00023 
00024 namespace GeographicLib {
00025 
00026   using namespace std;
00027 
00028   void GeoCoords::Reset(const std::string& s) {
00029     vector<string> sa;
00030     bool in = false;
00031     for (unsigned i = 0; i < s.size(); ++i) {
00032       if (isspace(s[i]) || s[i] == ',') {
00033         in = false;
00034         continue;
00035       }
00036       if (!in)
00037         sa.push_back("");
00038       in = true;
00039       sa.back().push_back(s[i]);
00040     }
00041     if (sa.size() == 1) {
00042       int prec;
00043       MGRS::Reverse(sa[0], _zone, _northp, _easting, _northing, prec);
00044       UTMUPS::Reverse(_zone, _northp, _easting, _northing,
00045                       _lat, _long, _gamma, _k);
00046     } else if (sa.size() == 2) {
00047       DMS::DecodeLatLon(sa[0], sa[1], _lat, _long);
00048       UTMUPS::Forward( _lat, _long,
00049                        _zone, _northp, _easting, _northing, _gamma, _k);
00050     } else if (sa.size() == 3) {
00051       unsigned zoneind, coordind;
00052       if (sa[0].size() > 0 && isalpha(sa[0][sa[0].size() - 1])) {
00053         zoneind = 0;
00054         coordind = 1;
00055       } else if (sa[2].size() > 0 && isalpha(sa[2][sa[2].size() - 1])) {
00056         zoneind = 2;
00057         coordind = 0;
00058       } else
00059         throw out_of_range("Neither " + sa[0] + " nor " + sa[2] +
00060                            " of the form UTM/UPS Zone + Hemisphere" +
00061                            " (ex: 38N, 09S, N)");
00062       char hemi = toupper(sa[zoneind][sa[zoneind].size() - 1]);
00063       _northp = hemi == 'N';
00064       if (! (_northp || hemi == 'S'))
00065         throw out_of_range(string("Illegal hemisphere letter ") + hemi
00066                            + " in " + sa[zoneind]);
00067       const char* c = sa[zoneind].c_str();
00068       char* q;
00069       _zone = strtol(c, &q, 10);
00070       if (q - c != int(sa[zoneind].size()) - 1)
00071         throw out_of_range("Extra text in UTM/UPS zone " + sa[zoneind]);
00072       if (q > c && _zone == 0)
00073         // Don't allow 0N as an alternative to N for UPS coordinates
00074         throw out_of_range("Illegal zone 0 in " + sa[zoneind]);
00075       if (q == c)
00076         _zone = 0;
00077       for (unsigned i = 0; i < 2; ++i) {
00078         const char* c = sa[coordind + i].c_str();
00079         errno = 0;
00080         double x = strtod(c, &q);
00081         if (errno ==  ERANGE || !isfinite(x))
00082           throw out_of_range("Number " + sa[coordind + i] + " out of range");
00083         if (q - c != int(sa[coordind + i].size()))
00084           throw out_of_range(string("Extra text in UTM/UPS ") +
00085                              (i == 0 ? "easting " : "northing ") +
00086                              sa[coordind + i]);
00087         if (i == 0)
00088           _easting = x;
00089         else
00090           _northing = x;
00091       }
00092       UTMUPS::Reverse(_zone, _northp, _easting, _northing,
00093                       _lat, _long, _gamma, _k);
00094       FixHemisphere();
00095     } else
00096       throw out_of_range("Coordinate requires 1, 2, or 3 elements");
00097     CopyToAlt();
00098   }
00099 
00100 
00101   string GeoCoords::GeoRepresentation(int prec) const {
00102     prec = max(0, min(9, prec) + 5);
00103     ostringstream os;
00104     os << fixed << setprecision(prec)
00105        << _lat << " " << _long;
00106     return os.str();
00107   }
00108 
00109   string GeoCoords::DMSRepresentation(int prec) const {
00110     prec = max(0, min(10, prec) + 5);
00111     return DMS::Encode(_lat, unsigned(prec), DMS::LATITUDE) +
00112       " " + DMS::Encode(_long, unsigned(prec), DMS::LONGITUDE);
00113   }
00114 
00115   string GeoCoords::MGRSRepresentation(int prec) const {
00116     // Max precision is um
00117     prec = max(0, min(6, prec) + 5);
00118     string mgrs;
00119     MGRS::Forward(_zone, _northp, _easting, _northing, _lat, prec, mgrs);
00120     return mgrs;
00121   }
00122 
00123   string GeoCoords::AltMGRSRepresentation(int prec) const {
00124     // Max precision is um
00125     prec = max(0, min(6, prec) + 5);
00126     string mgrs;
00127     MGRS::Forward(_alt_zone, _northp, _alt_easting, _alt_northing, _lat, prec,
00128                   mgrs);
00129     return mgrs;
00130   }
00131 
00132   void GeoCoords::UTMUPSString(int zone, double easting, double northing,
00133                                int prec, std::string& utm) const {
00134     ostringstream os;
00135     os << fixed << setfill('0');
00136     if (zone)
00137       os << setw(2) << zone;
00138     prec = max(-5, min(9, prec));
00139     double scale = prec < 0 ? pow(10.0, -prec) : 1.0;
00140     os << (_northp ? 'N' : 'S') << " "
00141        << setprecision(max(0, prec))
00142        << easting / scale;
00143     if (prec < 0 && abs(easting / scale) > 0.5)
00144       os << setw(-prec) << 0;
00145     os << " "
00146        << setprecision(max(0, prec))
00147        << northing / scale;
00148     if (prec < 0 && abs(northing / scale) > 0.5)
00149       os << setw(-prec) << 0;
00150     utm = os.str();
00151   }
00152 
00153   string GeoCoords::UTMUPSRepresentation(int prec) const {
00154     string utm;
00155     UTMUPSString(_zone, _easting, _northing, prec, utm);
00156     return utm;
00157   }
00158 
00159   string GeoCoords::AltUTMUPSRepresentation(int prec) const {
00160     string utm;
00161     UTMUPSString(_alt_zone, _alt_easting, _alt_northing, prec, utm);
00162     return utm;
00163   }
00164 
00165   void GeoCoords::FixHemisphere() {
00166     if (_lat == 0 || (_northp && _lat > 0) || (!_northp && _lat < 0))
00167       // Allow either hemisphere for equator
00168       return;
00169     if (_zone > 0) {
00170       _northing += (_northp ? 1 : -1) * MGRS::utmNshift;
00171       _northp = !_northp;
00172     } else
00173       throw out_of_range("Hemisphere mixup");
00174   }
00175 
00176 } // namespace GeographicLib