NETGeographicLib 1.52
Loading...
Searching...
No Matches
DMS.h
Go to the documentation of this file.
1/**
2 * \file NETGeographicLib/DMS.h
3 * \brief Header for NETGeographicLib::DMS class
4 *
5 * NETGeographicLib is copyright (c) Scott Heiman (2013)
6 * GeographicLib is Copyright (c) Charles Karney (2010-2012)
7 * <charles@karney.com> and licensed under the MIT/X11 License.
8 * For more information, see
9 * https://geographiclib.sourceforge.io/
10 **********************************************************************/
11#pragma once
12
13namespace NETGeographicLib
14{
15 /**
16 * \brief .NET wrapper for GeographicLib::DMS.
17 *
18 * Parse a string representing degree, minutes, and seconds and return the
19 * angle in degrees and format an angle in degrees as degree, minutes, and
20 * seconds. In addition, handle NANs and infinities on input and output.
21 *
22 * C# Example:
23 * \include example-DMS.cs
24 * Managed C++ Example:
25 * \include example-DMS.cpp
26 * Visual Basic Example:
27 * \include example-DMS.vb
28 **********************************************************************/
29public ref class DMS
30{
31public:
32 /**
33 * Indicator for presence of hemisphere indicator (N/S/E/W) on latitudes
34 * and longitudes.
35 **********************************************************************/
36 enum class Flag {
37 /**
38 * No indicator present.
39 * @hideinitializer
40 **********************************************************************/
41 NONE = 0,
42 /**
43 * Latitude indicator (N/S) present.
44 * @hideinitializer
45 **********************************************************************/
46 LATITUDE = 1,
47 /**
48 * Longitude indicator (E/W) present.
49 * @hideinitializer
50 **********************************************************************/
51 LONGITUDE = 2,
52 /**
53 * Used in Encode to indicate output of an azimuth in [000, 360) with no
54 * letter indicator.
55 * @hideinitializer
56 **********************************************************************/
57 AZIMUTH = 3,
58 /**
59 * Used in Encode to indicate output of a plain number.
60 * @hideinitializer
61 **********************************************************************/
62 NUMBER = 4,
63 };
64
65 /**
66 * Indicator for trailing units on an angle.
67 **********************************************************************/
68 enum class Component {
69 /**
70 * Trailing unit is degrees.
71 * @hideinitializer
72 **********************************************************************/
73 DEGREE = 0,
74 /**
75 * Trailing unit is arc minutes.
76 * @hideinitializer
77 **********************************************************************/
78 MINUTE = 1,
79 /**
80 * Trailing unit is arc seconds.
81 * @hideinitializer
82 **********************************************************************/
83 SECOND = 2,
84 };
85
86 /**
87 * Convert a string in DMS to an angle.
88 *
89 * @param[in] dms string input.
90 * @param[out] ind a DMS::flag value signaling the presence of a
91 * hemisphere indicator.
92 * @exception GeographicErr if \e dms is malformed (see below).
93 * @return angle (degrees).
94 *
95 * Degrees, minutes, and seconds are indicated by the characters d, '
96 * (single quote), &quot; (double quote), and these components may only be
97 * given in this order. Any (but not all) components may be omitted and
98 * other symbols (e.g., the &deg; symbol for degrees and the unicode prime
99 * and double prime symbols for minutes and seconds) may be substituted;
100 * two single quotes can be used instead of &quot;. The last component
101 * indicator may be omitted and is assumed to be the next smallest unit
102 * (thus 33d10 is interpreted as 33d10'). The final component may be a
103 * decimal fraction but the non-final components must be integers. Instead
104 * of using d, ', and &quot; to indicate degrees, minutes, and seconds, :
105 * (colon) may be used to <i>separate</i> these components (numbers must
106 * appear before and after each colon); thus 50d30'10.3&quot; may be
107 * written as 50:30:10.3, 5.5' may be written 0:5.5, and so on. The
108 * integer parts of the minutes and seconds components must be less
109 * than 60. A single leading sign is permitted. A hemisphere designator
110 * (N, E, W, S) may be added to the beginning or end of the string. The
111 * result is multiplied by the implied sign of the hemisphere designator
112 * (negative for S and W). In addition \e ind is set to DMS::LATITUDE if N
113 * or S is present, to DMS::LONGITUDE if E or W is present, and to
114 * DMS::NONE otherwise. Throws an error on a malformed string. No check
115 * is performed on the range of the result. Examples of legal and illegal
116 * strings are
117 * - <i>LEGAL</i> (all the entries on each line are equivalent)
118 * - -20.51125, 20d30'40.5&quot;S, -20&deg;30'40.5, -20d30.675,
119 * N-20d30'40.5&quot;, -20:30:40.5
120 * - 4d0'9, 4d9&quot;, 4d9'', 4:0:9, 004:00:09, 4.0025, 4.0025d, 4d0.15,
121 * 04:.15
122 * - 4:59.99999999999999, 4:60.0, 4:59:59.9999999999999, 4:59:60.0, 5
123 * - <i>ILLEGAL</i> (the exception thrown explains the problem)
124 * - 4d5&quot;4', 4::5, 4:5:, :4:5, 4d4.5'4&quot;, -N20.5, 1.8e2d, 4:60,
125 * 4:59:60
126 *
127 * The decoding operation can also perform addition and subtraction
128 * operations. If the string includes <i>internal</i> signs (i.e., not at
129 * the beginning nor immediately after an initial hemisphere designator),
130 * then the string is split immediately before such signs and each piece is
131 * decoded according to the above rules and the results added; thus
132 * <code>S3-2.5+4.1N</code> is parsed as the sum of <code>S3</code>,
133 * <code>-2.5</code>, <code>+4.1N</code>. Any piece can include a
134 * hemisphere designator; however, if multiple designators are given, they
135 * must compatible; e.g., you cannot mix N and E. In addition, the
136 * designator can appear at the beginning or end of the first piece, but
137 * must be at the end of all subsequent pieces (a hemisphere designator is
138 * not allowed after the initial sign). Examples of legal and illegal
139 * combinations are
140 * - <i>LEGAL</i> (these are all equivalent)
141 * - 070:00:45, 70:01:15W+0:0.5, 70:01:15W-0:0:30W, W70:01:15+0:0:30E
142 * - <i>ILLEGAL</i> (the exception thrown explains the problem)
143 * - 70:01:15W+0:0:15N, W70:01:15+W0:0:15
144 *
145 * <b>WARNING:</b> "Exponential" notation is not recognized. Thus
146 * <code>7.0E1</code> is illegal, while <code>7.0E+1</code> is parsed as
147 * <code>(7.0E) + (+1)</code>, yielding the same result as
148 * <code>8.0E</code>.
149 *
150 * <b>NOTE:</b> At present, all the string handling in the C++
151 * implementation %GeographicLib is with 8-bit characters. The support for
152 * unicode symbols for degrees, minutes, and seconds is therefore via the
153 * <a href="https://en.wikipedia.org/wiki/UTF-8">UTF-8</a> encoding. (The
154 * JavaScript implementation of this class uses unicode natively, of
155 * course.)
156 *
157 * Here is the list of Unicode symbols supported for degrees, minutes,
158 * seconds:
159 * - degrees:
160 * - d, D lower and upper case letters
161 * - U+00b0 degree symbol (&deg;)
162 * - U+00ba masculine ordinal indicator
163 * - U+2070 superscript zero
164 * - U+02da ring above
165 * - minutes:
166 * - ' apostrophe
167 * - U+2032 prime (&prime;)
168 * - U+00b4 acute accent
169 * - U+2019 right single quote (&rsquo;)
170 * - seconds:
171 * - &quot; quotation mark
172 * - U+2033 double prime (&Prime;)
173 * - U+201d right double quote (&rdquo;)
174 * - '&nbsp;' any two consecutive symbols for minutes
175 * .
176 * The codes with a leading zero byte, e.g., U+00b0, are accepted in their
177 * UTF-8 coded form 0xc2 0xb0 and as a single byte 0xb0.
178 **********************************************************************/
179 static double Decode(System::String^ dms,
180 [System::Runtime::InteropServices::Out] Flag% ind);
181
182 /**
183 * Convert DMS to an angle.
184 *
185 * @param[in] d degrees.
186 * @param[in] m arc minutes.
187 * @param[in] s arc seconds.
188 * @return angle (degrees)
189 *
190 * This does not propagate the sign on \e d to the other components, so
191 * -3d20' would need to be represented as - DMS::Decode(3.0, 20.0) or
192 * DMS::Decode(-3.0, -20.0).
193 **********************************************************************/
194 static double Decode(double d, double m, double s )
195 { return d + (m + s/double(60))/double(60); }
196
197 /**
198 * Convert a pair of strings to latitude and longitude.
199 *
200 * @param[in] dmsa first string.
201 * @param[in] dmsb second string.
202 * @param[out] lat latitude (degrees).
203 * @param[out] lon longitude (degrees).
204 * @param[in] longfirst if true assume longitude is given before latitude
205 * in the absence of hemisphere designators (default false).
206 * @exception GeographicErr if \e dmsa or \e dmsb is malformed.
207 * @exception GeographicErr if \e dmsa and \e dmsb are both interpreted as
208 * latitudes.
209 * @exception GeographicErr if \e dmsa and \e dmsb are both interpreted as
210 * longitudes.
211 * @exception GeographicErr if decoded latitude is not in [&minus;90&deg;,
212 * 90&deg;].
213 *
214 * By default, the \e lat (resp., \e lon) is assigned to the results of
215 * decoding \e dmsa (resp., \e dmsb). However this is overridden if either
216 * \e dmsa or \e dmsb contain a latitude or longitude hemisphere designator
217 * (N, S, E, W). If an exception is thrown, \e lat and \e lon are
218 * unchanged.
219 **********************************************************************/
220 static void DecodeLatLon(System::String^ dmsa, System::String^ dmsb,
221 [System::Runtime::InteropServices::Out] double% lat,
222 [System::Runtime::InteropServices::Out] double% lon,
223 bool longfirst );
224
225 /**
226 * Convert a string to an angle in degrees.
227 *
228 * @param[in] angstr input string.
229 * @exception GeographicErr if \e angstr is malformed.
230 * @exception GeographicErr if \e angstr includes a hemisphere designator.
231 * @return angle (degrees)
232 *
233 * No hemisphere designator is allowed and no check is done on the range of
234 * the result.
235 **********************************************************************/
236 static double DecodeAngle(System::String^ angstr);
237
238 /**
239 * Convert a string to an azimuth in degrees.
240 *
241 * @param[in] azistr input string.
242 * @exception GeographicErr if \e azistr is malformed.
243 * @exception GeographicErr if \e azistr includes a N/S designator.
244 * @return azimuth (degrees) reduced to the range [&minus;180&deg;,
245 * 180&deg;).
246 *
247 * A hemisphere designator E/W can be used; the result is multiplied by
248 * &minus;1 if W is present.
249 **********************************************************************/
250 static double DecodeAzimuth(System::String^ azistr);
251
252 /**
253 * Convert angle (in degrees) into a DMS string (using d, ', and &quot;).
254 *
255 * @param[in] angle input angle (degrees)
256 * @param[in] trailing DMS::component value indicating the trailing units
257 * on the string and this is given as a decimal number if necessary.
258 * @param[in] prec the number of digits after the decimal point for the
259 * trailing component.
260 * @param[in] ind DMS::flag value indicated additional formatting.
261 * @param[in] dmssep if non-null, use as the DMS separator character
262 * (instead of d, ', &quot; delimiters).
263 * @exception GeographicErr if memory for the string can't be allocated.
264 * @return formatted string
265 *
266 * The interpretation of \e ind is as follows:
267 * - ind == DMS::NONE, signed result no leading zeros on degrees except in
268 * the units place, e.g., -8d03'.
269 * - ind == DMS::LATITUDE, trailing N or S hemisphere designator, no sign,
270 * pad degrees to 2 digits, e.g., 08d03'S.
271 * - ind == DMS::LONGITUDE, trailing E or W hemisphere designator, no
272 * sign, pad degrees to 3 digits, e.g., 008d03'W.
273 * - ind == DMS::AZIMUTH, convert to the range [0, 360&deg;), no
274 * sign, pad degrees to 3 digits, , e.g., 351d57'.
275 * .
276 * The integer parts of the minutes and seconds components are always given
277 * with 2 digits.
278 **********************************************************************/
279 static System::String^ Encode(double angle, Component trailing, unsigned prec,
280 Flag ind, char dmssep );
281
282 /**
283 * Convert angle into a DMS string (using d, ', and &quot;) selecting the
284 * trailing component based on the precision.
285 *
286 * @param[in] angle input angle (degrees)
287 * @param[in] prec the precision relative to 1 degree.
288 * @param[in] ind DMS::flag value indicated additional formatting.
289 * @param[in] dmssep if non-null, use as the DMS separator character
290 * (instead of d, ', &quot; delimiters).
291 * @exception std::bad_alloc if memory for the string can't be allocated.
292 * @return formatted string
293 *
294 * \e prec indicates the precision relative to 1 degree, e.g., \e prec = 3
295 * gives a result accurate to 0.1' and \e prec = 4 gives a result accurate
296 * to 1&quot;. \e ind is interpreted as in DMS::Encode with the additional
297 * facility that DMS::NUMBER represents \e angle as a number in fixed
298 * format with precision \e prec.
299 **********************************************************************/
300 static System::String^ Encode(double angle, unsigned prec, Flag ind,
301 char dmssep );
302
303 /**
304 * Split angle into degrees and minutes
305 *
306 * @param[in] ang angle (degrees)
307 * @param[out] d degrees (an integer returned as a double)
308 * @param[out] m arc minutes.
309 **********************************************************************/
310 static void Encode(double ang,
311 [System::Runtime::InteropServices::Out] double% d,
312 [System::Runtime::InteropServices::Out] double% m)
313 {
314 d = int(ang); m = 60 * (ang - d);
315 }
316
317 /**
318 * Split angle into degrees and minutes and seconds.
319 *
320 * @param[in] ang angle (degrees)
321 * @param[out] d degrees (an integer returned as a double)
322 * @param[out] m arc minutes (an integer returned as a double)
323 * @param[out] s arc seconds.
324 **********************************************************************/
325 static void Encode(double ang,
326 [System::Runtime::InteropServices::Out] double% d,
327 [System::Runtime::InteropServices::Out] double% m,
328 [System::Runtime::InteropServices::Out] double% s)
329 {
330 d = int(ang); ang = 60 * (ang - d);
331 m = int(ang); s = 60 * (ang - m);
332 }
333};
334} // namespace NETGeographicLib
.NET wrapper for GeographicLib::DMS.
Definition: DMS.h:30
static void Encode(double ang, [System::Runtime::InteropServices::Out] double% d, [System::Runtime::InteropServices::Out] double% m)
Definition: DMS.h:310
static double Decode(double d, double m, double s)
Definition: DMS.h:194
static double DecodeAzimuth(System::String^ azistr)
static System::String ^ Encode(double angle, unsigned prec, Flag ind, char dmssep)
static void DecodeLatLon(System::String^ dmsa, System::String^ dmsb, [System::Runtime::InteropServices::Out] double% lat, [System::Runtime::InteropServices::Out] double% lon, bool longfirst)
static double DecodeAngle(System::String^ angstr)
static void Encode(double ang, [System::Runtime::InteropServices::Out] double% d, [System::Runtime::InteropServices::Out] double% m, [System::Runtime::InteropServices::Out] double% s)
Definition: DMS.h:325
static System::String ^ Encode(double angle, Component trailing, unsigned prec, Flag ind, char dmssep)
static double Decode(System::String^ dms, [System::Runtime::InteropServices::Out] Flag% ind)