src/DMS.cpp
Go to the documentation of this file.
1 
10 #include <GeographicLib/DMS.hpp>
12 
13 #if defined(_MSC_VER)
14 // Squelch warnings about constant conditional expressions
15 # pragma warning (disable: 4127)
16 #endif
17 
18 namespace GeographicLib {
19 
20  using namespace std;
21 
22  const char* const DMS::hemispheres_ = "SNWE";
23  const char* const DMS::signs_ = "-+";
24  const char* const DMS::digits_ = "0123456789";
25  const char* const DMS::dmsindicators_ = "D'\":";
26  const char* const DMS::components_[] = {"degrees", "minutes", "seconds"};
27 
28  Math::real DMS::Decode(const std::string& dms, flag& ind) {
29  string dmsa = dms;
30  replace(dmsa, "\xc2\xb0", 'd'); // U+00b0 degree symbol
31  replace(dmsa, "\xc2\xba", 'd'); // U+00ba alt symbol
32  replace(dmsa, "\xe2\x81\xb0", 'd'); // U+2070 sup zero
33  replace(dmsa, "\xcb\x9a", 'd'); // U+02da ring above
34  replace(dmsa, "\xe2\x80\xb2", '\''); // U+2032 prime
35  replace(dmsa, "\xc2\xb4", '\''); // U+00b4 acute accent
36  replace(dmsa, "\xe2\x80\x99", '\''); // U+2019 right single quote
37  replace(dmsa, "\xe2\x80\xb3", '"'); // U+2033 double prime
38  replace(dmsa, "\xe2\x80\x9d", '"'); // U+201d right double quote
39  replace(dmsa, "\xe2\x88\x92", '-'); // U+2212 minus sign
40  replace(dmsa, "\xb0", 'd'); // 0xb0 bare degree symbol
41  replace(dmsa, "\xba", 'd'); // 0xba bare alt symbol
42  replace(dmsa, "\xb4", '\''); // 0xb4 bare acute accent
43  replace(dmsa, "''", '"'); // '' -> "
44  string::size_type
45  beg = 0,
46  end = unsigned(dmsa.size());
47  while (beg < end && isspace(dmsa[beg]))
48  ++beg;
49  while (beg < end && isspace(dmsa[end - 1]))
50  --end;
51  // The trimmed string in [beg, end)
52  real v = 0;
53  int i = 0;
54  flag ind1 = NONE;
55  // p is pointer to the next piece that needs decoding
56  for (string::size_type p = beg, pb; p < end; p = pb, ++i) {
57  string::size_type pa = p;
58  // Skip over initial hemisphere letter (for i == 0)
59  if (i == 0 && Utility::lookup(hemispheres_, dmsa[pa]) >= 0)
60  ++pa;
61  // Skip over initial sign (checking for it if i == 0)
62  if (i > 0 || (pa < end && Utility::lookup(signs_, dmsa[pa]) >= 0))
63  ++pa;
64  // Find next sign
65  pb = min(dmsa.find_first_of(signs_, pa), end);
66  flag ind2 = NONE;
67  v += InternalDecode(dmsa.substr(p, pb - p), ind2);
68  if (ind1 == NONE)
69  ind1 = ind2;
70  else if (!(ind2 == NONE || ind1 == ind2))
71  throw GeographicErr("Incompatible hemisphere specifies in " +
72  dmsa.substr(beg, pb - beg));
73  }
74  if (i == 0)
75  throw GeographicErr("Empty or incomplete DMS string " +
76  dmsa.substr(beg, end - beg));
77  ind = ind1;
78  return v;
79  }
80 
81  Math::real DMS::InternalDecode(const std::string& dmsa, flag& ind) {
82  string errormsg;
83  do { // Executed once (provides the ability to break)
84  int sign = 1;
85  unsigned
86  beg = 0,
87  end = unsigned(dmsa.size());
88  flag ind1 = NONE;
89  int k = -1;
90  if (end > beg && (k = Utility::lookup(hemispheres_, dmsa[beg])) >= 0) {
91  ind1 = (k / 2) ? LONGITUDE : LATITUDE;
92  sign = k % 2 ? 1 : -1;
93  ++beg;
94  }
95  if (end > beg && (k = Utility::lookup(hemispheres_, dmsa[end-1])) >= 0) {
96  if (k >= 0) {
97  if (ind1 != NONE) {
98  if (toupper(dmsa[beg - 1]) == toupper(dmsa[end - 1]))
99  errormsg = "Repeated hemisphere indicators "
100  + Utility::str(dmsa[beg - 1])
101  + " in " + dmsa.substr(beg - 1, end - beg + 1);
102  else
103  errormsg = "Contradictory hemisphere indicators "
104  + Utility::str(dmsa[beg - 1]) + " and "
105  + Utility::str(dmsa[end - 1]) + " in "
106  + dmsa.substr(beg - 1, end - beg + 1);
107  break;
108  }
109  ind1 = (k / 2) ? LONGITUDE : LATITUDE;
110  sign = k % 2 ? 1 : -1;
111  --end;
112  }
113  }
114  if (end > beg && (k = Utility::lookup(signs_, dmsa[beg])) >= 0) {
115  if (k >= 0) {
116  sign *= k ? 1 : -1;
117  ++beg;
118  }
119  }
120  if (end == beg) {
121  errormsg = "Empty or incomplete DMS string " + dmsa;
122  break;
123  }
124  real ipieces[] = {0, 0, 0};
125  real fpieces[] = {0, 0, 0};
126  unsigned npiece = 0;
127  real icurrent = 0;
128  real fcurrent = 0;
129  unsigned ncurrent = 0, p = beg;
130  bool pointseen = false;
131  unsigned digcount = 0, intcount = 0;
132  while (p < end) {
133  char x = dmsa[p++];
134  if ((k = Utility::lookup(digits_, x)) >= 0) {
135  ++ncurrent;
136  if (digcount > 0)
137  ++digcount; // Count of decimal digits
138  else {
139  icurrent = 10 * icurrent + k;
140  ++intcount;
141  }
142  } else if (x == '.') {
143  if (pointseen) {
144  errormsg = "Multiple decimal points in "
145  + dmsa.substr(beg, end - beg);
146  break;
147  }
148  pointseen = true;
149  digcount = 1;
150  } else if ((k = Utility::lookup(dmsindicators_, x)) >= 0) {
151  if (k >= 3) {
152  if (p == end) {
153  errormsg = "Illegal for : to appear at the end of " +
154  dmsa.substr(beg, end - beg);
155  break;
156  }
157  k = npiece;
158  }
159  if (unsigned(k) == npiece - 1) {
160  errormsg = "Repeated " + string(components_[k]) +
161  " component in " + dmsa.substr(beg, end - beg);
162  break;
163  } else if (unsigned(k) < npiece) {
164  errormsg = string(components_[k]) + " component follows "
165  + string(components_[npiece - 1]) + " component in "
166  + dmsa.substr(beg, end - beg);
167  break;
168  }
169  if (ncurrent == 0) {
170  errormsg = "Missing numbers in " + string(components_[k]) +
171  " component of " + dmsa.substr(beg, end - beg);
172  break;
173  }
174  if (digcount > 0) {
175  istringstream s(dmsa.substr(p - intcount - digcount - 1,
176  intcount + digcount));
177  s >> fcurrent;
178  icurrent = 0;
179  }
180  ipieces[k] = icurrent;
181  fpieces[k] = icurrent + fcurrent;
182  if (p < end) {
183  npiece = k + 1;
184  icurrent = fcurrent = 0;
185  ncurrent = digcount = intcount = 0;
186  }
187  } else if (Utility::lookup(signs_, x) >= 0) {
188  errormsg = "Internal sign in DMS string "
189  + dmsa.substr(beg, end - beg);
190  break;
191  } else {
192  errormsg = "Illegal character " + Utility::str(x) + " in DMS string "
193  + dmsa.substr(beg, end - beg);
194  break;
195  }
196  }
197  if (!errormsg.empty())
198  break;
199  if (Utility::lookup(dmsindicators_, dmsa[p - 1]) < 0) {
200  if (npiece >= 3) {
201  errormsg = "Extra text following seconds in DMS string "
202  + dmsa.substr(beg, end - beg);
203  break;
204  }
205  if (ncurrent == 0) {
206  errormsg = "Missing numbers in trailing component of "
207  + dmsa.substr(beg, end - beg);
208  break;
209  }
210  if (digcount > 0) {
211  istringstream s(dmsa.substr(p - intcount - digcount,
212  intcount + digcount));
213  s >> fcurrent;
214  icurrent = 0;
215  }
216  ipieces[npiece] = icurrent;
217  fpieces[npiece] = icurrent + fcurrent;
218  }
219  if (pointseen && digcount == 0) {
220  errormsg = "Decimal point in non-terminal component of "
221  + dmsa.substr(beg, end - beg);
222  break;
223  }
224  // Note that we accept 59.999999... even though it rounds to 60.
225  if (ipieces[1] >= 60 || fpieces[1] > 60 ) {
226  errormsg = "Minutes " + Utility::str(fpieces[1])
227  + " not in range [0, 60)";
228  break;
229  }
230  if (ipieces[2] >= 60 || fpieces[2] > 60) {
231  errormsg = "Seconds " + Utility::str(fpieces[2])
232  + " not in range [0, 60)";
233  break;
234  }
235  ind = ind1;
236  // Assume check on range of result is made by calling routine (which
237  // might be able to offer a better diagnostic).
238  return real(sign) *
239  ( fpieces[2] != 0 ?
240  (60*(60*fpieces[0] + fpieces[1]) + fpieces[2]) / 3600 :
241  ( fpieces[1] != 0 ?
242  (60*fpieces[0] + fpieces[1]) / 60 : fpieces[0] ) );
243  } while (false);
244  real val = Utility::nummatch<real>(dmsa);
245  if (val == 0)
246  throw GeographicErr(errormsg);
247  else
248  ind = NONE;
249  return val;
250  }
251 
252  void DMS::DecodeLatLon(const std::string& stra, const std::string& strb,
253  real& lat, real& lon,
254  bool longfirst) {
255  real a, b;
256  flag ia, ib;
257  a = Decode(stra, ia);
258  b = Decode(strb, ib);
259  if (ia == NONE && ib == NONE) {
260  // Default to lat, long unless longfirst
261  ia = longfirst ? LONGITUDE : LATITUDE;
262  ib = longfirst ? LATITUDE : LONGITUDE;
263  } else if (ia == NONE)
264  ia = flag(LATITUDE + LONGITUDE - ib);
265  else if (ib == NONE)
266  ib = flag(LATITUDE + LONGITUDE - ia);
267  if (ia == ib)
268  throw GeographicErr("Both " + stra + " and "
269  + strb + " interpreted as "
270  + (ia == LATITUDE ? "latitudes" : "longitudes"));
271  real
272  lat1 = ia == LATITUDE ? a : b,
273  lon1 = ia == LATITUDE ? b : a;
274  if (abs(lat1) > 90)
275  throw GeographicErr("Latitude " + Utility::str(lat1)
276  + "d not in [-90d, 90d]");
277  lat = lat1;
278  lon = lon1;
279  }
280 
281  Math::real DMS::DecodeAngle(const std::string& angstr) {
282  flag ind;
283  real ang = Decode(angstr, ind);
284  if (ind != NONE)
285  throw GeographicErr("Arc angle " + angstr
286  + " includes a hemisphere, N/E/W/S");
287  return ang;
288  }
289 
290  Math::real DMS::DecodeAzimuth(const std::string& azistr) {
291  flag ind;
292  real azi = Decode(azistr, ind);
293  if (ind == LATITUDE)
294  throw GeographicErr("Azimuth " + azistr
295  + " has a latitude hemisphere, N/S");
296  return Math::AngNormalize(azi);
297  }
298 
299  string DMS::Encode(real angle, component trailing, unsigned prec, flag ind,
300  char dmssep) {
301  // Assume check on range of input angle has been made by calling
302  // routine (which might be able to offer a better diagnostic).
303  if (!Math::isfinite(angle))
304  return angle < 0 ? string("-inf") :
305  (angle > 0 ? string("inf") : string("nan"));
306 
307  // 15 - 2 * trailing = ceiling(log10(2^53/90/60^trailing)).
308  // This suffices to give full real precision for numbers in [-90,90]
309  prec = min(15 + Math::extra_digits() - 2 * unsigned(trailing), prec);
310  real scale = 1;
311  for (unsigned i = 0; i < unsigned(trailing); ++i)
312  scale *= 60;
313  for (unsigned i = 0; i < prec; ++i)
314  scale *= 10;
315  if (ind == AZIMUTH)
316  angle -= floor(angle/360) * 360;
317  int sign = angle < 0 ? -1 : 1;
318  angle *= sign;
319 
320  // Break off integer part to preserve precision in manipulation of
321  // fractional part.
322  real
323  idegree = floor(angle),
324  fdegree = (angle - idegree) * scale + real(0.5);
325  {
326  // Implement the "round ties to even" rule
327  real f = floor(fdegree);
328  fdegree = (f == fdegree && fmod(f, real(2)) == 1) ? f - 1 : f;
329  }
330  fdegree /= scale;
331  if (fdegree >= 1) {
332  idegree += 1;
333  fdegree -= 1;
334  }
335  real pieces[3] = {fdegree, 0, 0};
336  for (unsigned i = 1; i <= unsigned(trailing); ++i) {
337  real
338  ip = floor(pieces[i - 1]),
339  fp = pieces[i - 1] - ip;
340  pieces[i] = fp * 60;
341  pieces[i - 1] = ip;
342  }
343  pieces[0] += idegree;
344  ostringstream s;
345  s << fixed << setfill('0');
346  if (ind == NONE && sign < 0)
347  s << '-';
348  switch (trailing) {
349  case DEGREE:
350  if (ind != NONE)
351  s << setw(1 + min(int(ind), 2) + prec + (prec ? 1 : 0));
352  s << Utility::str(pieces[0], prec);
353  // Don't include degree designator (d) if it is the trailing component.
354  break;
355  default:
356  if (ind != NONE)
357  s << setw(1 + min(int(ind), 2));
358  s << int(pieces[0])
359  << (dmssep ? dmssep : char(tolower(dmsindicators_[0])));
360  switch (trailing) {
361  case MINUTE:
362  s << setw(2 + prec + (prec ? 1 : 0)) << Utility::str(pieces[1], prec);
363  if (!dmssep)
364  s << char(tolower(dmsindicators_[1]));
365  break;
366  case SECOND:
367  s << setw(2)
368  << int(pieces[1])
369  << (dmssep ? dmssep : char(tolower(dmsindicators_[1])))
370  << setw(2 + prec + (prec ? 1 : 0)) << Utility::str(pieces[2], prec);
371  if (!dmssep)
372  s << char(tolower(dmsindicators_[2]));
373  break;
374  default:
375  break;
376  }
377  }
378  if (ind != NONE && ind != AZIMUTH)
379  s << hemispheres_[(ind == LATITUDE ? 0 : 2) + (sign < 0 ? 0 : 1)];
380  return s.str();
381  }
382 
383 } // namespace GeographicLib
static T AngNormalize(T x)
Definition: Math.hpp:440
static Math::real InternalDecode(const std::string &dmsa, flag &ind)
Definition: src/DMS.cpp:81
static Math::real DecodeAngle(const std::string &angstr)
Definition: src/DMS.cpp:281
float real
Definition: datatypes.h:10
Scalar * b
Definition: benchVecAdd.cpp:17
static const double lat
Header for GeographicLib::Utility class.
#define min(a, b)
Definition: datatypes.h:19
static bool isfinite(T x)
Definition: Math.hpp:806
static const char *const components_[3]
Definition: DMS.hpp:107
static const char *const dmsindicators_
Definition: DMS.hpp:106
Definition: BFloat16.h:88
static int extra_digits()
Definition: Math.hpp:187
EIGEN_DEVICE_FUNC const FloorReturnType floor() const
static const char *const hemispheres_
Definition: DMS.hpp:103
static const char *const signs_
Definition: DMS.hpp:104
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bfloat16 fmod(const bfloat16 &a, const bfloat16 &b)
Definition: BFloat16.h:567
static std::string Encode(real angle, component trailing, unsigned prec, flag ind=NONE, char dmssep=char(0))
Definition: src/DMS.cpp:299
std::vector< int > ind
static Math::real DecodeAzimuth(const std::string &azistr)
Definition: src/DMS.cpp:290
Array< int, Dynamic, 1 > v
EIGEN_DEVICE_FUNC const SignReturnType sign() const
static Math::real Decode(const std::string &dms, flag &ind)
Definition: src/DMS.cpp:28
Definition: main.h:100
Point2(* f)(const Point3 &, OptionalJacobian< 2, 3 >)
Namespace for GeographicLib.
RealScalar s
static std::string str(T x, int p=-1)
Definition: Utility.hpp:276
static const char *const digits_
Definition: DMS.hpp:105
static void DecodeLatLon(const std::string &dmsa, const std::string &dmsb, real &lat, real &lon, bool longfirst=false)
Definition: src/DMS.cpp:252
Exception handling for GeographicLib.
Definition: Constants.hpp:389
static EIGEN_DEPRECATED const end_t end
float * p
static int lookup(const std::string &s, char c)
Definition: Utility.hpp:459
set noclip points set clip one set noclip two set bar set border lt lw set xdata set ydata set zdata set x2data set y2data set boxwidth set dummy y set format x g set format y g set format x2 g set format y2 g set format z g set angles radians set nogrid set key title set key left top Right noreverse box linetype linewidth samplen spacing width set nolabel set noarrow set nologscale set logscale x set set pointsize set encoding default set nopolar set noparametric set set set set surface set nocontour set clabel set mapping cartesian set nohidden3d set cntrparam order set cntrparam linear set cntrparam levels auto set cntrparam points set size set set xzeroaxis lt lw set x2zeroaxis lt lw set yzeroaxis lt lw set y2zeroaxis lt lw set tics in set ticslevel set tics scale
static const double lon
set noclip points set clip one set noclip two set bar set border lt lw set xdata set ydata set zdata set x2data set y2data set boxwidth set dummy x
#define abs(x)
Definition: datatypes.h:17
Header for GeographicLib::DMS class.


gtsam
Author(s):
autogenerated on Tue Jul 4 2023 02:34:10