Files
aman-es/external/include/GeographicLib/DMS.hpp
2021-11-22 16:16:36 +01:00

406 lines
17 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* \file DMS.hpp
* \brief Header for GeographicLib::DMS class
*
* Copyright (c) Charles Karney (2008-2020) <charles@karney.com> and licensed
* under the MIT/X11 License. For more information, see
* https://geographiclib.sourceforge.io/
**********************************************************************/
#if !defined(GEOGRAPHICLIB_DMS_HPP)
#define GEOGRAPHICLIB_DMS_HPP 1
#include <GeographicLib/Constants.hpp>
#include <GeographicLib/Utility.hpp>
#if defined(_MSC_VER)
// Squelch warnings about dll vs vector and constant conditional expressions
# pragma warning (push)
# pragma warning (disable: 4251 4127)
#endif
namespace GeographicLib {
/**
* \brief Convert between degrees and the %DMS representation
*
* Parse a string representing degree, minutes, and seconds and return the
* angle in degrees and format an angle in degrees as degree, minutes, and
* seconds. In addition, handle NANs and infinities on input and output.
*
* Example of use:
* \include example-DMS.cpp
**********************************************************************/
class GEOGRAPHICLIB_EXPORT DMS {
public:
/**
* Indicator for presence of hemisphere indicator (N/S/E/W) on latitudes
* and longitudes.
**********************************************************************/
enum flag {
/**
* No indicator present.
* @hideinitializer
**********************************************************************/
NONE = 0,
/**
* Latitude indicator (N/S) present.
* @hideinitializer
**********************************************************************/
LATITUDE = 1,
/**
* Longitude indicator (E/W) present.
* @hideinitializer
**********************************************************************/
LONGITUDE = 2,
/**
* Used in Encode to indicate output of an azimuth in [000, 360) with no
* letter indicator.
* @hideinitializer
**********************************************************************/
AZIMUTH = 3,
/**
* Used in Encode to indicate output of a plain number.
* @hideinitializer
**********************************************************************/
NUMBER = 4,
};
/**
* Indicator for trailing units on an angle.
**********************************************************************/
enum component {
/**
* Trailing unit is degrees.
* @hideinitializer
**********************************************************************/
DEGREE = 0,
/**
* Trailing unit is arc minutes.
* @hideinitializer
**********************************************************************/
MINUTE = 1,
/**
* Trailing unit is arc seconds.
* @hideinitializer
**********************************************************************/
SECOND = 2,
};
private:
typedef Math::real real;
// Replace all occurrences of pat by c. If c is NULL remove pat.
static void replace(std::string& s, const std::string& pat, char c) {
std::string::size_type p = 0;
int count = c ? 1 : 0;
while (true) {
p = s.find(pat, p);
if (p == std::string::npos)
break;
s.replace(p, pat.length(), count, c);
}
}
static const char* const hemispheres_;
static const char* const signs_;
static const char* const digits_;
static const char* const dmsindicators_;
static const char* const components_[3];
static Math::real NumMatch(const std::string& s);
static Math::real InternalDecode(const std::string& dmsa, flag& ind);
DMS(); // Disable constructor
public:
/**
* Convert a string in DMS to an angle.
*
* @param[in] dms string input.
* @param[out] ind a DMS::flag value signaling the presence of a
* hemisphere indicator.
* @exception GeographicErr if \e dms is malformed (see below).
* @return angle (degrees).
*
* Degrees, minutes, and seconds are indicated by the characters d, '
* (single quote), &quot; (double quote), and these components may only be
* given in this order. Any (but not all) components may be omitted and
* other symbols (e.g., the &deg; symbol for degrees and the unicode prime
* and double prime symbols for minutes and seconds) may be substituted;
* two single quotes can be used instead of &quot;. The last component
* indicator may be omitted and is assumed to be the next smallest unit
* (thus 33d10 is interpreted as 33d10'). The final component may be a
* decimal fraction but the non-final components must be integers. Instead
* of using d, ', and &quot; to indicate degrees, minutes, and seconds, :
* (colon) may be used to <i>separate</i> these components (numbers must
* appear before and after each colon); thus 50d30'10.3&quot; may be
* written as 50:30:10.3, 5.5' may be written 0:5.5, and so on. The
* integer parts of the minutes and seconds components must be less
* than 60. A single leading sign is permitted. A hemisphere designator
* (N, E, W, S) may be added to the beginning or end of the string. The
* result is multiplied by the implied sign of the hemisphere designator
* (negative for S and W). In addition \e ind is set to DMS::LATITUDE if N
* or S is present, to DMS::LONGITUDE if E or W is present, and to
* DMS::NONE otherwise. Throws an error on a malformed string. No check
* is performed on the range of the result. Examples of legal and illegal
* strings are
* - <i>LEGAL</i> (all the entries on each line are equivalent)
* - -20.51125, 20d30'40.5&quot;S, -20&deg;30'40.5, -20d30.675,
* N-20d30'40.5&quot;, -20:30:40.5
* - 4d0'9, 4d9&quot;, 4d9'', 4:0:9, 004:00:09, 4.0025, 4.0025d, 4d0.15,
* 04:.15
* - 4:59.99999999999999, 4:60.0, 4:59:59.9999999999999, 4:59:60.0, 5
* - <i>ILLEGAL</i> (the exception thrown explains the problem)
* - 4d5&quot;4', 4::5, 4:5:, :4:5, 4d4.5'4&quot;, -N20.5, 1.8e2d, 4:60,
* 4:59:60
*
* The decoding operation can also perform addition and subtraction
* operations. If the string includes <i>internal</i> signs (i.e., not at
* the beginning nor immediately after an initial hemisphere designator),
* then the string is split immediately before such signs and each piece is
* decoded according to the above rules and the results added; thus
* <code>S3-2.5+4.1N</code> is parsed as the sum of <code>S3</code>,
* <code>-2.5</code>, <code>+4.1N</code>. Any piece can include a
* hemisphere designator; however, if multiple designators are given, they
* must compatible; e.g., you cannot mix N and E. In addition, the
* designator can appear at the beginning or end of the first piece, but
* must be at the end of all subsequent pieces (a hemisphere designator is
* not allowed after the initial sign). Examples of legal and illegal
* combinations are
* - <i>LEGAL</i> (these are all equivalent)
* - 070:00:45, 70:01:15W+0:0.5, 70:01:15W-0:0:30W, W70:01:15+0:0:30E
* - <i>ILLEGAL</i> (the exception thrown explains the problem)
* - 70:01:15W+0:0:15N, W70:01:15+W0:0:15
*
* \warning The "exponential" notation is not recognized. Thus
* <code>7.0E1</code> is illegal, while <code>7.0E+1</code> is parsed as
* <code>(7.0E) + (+1)</code>, yielding the same result as
* <code>8.0E</code>.
*
* \note At present, all the string handling in the C++ implementation of
* %GeographicLib is with 8-bit characters. The support for unicode
* symbols for degrees, minutes, and seconds is therefore via the
* <a href="https://en.wikipedia.org/wiki/UTF-8">UTF-8</a> encoding. (The
* JavaScript implementation of this class uses unicode natively, of
* course.)
*
* Here is the list of Unicode symbols supported for degrees, minutes,
* seconds, and the plus and minus signs; various symbols denoting variants
* of a space, which may separate the components of a DMS string, are
* removed:
* - degrees:
* - d, D lower and upper case letters
* - U+00b0 degree symbol (&deg;)
* - U+00ba masculine ordinal indicator (&ordm;)
* - U+2070 superscript zero (⁰)
* - U+02da ring above (˚)
* - U+2218 compose function (∘)
* - * the <a href="https://grid.nga.mil">GRiD</a> symbol for degrees
* - minutes:
* - ' apostrophe
* - ` grave accent
* - U+2032 prime (&prime;)
* - U+2035 back prime ()
* - U+00b4 acute accent (&acute;)
* - U+2018 left single quote (&lsquo;)
* - U+2019 right single quote (&rsquo;)
* - U+201b reversed-9 single quote ()
* - U+02b9 modifier letter prime (ʹ)
* - U+02ca modifier letter acute accent (ˊ)
* - U+02cb modifier letter grave accent (ˋ)
* - seconds:
* - &quot; quotation mark
* - U+2033 double prime (&Prime;)
* - U+2036 reversed double prime (‶)
* + U+02dd double acute accent (˝)
* - U+201c left double quote (&ldquo;)
* - U+201d right double quote (&rdquo;)
* - U+201f reversed-9 double quote (‟)
* - U+02ba modifier letter double prime (ʺ)
* - '&nbsp;' any two consecutive symbols for minutes
* - plus sign:
* - + plus
* - U+2795 heavy plus ()
* - U+2064 invisible plus (||)
* - minus sign:
* - - hyphen
* - U+2010 dash ()
* - U+2011 non-breaking hyphen ()
* - U+2013 en dash (&ndash;)
* - U+2014 em dash (&mdash;)
* - U+2212 minus sign (&minus;)
* - U+2796 heavy minus ()
* - ignored spaces:
* - U+00a0 non-breaking space
* - U+2007 figure space (||)
* - U+2009 thin space (|&thinsp;|)
* - U+200a hair space (||)
* - U+200b invisible space (||)
* - U+202f narrow space (||)
* - U+2063 invisible separator (||)
* .
* The codes with a leading zero byte, e.g., U+00b0, are accepted in their
* UTF-8 coded form 0xc2 0xb0 and as a single byte 0xb0.
**********************************************************************/
static Math::real Decode(const std::string& dms, flag& ind);
/**
* Convert DMS to an angle.
*
* @param[in] d degrees.
* @param[in] m arc minutes.
* @param[in] s arc seconds.
* @return angle (degrees)
*
* This does not propagate the sign on \e d to the other components,
* so -3d20' would need to be represented as - DMS::Decode(3.0, 20.0) or
* DMS::Decode(-3.0, -20.0).
**********************************************************************/
static Math::real Decode(real d, real m = 0, real s = 0)
{ return d + (m + s / 60) / 60; }
/**
* Convert a pair of strings to latitude and longitude.
*
* @param[in] dmsa first string.
* @param[in] dmsb second string.
* @param[out] lat latitude (degrees).
* @param[out] lon longitude (degrees).
* @param[in] longfirst if true assume longitude is given before latitude
* in the absence of hemisphere designators (default false).
* @exception GeographicErr if \e dmsa or \e dmsb is malformed.
* @exception GeographicErr if \e dmsa and \e dmsb are both interpreted as
* latitudes.
* @exception GeographicErr if \e dmsa and \e dmsb are both interpreted as
* longitudes.
* @exception GeographicErr if decoded latitude is not in [&minus;90&deg;,
* 90&deg;].
*
* By default, the \e lat (resp., \e lon) is assigned to the results of
* decoding \e dmsa (resp., \e dmsb). However this is overridden if either
* \e dmsa or \e dmsb contain a latitude or longitude hemisphere designator
* (N, S, E, W). If an exception is thrown, \e lat and \e lon are
* unchanged.
**********************************************************************/
static void DecodeLatLon(const std::string& dmsa, const std::string& dmsb,
real& lat, real& lon,
bool longfirst = false);
/**
* Convert a string to an angle in degrees.
*
* @param[in] angstr input string.
* @exception GeographicErr if \e angstr is malformed.
* @exception GeographicErr if \e angstr includes a hemisphere designator.
* @return angle (degrees)
*
* No hemisphere designator is allowed and no check is done on the range of
* the result.
**********************************************************************/
static Math::real DecodeAngle(const std::string& angstr);
/**
* Convert a string to an azimuth in degrees.
*
* @param[in] azistr input string.
* @exception GeographicErr if \e azistr is malformed.
* @exception GeographicErr if \e azistr includes a N/S designator.
* @return azimuth (degrees) reduced to the range [&minus;180&deg;,
* 180&deg;].
*
* A hemisphere designator E/W can be used; the result is multiplied by
* &minus;1 if W is present.
**********************************************************************/
static Math::real DecodeAzimuth(const std::string& azistr);
/**
* Convert angle (in degrees) into a DMS string (using d, ', and &quot;).
*
* @param[in] angle input angle (degrees)
* @param[in] trailing DMS::component value indicating the trailing units
* of the string (this component is given as a decimal number if
* necessary).
* @param[in] prec the number of digits after the decimal point for the
* trailing component.
* @param[in] ind DMS::flag value indicating additional formatting.
* @param[in] dmssep if non-null, use as the DMS separator character
* (instead of d, ', &quot; delimiters).
* @exception std::bad_alloc if memory for the string can't be allocated.
* @return formatted string
*
* The interpretation of \e ind is as follows:
* - ind == DMS::NONE, signed result no leading zeros on degrees except in
* the units place, e.g., -8d03'.
* - ind == DMS::LATITUDE, trailing N or S hemisphere designator, no sign,
* pad degrees to 2 digits, e.g., 08d03'S.
* - ind == DMS::LONGITUDE, trailing E or W hemisphere designator, no
* sign, pad degrees to 3 digits, e.g., 008d03'W.
* - ind == DMS::AZIMUTH, convert to the range [0, 360&deg;), no
* sign, pad degrees to 3 digits, e.g., 351d57'.
* .
* The integer parts of the minutes and seconds components are always given
* with 2 digits.
**********************************************************************/
static std::string Encode(real angle, component trailing, unsigned prec,
flag ind = NONE, char dmssep = char(0));
/**
* Convert angle into a DMS string (using d, ', and &quot;) selecting the
* trailing component based on the precision.
*
* @param[in] angle input angle (degrees)
* @param[in] prec the precision relative to 1 degree.
* @param[in] ind DMS::flag value indicated additional formatting.
* @param[in] dmssep if non-null, use as the DMS separator character
* (instead of d, ', &quot; delimiters).
* @exception std::bad_alloc if memory for the string can't be allocated.
* @return formatted string
*
* \e prec indicates the precision relative to 1 degree, e.g., \e prec = 3
* gives a result accurate to 0.1' and \e prec = 4 gives a result accurate
* to 1&quot;. \e ind is interpreted as in DMS::Encode with the additional
* facility that DMS::NUMBER represents \e angle as a number in fixed
* format with precision \e prec.
**********************************************************************/
static std::string Encode(real angle, unsigned prec, flag ind = NONE,
char dmssep = char(0)) {
return ind == NUMBER ? Utility::str(angle, int(prec)) :
Encode(angle,
prec < 2 ? DEGREE : (prec < 4 ? MINUTE : SECOND),
prec < 2 ? prec : (prec < 4 ? prec - 2 : prec - 4),
ind, dmssep);
}
/**
* Split angle into degrees and minutes
*
* @param[in] ang angle (degrees)
* @param[out] d degrees (an integer returned as a real)
* @param[out] m arc minutes.
**********************************************************************/
static void Encode(real ang, real& d, real& m) {
d = int(ang); m = 60 * (ang - d);
}
/**
* Split angle into degrees and minutes and seconds.
*
* @param[in] ang angle (degrees)
* @param[out] d degrees (an integer returned as a real)
* @param[out] m arc minutes (an integer returned as a real)
* @param[out] s arc seconds.
**********************************************************************/
static void Encode(real ang, real& d, real& m, real& s) {
d = int(ang); ang = 60 * (ang - d);
m = int(ang); s = 60 * (ang - m);
}
};
} // namespace GeographicLib
#if defined(_MSC_VER)
# pragma warning (pop)
#endif
#endif // GEOGRAPHICLIB_DMS_HPP