00001 /** 00002 * \file NETGeographicLib/DMS.h 00003 * \brief Header for NETGeographicLib::DMS class 00004 * 00005 * NETGeographicLib is copyright (c) Scott Heiman (2013) 00006 * GeographicLib is Copyright (c) Charles Karney (2010-2012) 00007 * <charles@karney.com> and licensed under the MIT/X11 License. 00008 * For more information, see 00009 * http://geographiclib.sourceforge.net/ 00010 **********************************************************************/ 00011 #pragma once 00012 00013 namespace NETGeographicLib 00014 { 00015 /** 00016 * \brief .NET wrapper for GeographicLib::DMS. 00017 * 00018 * Parse a string representing degree, minutes, and seconds and return the 00019 * angle in degrees and format an angle in degrees as degree, minutes, and 00020 * seconds. In addition, handle NANs and infinities on input and output. 00021 * 00022 * C# Example: 00023 * \include example-DMS.cs 00024 * Managed C++ Example: 00025 * \include example-DMS.cpp 00026 * Visual Basic Example: 00027 * \include example-DMS.vb 00028 **********************************************************************/ 00029 public ref class DMS 00030 { 00031 public: 00032 /** 00033 * Indicator for presence of hemisphere indicator (N/S/E/W) on latitudes 00034 * and longitudes. 00035 **********************************************************************/ 00036 enum class Flag { 00037 /** 00038 * No indicator present. 00039 * @hideinitializer 00040 **********************************************************************/ 00041 NONE = 0, 00042 /** 00043 * Latitude indicator (N/S) present. 00044 * @hideinitializer 00045 **********************************************************************/ 00046 LATITUDE = 1, 00047 /** 00048 * Longitude indicator (E/W) present. 00049 * @hideinitializer 00050 **********************************************************************/ 00051 LONGITUDE = 2, 00052 /** 00053 * Used in Encode to indicate output of an azimuth in [000, 360) with no 00054 * letter indicator. 00055 * @hideinitializer 00056 **********************************************************************/ 00057 AZIMUTH = 3, 00058 /** 00059 * Used in Encode to indicate output of a plain number. 00060 * @hideinitializer 00061 **********************************************************************/ 00062 NUMBER = 4, 00063 }; 00064 00065 /** 00066 * Indicator for trailing units on an angle. 00067 **********************************************************************/ 00068 enum class Component { 00069 /** 00070 * Trailing unit is degrees. 00071 * @hideinitializer 00072 **********************************************************************/ 00073 DEGREE = 0, 00074 /** 00075 * Trailing unit is arc minutes. 00076 * @hideinitializer 00077 **********************************************************************/ 00078 MINUTE = 1, 00079 /** 00080 * Trailing unit is arc seconds. 00081 * @hideinitializer 00082 **********************************************************************/ 00083 SECOND = 2, 00084 }; 00085 00086 /** 00087 * Convert a string in DMS to an angle. 00088 * 00089 * @param[in] dms string input. 00090 * @param[out] ind a DMS::flag value signaling the presence of a 00091 * hemisphere indicator. 00092 * @exception GeographicErr if \e dms is malformed (see below). 00093 * @return angle (degrees). 00094 * 00095 * Degrees, minutes, and seconds are indicated by the characters d, ' 00096 * (single quote), " (double quote), and these components may only be 00097 * given in this order. Any (but not all) components may be omitted and 00098 * other symbols (e.g., the ° symbol for degrees and the unicode 00099 * prime and double prime symbols for minutes and seconds) may be 00100 * substituted. The last component indicator may be omitted and is assumed 00101 * to be the next smallest unit (thus 33d10 is interpreted as 33d10'). The 00102 * final component may be a decimal fraction but the non-final components 00103 * must be integers. Instead of using d, ', and " to indicate 00104 * degrees, minutes, and seconds, : (colon) may be used to <i>separate</i> 00105 * these components (numbers must appear before and after each colon); thus 00106 * 50d30'10.3" may be written as 50:30:10.3, 5.5' may be written 00107 * 0:5.5, and so on. The integer parts of the minutes and seconds 00108 * components must be less than 60. A single leading sign is permitted. A 00109 * hemisphere designator (N, E, W, S) may be added to the beginning or end 00110 * of the string. The result is multiplied by the implied sign of the 00111 * hemisphere designator (negative for S and W). In addition \e ind is set 00112 * to DMS::LATITUDE if N or S is present, to DMS::LONGITUDE if E or W is 00113 * present, and to DMS::NONE otherwise. Throws an error on a malformed 00114 * string. No check is performed on the range of the result. Examples of 00115 * legal and illegal strings are 00116 * - <i>LEGAL</i> (all the entries on each line are equivalent) 00117 * - -20.51125, 20d30'40.5"S, -20°30'40.5, -20d30.675, 00118 * N-20d30'40.5", -20:30:40.5 00119 * - 4d0'9, 4d9", 4d9'', 4:0:9, 004:00:09, 4.0025, 4.0025d, 4d0.15, 00120 * 04:.15 00121 * - <i>ILLEGAL</i> (the exception thrown explains the problem) 00122 * - 4d5"4', 4::5, 4:5:, :4:5, 4d4.5'4", -N20.5, 1.8e2d, 4:60, 00123 * 4d-5' 00124 * 00125 * <b>NOTE:</b> At present, all the string handling in the C++ 00126 * implementation %GeographicLib is with 8-bit characters. The support for 00127 * unicode symbols for degrees, minutes, and seconds is therefore via the 00128 * <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a> encoding. (The 00129 * JavaScript implementation of this class uses unicode natively, of 00130 * course.) 00131 * 00132 * Here is the list of Unicode symbols supported for degrees, minutes, 00133 * seconds: 00134 * - degrees: 00135 * - d, D lower and upper case letters 00136 * - U+00b0 degree symbol (°) 00137 * - U+00ba masculine ordinal indicator 00138 * - U+2070 superscript zero 00139 * - U+02da ring above 00140 * - minutes: 00141 * - ' apostrophe 00142 * - U+2032 prime (′) 00143 * - U+00b4 acute accent 00144 * - U+2019 right single quote (’) 00145 * - seconds: 00146 * - " quotation mark 00147 * - U+2033 double prime (″) 00148 * - U+201d right double quote (”) 00149 * - ' ' any two consecutive symbols for minutes 00150 * . 00151 * The codes with a leading zero byte, e.g., U+00b0, are accepted in their 00152 * UTF-8 coded form 0xc2 0xb0 and as a single byte 0xb0. 00153 **********************************************************************/ 00154 static double Decode(System::String^ dms, 00155 [System::Runtime::InteropServices::Out] Flag% ind); 00156 00157 /** 00158 * Convert DMS to an angle. 00159 * 00160 * @param[in] d degrees. 00161 * @param[in] m arc minutes. 00162 * @param[in] s arc seconds. 00163 * @return angle (degrees) 00164 * 00165 * This does not propagate the sign on \e d to the other components, so 00166 * -3d20' would need to be represented as - DMS::Decode(3.0, 20.0) or 00167 * DMS::Decode(-3.0, -20.0). 00168 **********************************************************************/ 00169 static double Decode(double d, double m, double s ) 00170 { return d + (m + s/double(60))/double(60); } 00171 00172 /// \cond SKIP 00173 /** 00174 * <b>DEPRECATED</b> (use Utility::num, instead). 00175 * Convert a string to a double number. 00176 * 00177 * @param[in] str string input. 00178 * @exception GeographicErr if \e str is malformed. 00179 * @return decoded number. 00180 **********************************************************************/ 00181 static double Decode(System::String^ str); 00182 /// \endcond 00183 00184 /** 00185 * Convert a pair of strings to latitude and longitude. 00186 * 00187 * @param[in] dmsa first string. 00188 * @param[in] dmsb second string. 00189 * @param[out] lat latitude. 00190 * @param[out] lon longitude reduced to the range [−180°, 00191 * 180°). 00192 * @param[in] swaplatlong if true assume longitude is given before latitude 00193 * in the absence of hemisphere designators (default false). 00194 * @exception GeographicErr if \e dmsa or \e dmsb is malformed. 00195 * @exception GeographicErr if \e dmsa and \e dmsb are both interpreted as 00196 * latitudes. 00197 * @exception GeographicErr if \e dmsa and \e dmsb are both interpreted as 00198 * longitudes. 00199 * @exception GeographicErr if decoded latitude is not in [−90°, 00200 * 90°]. 00201 * @exception GeographicErr if decoded longitude is not in 00202 * [−540°, 540°). 00203 * 00204 * By default, the \e lat (resp., \e lon) is assigned to the results of 00205 * decoding \e dmsa (resp., \e dmsb). However this is overridden if either 00206 * \e dmsa or \e dmsb contain a latitude or longitude hemisphere designator 00207 * (N, S, E, W). If an exception is thrown, \e lat and \e lon are 00208 * unchanged. 00209 **********************************************************************/ 00210 static void DecodeLatLon(System::String^ dmsa, System::String^ dmsb, 00211 [System::Runtime::InteropServices::Out] double% lat, 00212 [System::Runtime::InteropServices::Out] double% lon, 00213 bool swaplatlong ); 00214 00215 /** 00216 * Convert a string to an angle in degrees. 00217 * 00218 * @param[in] angstr input string. 00219 * @exception GeographicErr if \e angstr is malformed. 00220 * @exception GeographicErr if \e angstr includes a hemisphere designator. 00221 * @return angle (degrees) 00222 * 00223 * No hemisphere designator is allowed and no check is done on the range of 00224 * the result. 00225 **********************************************************************/ 00226 static double DecodeAngle(System::String^ angstr); 00227 00228 /** 00229 * Convert a string to an azimuth in degrees. 00230 * 00231 * @param[in] azistr input string. 00232 * @exception GeographicErr if \e azistr is malformed. 00233 * @exception GeographicErr if \e azistr includes a N/S designator. 00234 * @exception GeographicErr if decoded azimuth is not in 00235 * [−540°, 540°). 00236 * @return azimuth (degrees) reduced to the range [−180°, 00237 * 180°). 00238 * 00239 * A hemisphere designator E/W can be used; the result is multiplied by 00240 * −1 if W is present. 00241 **********************************************************************/ 00242 static double DecodeAzimuth(System::String^ azistr); 00243 00244 /** 00245 * Convert angle (in degrees) into a DMS string (using d, ', and "). 00246 * 00247 * @param[in] angle input angle (degrees) 00248 * @param[in] trailing DMS::component value indicating the trailing units 00249 * on the string and this is given as a decimal number if necessary. 00250 * @param[in] prec the number of digits after the decimal point for the 00251 * trailing component. 00252 * @param[in] ind DMS::flag value indicated additional formatting. 00253 * @param[in] dmssep if non-null, use as the DMS separator character 00254 * (instead of d, ', " delimiters). 00255 * @exception GeographicErr if memory for the string can't be allocated. 00256 * @return formatted string 00257 * 00258 * The interpretation of \e ind is as follows: 00259 * - ind == DMS::NONE, signed result no leading zeros on degrees except in 00260 * the units place, e.g., -8d03'. 00261 * - ind == DMS::LATITUDE, trailing N or S hemisphere designator, no sign, 00262 * pad degrees to 2 digits, e.g., 08d03'S. 00263 * - ind == DMS::LONGITUDE, trailing E or W hemisphere designator, no 00264 * sign, pad degrees to 3 digits, e.g., 008d03'W. 00265 * - ind == DMS::AZIMUTH, convert to the range [0, 360°), no 00266 * sign, pad degrees to 3 digits, , e.g., 351d57'. 00267 * . 00268 * The integer parts of the minutes and seconds components are always given 00269 * with 2 digits. 00270 **********************************************************************/ 00271 static System::String^ Encode(double angle, Component trailing, unsigned prec, 00272 Flag ind, char dmssep ); 00273 00274 /** 00275 * Convert angle into a DMS string (using d, ', and ") selecting the 00276 * trailing component based on the precision. 00277 * 00278 * @param[in] angle input angle (degrees) 00279 * @param[in] prec the precision relative to 1 degree. 00280 * @param[in] ind DMS::flag value indicated additional formatting. 00281 * @param[in] dmssep if non-null, use as the DMS separator character 00282 * (instead of d, ', " delimiters). 00283 * @exception std::bad_alloc if memory for the string can't be allocated. 00284 * @return formatted string 00285 * 00286 * \e prec indicates the precision relative to 1 degree, e.g., \e prec = 3 00287 * gives a result accurate to 0.1' and \e prec = 4 gives a result accurate 00288 * to 1". \e ind is interpreted as in DMS::Encode with the additional 00289 * facility that DMS::NUMBER represents \e angle as a number in fixed 00290 * format with precision \e prec. 00291 **********************************************************************/ 00292 static System::String^ Encode(double angle, unsigned prec, Flag ind, 00293 char dmssep ); 00294 00295 /** 00296 * Split angle into degrees and minutes 00297 * 00298 * @param[in] ang angle (degrees) 00299 * @param[out] d degrees (an integer returned as a double) 00300 * @param[out] m arc minutes. 00301 **********************************************************************/ 00302 static void Encode(double ang, 00303 [System::Runtime::InteropServices::Out] double% d, 00304 [System::Runtime::InteropServices::Out] double% m) 00305 { 00306 d = int(ang); m = 60 * (ang - d); 00307 } 00308 00309 /** 00310 * Split angle into degrees and minutes and seconds. 00311 * 00312 * @param[in] ang angle (degrees) 00313 * @param[out] d degrees (an integer returned as a double) 00314 * @param[out] m arc minutes (an integer returned as a double) 00315 * @param[out] s arc seconds. 00316 **********************************************************************/ 00317 static void Encode(double ang, 00318 [System::Runtime::InteropServices::Out] double% d, 00319 [System::Runtime::InteropServices::Out] double% m, 00320 [System::Runtime::InteropServices::Out] double% s) 00321 { 00322 d = int(ang); ang = 60 * (ang - d); 00323 m = int(ang); s = 60 * (ang - m); 00324 } 00325 }; 00326 } // namespace NETGeographicLib