00001 /** 00002 * \file DMS.hpp 00003 * \brief Header for GeographicLib::DMS class 00004 * 00005 * Copyright (c) Charles Karney (2008, 2009, 2010) <charles@karney.com> 00006 * and licensed under the LGPL. For more information, see 00007 * http://geographiclib.sourceforge.net/ 00008 **********************************************************************/ 00009 00010 #if !defined(GEOGRAPHICLIB_DMS_HPP) 00011 #define GEOGRAPHICLIB_DMS_HPP "$Id: DMS.hpp 6827 2010-05-20 19:56:18Z karney $" 00012 00013 #include "GeographicLib/Constants.hpp" 00014 #include <sstream> 00015 #include <iomanip> 00016 00017 namespace GeographicLib { 00018 00019 /** 00020 * \brief Convert between degrees and %DMS representation. 00021 * 00022 * Parse a string representing degree, minutes, and seconds and return the 00023 * angle in degrees and format an angle in degrees as degree, minutes, and 00024 * seconds. 00025 **********************************************************************/ 00026 class DMS { 00027 private: 00028 typedef Math::real real; 00029 static int lookup(const std::string& s, char c) throw() { 00030 std::string::size_type r = s.find(toupper(c)); 00031 return r == std::string::npos ? -1 : int(r); 00032 } 00033 template<typename T> static std::string str(T x) { 00034 std::ostringstream s; s << x; return s.str(); 00035 } 00036 static const std::string hemispheres; 00037 static const std::string signs; 00038 static const std::string digits; 00039 static const std::string dmsindicators; 00040 static const std::string components[3]; 00041 DMS(); // Disable constructor 00042 00043 public: 00044 00045 /** 00046 * Indicator for presence of hemisphere indicator (N/S/E/W) on latitudes 00047 * and longitudes. DMS::AZIMUTH is used in Encode to indicate output in 00048 * [000, 360) with no letter indicator. DMS::NUMBER is used in Encode to 00049 * indicate the output of a plain number. 00050 **********************************************************************/ 00051 enum flag { NONE = 0, LATITUDE = 1, LONGITUDE = 2, AZIMUTH = 3, 00052 NUMBER = 4, }; 00053 00054 /** 00055 * Indicator for trailing units on an angle. 00056 **********************************************************************/ 00057 enum component { DEGREE = 0, MINUTE = 1, SECOND = 2 }; 00058 00059 /** 00060 * Read a string \e dms in DMS format and return the resulting angle in 00061 * degrees. Degrees, minutes, and seconds are indicated by the letters d, 00062 * ', ", and these components may only be given in this order. Any 00063 * (but not all) components may be omitted. The last component indicator 00064 * may be omitted and is assumed to be tbe next smallest unit (thus 33d10 00065 * is interpreted as 33d10'). The final component may be a decimal 00066 * fraction but the non-final components must be integers. The integer 00067 * parts of the minutes and seconds components must be less than 60. A 00068 * single leading sign is permitted. A hemisphere designator (N, E, W, S) 00069 * may be added to tbe beginning or end of the string. The result is 00070 * multiplied by the implied signed of the hemisphere designator (negative 00071 * for S and W). In addition \e flag is used to indicate whether such a 00072 * designator was found and whether it implies that the angle is a latitude 00073 * (N or S) or longitude (E or W). Throws an error on a malformed string. 00074 * No check is performed on the range of the result. 00075 **********************************************************************/ 00076 static Math::real Decode(const std::string& dms, flag& ind); 00077 00078 /** 00079 * Convert real degrees \e d, minutes \e m, and seconds \e s, to degrees. 00080 * This does not propagate the sign on \e d to the other components, so 00081 * -3d20' would need to be represented as - DMS::Decode(3.0, 20.0) or 00082 * DMS::Decode(-3.0, -20.0). 00083 **********************************************************************/ 00084 static Math::real Decode(real d, real m = 0, real s = 0) throw() 00085 { return d + (m + s/real(60))/real(60); } 00086 00087 /** 00088 * Convert a string \e str to a real number. 00089 **********************************************************************/ 00090 static Math::real Decode(const std::string& str); 00091 00092 /** 00093 * Convert two strings \e dmsa and \e dmsb to a latitude, \e lat, and 00094 * longitude, \e lon. By default, the \e lat (resp., \e lon) is assigned 00095 * to the results of decoding \e dmsa (resp., \e dmsb). However this is 00096 * overridden if either \e dmsa or \e dmsb contain a latitude or longitude 00097 * hemisphere designator (N, S, E, W). Throws an error if the decoded 00098 * numbers are out of the ranges [-90<sup>o</sup>, 90<sup>o</sup>] for 00099 * latitude and [-180<sup>o</sup>, 360<sup>o</sup>] for longitude and, in 00100 * which case \e lat and \e lon are unchanged. Finally the longitude is 00101 * reduced to the range [-180<sup>o</sup>, 180<sup>o</sup>). 00102 **********************************************************************/ 00103 static void DecodeLatLon(const std::string& dmsa, const std::string& dmsb, 00104 real& lat, real& lon); 00105 00106 /** 00107 * Convert a string \e angstr to an angle in degrees. No hemisphere 00108 * designator is allowed and no check is done on the range of the result. 00109 **********************************************************************/ 00110 static Math::real DecodeAngle(const std::string& angstr); 00111 00112 /** 00113 * Convert a string \e azistr to an azimuth in degrees. A hemisphere 00114 * designator E/W can be used; the result is multiplied by -1 if W is 00115 * present. Throws an error if the result is out of the range 00116 * [-180<sup>o</sup>, 360<sup>o</sup>]. Finally the azimuth is reduced to 00117 * the range [-180<sup>o</sup>, 180<sup>o</sup>). 00118 **********************************************************************/ 00119 static Math::real DecodeAzimuth(const std::string& azistr); 00120 00121 /** 00122 * Convert \e angle (in degrees) into a DMS string. \e trailing indicates 00123 * the least significant component of the string (and this component is 00124 * given as a decimal number if necessary). \e prec indicates the number 00125 * of digits after the decimal point for the trailing component. \e flag 00126 * indicates additional formating as follows 00127 * - flag == DMS::NONE, signed result no leading zeros on degrees except in 00128 * the units place, e.g., -8d03'. 00129 * - flag == DMS::LATITUDE, trailing N or S hemisphere designator, no sign, 00130 * pad degress to 2 digits, e.g., 08d03'S. 00131 * - flag == DMS::LONGITUDE, trailing E or W hemisphere designator, no 00132 * sign, pad degress to 3 digits, e.g., 008d03'W. 00133 * - flag == DMS::AZIMUTH, convert to the range [0, 360<sup>o</sup>), no 00134 * sign, pad degrees to 3 digits, , e.g., 351d57'. 00135 * . 00136 * The integer parts of the minutes and seconds components are always given 00137 * with 2 digits. 00138 **********************************************************************/ 00139 static std::string Encode(real angle, component trailing, unsigned prec, 00140 flag ind = NONE); 00141 00142 /** 00143 * Convert \e angle into a DMS string selecting the trailing component 00144 * based on \e prec. \e prec indicates the precision relative to 1 degree, 00145 * e.g., \e prec = 3 gives a result accurate to 0.1' and \e prec = 4 gives 00146 * a result accurate to 1". If \e ind is DMS::NUMBER, then merely 00147 * format \e angle as a number in fixed format with precision \e prec. 00148 **********************************************************************/ 00149 static std::string Encode(real angle, unsigned prec, flag ind = NONE) { 00150 if (ind == NUMBER) { 00151 std::ostringstream s; 00152 s << std::fixed << std::setprecision(prec) << angle; 00153 return s.str(); 00154 } else 00155 return Encode(angle, 00156 prec < 2 ? DEGREE : (prec < 4 ? MINUTE : SECOND), 00157 prec < 2 ? prec : (prec < 4 ? prec - 2 : prec - 4), 00158 ind); 00159 } 00160 00161 /** 00162 * Split angle, \e ang, into degrees, \e d, and minutes \e m. 00163 **********************************************************************/ 00164 static void Encode(real ang, real& d, real& m) throw() { 00165 d = int(ang); m = 60 * (ang - d); 00166 } 00167 00168 /** 00169 * Split angle, \e ang, into degrees, \e d, minutes, \e m, and seconds \e 00170 * s. 00171 **********************************************************************/ 00172 static void Encode(real ang, real& d, real& m, real& s) throw() { 00173 d = int(ang); ang = 60 * (ang - d); 00174 m = int(ang); s = 60 * (ang - m); 00175 } 00176 00177 }; 00178 00179 } // namespace GeographicLib 00180 00181 #endif