DMS.hpp

Go to the documentation of this file.
00001 /**
00002  * \file DMS.hpp
00003  * \brief Header for GeographicLib::DMS class
00004  *
00005  * Copyright (c) Charles Karney (2008, 2009, 2010, 2011) <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 6954 2011-02-16 14:36:56Z 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     static Math::real NumMatch(const std::string& s);
00042     DMS();                      // Disable constructor
00043 
00044   public:
00045 
00046     /**
00047      * Indicator for presence of hemisphere indicator (N/S/E/W) on latitudes
00048      * and longitudes.
00049      **********************************************************************/
00050     enum flag {
00051       /**
00052        * No indicator present.
00053        * @hideinitializer
00054        **********************************************************************/
00055       NONE = 0,
00056       /**
00057        * Latitude indicator (N/S) present.
00058        * @hideinitializer
00059        **********************************************************************/
00060       LATITUDE = 1,
00061       /**
00062        * Longitude indicator (E/W) present.
00063        * @hideinitializer
00064        **********************************************************************/
00065       LONGITUDE = 2,
00066       /**
00067        * Used in Encode to indicate output of an azimuth in [000, 360) with no
00068        * letter indicator.
00069        * @hideinitializer
00070        **********************************************************************/
00071       AZIMUTH = 3,
00072       /**
00073        * Used in Encode to indicate output of a plain number.
00074        * @hideinitializer
00075        **********************************************************************/
00076       NUMBER = 4,
00077     };
00078 
00079     /**
00080      * Indicator for trailing units on an angle.
00081      **********************************************************************/
00082     enum component {
00083       /**
00084        * Trailing unit is degrees.
00085        * @hideinitializer
00086        **********************************************************************/
00087       DEGREE = 0,
00088       /**
00089        * Trailing unit is arc minutes.
00090        * @hideinitializer
00091        **********************************************************************/
00092       MINUTE = 1,
00093       /**
00094        * Trailing unit is arc seconds.
00095        * @hideinitializer
00096        **********************************************************************/
00097       SECOND = 2,
00098     };
00099 
00100     /**
00101      * Convert a string in DMS to an angle.
00102      *
00103      * @param[in] dms string input.
00104      * @param[out] ind a DMS::flag value indicating the presence of a
00105      *   hemisphere indicator.
00106      * @return angle (degrees).
00107      *
00108      * Degrees, minutes, and seconds are indicated by the letters d, ', &quot;,
00109      * and these components may only be given in this order.  Any (but not all)
00110      * components may be omitted.  The last component indicator may be omitted
00111      * and is assumed to be tbe next smallest unit (thus 33d10 is interpreted
00112      * as 33d10').  The final component may be a decimal fraction but the
00113      * non-final components must be integers.  The integer parts of the minutes
00114      * and seconds components must be less than 60.  A single leading sign is
00115      * permitted.  A hemisphere designator (N, E, W, S) may be added to tbe
00116      * beginning or end of the string.  The result is multiplied by the implied
00117      * signed of the hemisphere designator (negative for S and W).  In addition
00118      * \e ind is set to DMS::LATITUDE if N or S is present, to DMS::LONGITUDE
00119      * if E or W is present, and to DMS::NONE otherwise.  Throws an error on a
00120      * malformed string.  No check is performed on the range of the result.
00121      **********************************************************************/
00122     static Math::real Decode(const std::string& dms, flag& ind);
00123 
00124     /**
00125      * Convert DMS to an angle.
00126      *
00127      * @param[in] d degrees.
00128      * @param[in] m arc minutes.
00129      * @param[in] s arc seconds.
00130      * @return angle (degrees)
00131      *
00132      * This does not propagate the sign on \e d to the other components, so
00133      * -3d20' would need to be represented as - DMS::Decode(3.0, 20.0) or
00134      * DMS::Decode(-3.0, -20.0).
00135      **********************************************************************/
00136     static Math::real Decode(real d, real m = 0, real s = 0) throw()
00137     { return d + (m + s/real(60))/real(60); }
00138 
00139     /**
00140      * Convert a string to a real number.
00141      *
00142      * @param[in] str string input.
00143      * @return decoded number.
00144      **********************************************************************/
00145     static Math::real Decode(const std::string& str);
00146 
00147     /**
00148      * Convert a pait of strings to latitude and longitude.
00149      *
00150      * @param[in] dmsa first string.
00151      * @param[in] dmsb second string.
00152      * @param[out] lat latitude.
00153      * @param[out] lon longitude.
00154      *
00155      * By default, the \e lat (resp., \e lon) is assigned to the results of
00156      * decoding \e dmsa (resp., \e dmsb).  However this is overridden if either
00157      * \e dmsa or \e dmsb contain a latitude or longitude hemisphere designator
00158      * (N, S, E, W).  Throws an error if the decoded numbers are out of the
00159      * ranges [-90<sup>o</sup>, 90<sup>o</sup>] for latitude and
00160      * [-180<sup>o</sup>, 360<sup>o</sup>] for longitude and, in which case \e
00161      * lat and \e lon are unchanged.  Finally the longitude is reduced to the
00162      * range [-180<sup>o</sup>, 180<sup>o</sup>).
00163      **********************************************************************/
00164     static void DecodeLatLon(const std::string& dmsa, const std::string& dmsb,
00165                              real& lat, real& lon);
00166 
00167     /**
00168      * Convert a string to an angle in degrees.
00169      *
00170      * @param[in] angstr input string.
00171      * @return angle (degrees)
00172      *
00173      * No hemisphere designator is allowed and no check is done on the range of
00174      * the result.
00175      **********************************************************************/
00176     static Math::real DecodeAngle(const std::string& angstr);
00177 
00178     /**
00179      * Convert a string to an azimuth in degrees.
00180      *
00181      * @param[in] azistr input string.
00182      * @return azimuth (degrees)
00183      *
00184      * A hemisphere designator E/W can be used; the result is multiplied by -1
00185      * if W is present.  Throws an error if the result is out of the range
00186      * [-180<sup>o</sup>, 360<sup>o</sup>].  Finally the azimuth is reduced to
00187      * the range [-180<sup>o</sup>, 180<sup>o</sup>).
00188      **********************************************************************/
00189     static Math::real DecodeAzimuth(const std::string& azistr);
00190 
00191     /**
00192      * Convert angle (in degrees) into a DMS string.
00193      *
00194      * @param[in] angle input angle (degrees)
00195      * @param[in] trailing DMS::component value indicating the trailing units
00196      *   on the string and this is given as a decimal number if necessary.
00197      * @param[in] prec the number of digits after the decimal point for the
00198      *   trailing component.
00199      * @param[in] ind DMS::flag value indicated additional formatting.
00200      * @return formatted string
00201      *
00202      * The interpretation of \e ind is as follows:
00203      * - ind == DMS::NONE, signed result no leading zeros on degrees except in
00204      *   the units place, e.g., -8d03'.
00205      * - ind == DMS::LATITUDE, trailing N or S hemisphere designator, no sign,
00206      *   pad degrees to 2 digits, e.g., 08d03'S.
00207      * - ind == DMS::LONGITUDE, trailing E or W hemisphere designator, no
00208      *   sign, pad degrees to 3 digits, e.g., 008d03'W.
00209      * - ind == DMS::AZIMUTH, convert to the range [0, 360<sup>o</sup>), no
00210      *   sign, pad degrees to 3 digits, , e.g., 351d57'.
00211      * .
00212      * The integer parts of the minutes and seconds components are always given
00213      * with 2 digits.
00214      **********************************************************************/
00215     static std::string Encode(real angle, component trailing, unsigned prec,
00216                               flag ind = NONE);
00217 
00218     /**
00219      * Convert angle into a DMS string selecting the trailing component
00220      * based on the precision.
00221      *
00222      * @param[in] angle input angle (degrees)
00223      * @param[in] prec the precision relative to 1 degree.
00224      * @param[in] ind DMS::flag value indicated additional formatting.
00225      * @return formatted string
00226      *
00227      * \e prec indicates the precision relative to 1 degree, e.g., \e prec = 3
00228      * gives a result accurate to 0.1' and \e prec = 4 gives a result accurate
00229      * to 1&quot;.  \e ind is interpreted as in DMS::Encode with the additional
00230      * facility at DMS::NUMBER treats \e angle a number in fixed format with
00231      * precision \e prec.
00232      **********************************************************************/
00233     static std::string Encode(real angle, unsigned prec, flag ind = NONE) {
00234       if (ind == NUMBER) {
00235         if (!Math::isfinite(angle))
00236           return angle < 0 ? std::string("-inf") :
00237             (angle > 0 ? std::string("inf") : std::string("nan"));
00238         std::ostringstream s;
00239         s << std::fixed << std::setprecision(prec) << angle;
00240         return s.str();
00241       } else
00242         return Encode(angle,
00243                       prec < 2 ? DEGREE : (prec < 4 ? MINUTE : SECOND),
00244                       prec < 2 ? prec : (prec < 4 ? prec - 2 : prec - 4),
00245                       ind);
00246     }
00247 
00248     /**
00249      * Split angle into degrees and mintues
00250      *
00251      * @param[in] ang angle (degrees)
00252      * @param[out] d degrees (an integer returned as a real)
00253      * @param[out] m arc minutes.
00254      **********************************************************************/
00255     static void Encode(real ang, real& d, real& m) throw() {
00256       d = int(ang); m = 60 * (ang - d);
00257     }
00258 
00259     /**
00260      * Split angle into degrees and mintues and seconds.
00261      *
00262      * @param[in] ang angle (degrees)
00263      * @param[out] d degrees (an integer returned as a real)
00264      * @param[out] m arc minutes (an integer returned as a real)
00265      * @param[out] s arc seconds.
00266      **********************************************************************/
00267     static void Encode(real ang, real& d, real& m, real& s) throw() {
00268       d = int(ang); ang = 60 * (ang - d);
00269       m = int(ang); s = 60 * (ang - m);
00270     }
00271 
00272   };
00273 
00274 } // namespace GeographicLib
00275 
00276 #endif