GeographicLib  2.1.2
GARS.cpp
Go to the documentation of this file.
1 /**
2  * \file GARS.cpp
3  * \brief Implementation for GeographicLib::GARS class
4  *
5  * Copyright (c) Charles Karney (2015-2022) <charles@karney.com> and licensed
6  * under the MIT/X11 License. For more information, see
7  * https://geographiclib.sourceforge.io/
8  **********************************************************************/
9 
10 #include <GeographicLib/GARS.hpp>
12 
13 #if defined(_MSC_VER)
14 // Squelch warnings about enum-float expressions
15 # pragma warning (disable: 5055)
16 #endif
17 
18 namespace GeographicLib {
19 
20  using namespace std;
21 
22  const char* const GARS::digits_ = "0123456789";
23  const char* const GARS::letters_ = "ABCDEFGHJKLMNPQRSTUVWXYZ";
24 
25  void GARS::Forward(real lat, real lon, int prec, string& gars) {
26  using std::isnan; // Needed for Centos 7, ubuntu 14
27  if (fabs(lat) > Math::qd)
28  throw GeographicErr("Latitude " + Utility::str(lat)
29  + "d not in [-" + to_string(Math::qd)
30  + "d, " + to_string(Math::qd) + "d]");
31  if (isnan(lat) || isnan(lon)) {
32  gars = "INVALID";
33  return;
34  }
35  lon = Math::AngNormalize(lon);
36  if (lon == Math::hd) lon = -Math::hd; // lon now in [-180,180)
37  if (lat == Math::qd) lat *= (1 - numeric_limits<real>::epsilon() / 2);
38  prec = max(0, min(int(maxprec_), prec));
39  int
40  x = int(floor(lon * m_)) - lonorig_ * m_,
41  y = int(floor(lat * m_)) - latorig_ * m_,
42  ilon = x * mult1_ / m_,
43  ilat = y * mult1_ / m_;
44  x -= ilon * m_ / mult1_; y -= ilat * m_ / mult1_;
45  char gars1[maxlen_];
46  ++ilon;
47  for (int c = lonlen_; c--;) {
48  gars1[c] = digits_[ ilon % baselon_]; ilon /= baselon_;
49  }
50  for (int c = latlen_; c--;) {
51  gars1[lonlen_ + c] = letters_[ilat % baselat_]; ilat /= baselat_;
52  }
53  if (prec > 0) {
54  ilon = x / mult3_; ilat = y / mult3_;
55  gars1[baselen_] = digits_[mult2_ * (mult2_ - 1 - ilat) + ilon + 1];
56  if (prec > 1) {
57  ilon = x % mult3_; ilat = y % mult3_;
58  gars1[baselen_ + 1] = digits_[mult3_ * (mult3_ - 1 - ilat) + ilon + 1];
59  }
60  }
61  gars.resize(baselen_ + prec);
62  copy(gars1, gars1 + baselen_ + prec, gars.begin());
63  }
64 
65  void GARS::Reverse(const string& gars, real& lat, real& lon,
66  int& prec, bool centerp) {
67  int len = int(gars.length());
68  if (len >= 3 &&
69  toupper(gars[0]) == 'I' &&
70  toupper(gars[1]) == 'N' &&
71  toupper(gars[2]) == 'V') {
72  lat = lon = Math::NaN();
73  return;
74  }
75  if (len < baselen_)
76  throw GeographicErr("GARS must have at least 5 characters " + gars);
77  if (len > maxlen_)
78  throw GeographicErr("GARS can have at most 7 characters " + gars);
79  int prec1 = len - baselen_;
80  int ilon = 0;
81  for (int c = 0; c < lonlen_; ++c) {
82  int k = Utility::lookup(digits_, gars[c]);
83  if (k < 0)
84  throw GeographicErr("GARS must start with 3 digits " + gars);
85  ilon = ilon * baselon_ + k;
86  }
87  if (!(ilon >= 1 && ilon <= 2 * Math::td))
88  throw GeographicErr("Initial digits in GARS must lie in [1, 720] " +
89  gars);
90  --ilon;
91  int ilat = 0;
92  for (int c = 0; c < latlen_; ++c) {
93  int k = Utility::lookup(letters_, gars[lonlen_ + c]);
94  if (k < 0)
95  throw GeographicErr("Illegal letters in GARS " + gars.substr(3,2));
96  ilat = ilat * baselat_ + k;
97  }
98  if (!(ilat < Math::td))
99  throw GeographicErr("GARS letters must lie in [AA, QZ] " + gars);
100  real
101  unit = mult1_,
102  lat1 = ilat + latorig_ * unit,
103  lon1 = ilon + lonorig_ * unit;
104  if (prec1 > 0) {
105  int k = Utility::lookup(digits_, gars[baselen_]);
106  if (!(k >= 1 && k <= mult2_ * mult2_))
107  throw GeographicErr("6th character in GARS must [1, 4] " + gars);
108  --k;
109  unit *= mult2_;
110  lat1 = mult2_ * lat1 + (mult2_ - 1 - k / mult2_);
111  lon1 = mult2_ * lon1 + (k % mult2_);
112  if (prec1 > 1) {
113  k = Utility::lookup(digits_, gars[baselen_ + 1]);
114  if (!(k >= 1 /* && k <= mult3_ * mult3_ */))
115  throw GeographicErr("7th character in GARS must [1, 9] " + gars);
116  --k;
117  unit *= mult3_;
118  lat1 = mult3_ * lat1 + (mult3_ - 1 - k / mult3_);
119  lon1 = mult3_ * lon1 + (k % mult3_);
120  }
121  }
122  if (centerp) {
123  unit *= 2; lat1 = 2 * lat1 + 1; lon1 = 2 * lon1 + 1;
124  }
125  lat = lat1 / unit;
126  lon = lon1 / unit;
127  prec = prec1;
128  }
129 
130 } // namespace GeographicLib
Header for GeographicLib::GARS class.
Header for GeographicLib::Utility class.
static void Reverse(const std::string &gars, real &lat, real &lon, int &prec, bool centerp=true)
Definition: GARS.cpp:65
static void Forward(real lat, real lon, int prec, std::string &gars)
Definition: GARS.cpp:25
Exception handling for GeographicLib.
Definition: Constants.hpp:316
static T AngNormalize(T x)
Definition: Math.cpp:71
static T NaN()
Definition: Math.cpp:250
@ td
degrees per turn
Definition: Math.hpp:145
@ hd
degrees per half turn
Definition: Math.hpp:144
@ qd
degrees per quarter turn
Definition: Math.hpp:141
static int lookup(const std::string &s, char c)
Definition: Utility.cpp:160
static std::string str(T x, int p=-1)
Definition: Utility.hpp:161
Namespace for GeographicLib.
Definition: Accumulator.cpp:12