GeographicLib  2.1.1
Georef.cpp
Go to the documentation of this file.
1 /**
2  * \file Georef.cpp
3  * \brief Implementation for GeographicLib::Georef 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/Georef.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 Georef::digits_ = "0123456789";
23  const char* const Georef::lontile_ = "ABCDEFGHJKLMNPQRSTUVWXYZ";
24  const char* const Georef::lattile_ = "ABCDEFGHJKLM";
25  const char* const Georef::degrees_ = "ABCDEFGHJKLMNPQ";
26 
27  void Georef::Forward(real lat, real lon, int prec, string& georef) {
28  using std::isnan; // Needed for Centos 7, ubuntu 14
29  if (fabs(lat) > Math::qd)
30  throw GeographicErr("Latitude " + Utility::str(lat)
31  + "d not in [-" + to_string(Math::qd)
32  + "d, " + to_string(Math::qd) + "d]");
33  if (isnan(lat) || isnan(lon)) {
34  georef = "INVALID";
35  return;
36  }
37  lon = Math::AngNormalize(lon); // lon in [-180,180)
38  if (lat == Math::qd) lat *= (1 - numeric_limits<real>::epsilon() / 2);
39  prec = max(-1, min(int(maxprec_), prec));
40  if (prec == 1) ++prec; // Disallow prec = 1
41  // The C++ standard mandates 64 bits for long long. But
42  // check, to make sure.
43  static_assert(numeric_limits<long long>::digits >= 45,
44  "long long not wide enough to store 21600e9");
45  const long long m = 60000000000LL;
46  long long
47  x = (long long)(floor(lon * real(m))) - lonorig_ * m,
48  y = (long long)(floor(lat * real(m))) - latorig_ * m;
49  int ilon = int(x / m); int ilat = int(y / m);
50  char georef1[maxlen_];
51  georef1[0] = lontile_[ilon / tile_];
52  georef1[1] = lattile_[ilat / tile_];
53  if (prec >= 0) {
54  georef1[2] = degrees_[ilon % tile_];
55  georef1[3] = degrees_[ilat % tile_];
56  if (prec > 0) {
57  x -= m * ilon; y -= m * ilat;
58  long long d = (long long)pow(real(base_), maxprec_ - prec);
59  x /= d; y /= d;
60  for (int c = prec; c--;) {
61  georef1[baselen_ + c ] = digits_[x % base_]; x /= base_;
62  georef1[baselen_ + c + prec] = digits_[y % base_]; y /= base_;
63  }
64  }
65  }
66  georef.resize(baselen_ + 2 * prec);
67  copy(georef1, georef1 + baselen_ + 2 * prec, georef.begin());
68  }
69 
70  void Georef::Reverse(const string& georef, real& lat, real& lon,
71  int& prec, bool centerp) {
72  int len = int(georef.length());
73  if (len >= 3 &&
74  toupper(georef[0]) == 'I' &&
75  toupper(georef[1]) == 'N' &&
76  toupper(georef[2]) == 'V') {
77  lat = lon = Math::NaN();
78  return;
79  }
80  if (len < baselen_ - 2)
81  throw GeographicErr("Georef must start with at least 2 letters "
82  + georef);
83  int prec1 = (2 + len - baselen_) / 2 - 1;
84  int k;
85  k = Utility::lookup(lontile_, georef[0]);
86  if (k < 0)
87  throw GeographicErr("Bad longitude tile letter in georef " + georef);
88  real lon1 = k + lonorig_ / tile_;
89  k = Utility::lookup(lattile_, georef[1]);
90  if (k < 0)
91  throw GeographicErr("Bad latitude tile letter in georef " + georef);
92  real lat1 = k + latorig_ / tile_;
93  real unit = 1;
94  if (len > 2) {
95  unit *= tile_;
96  k = Utility::lookup(degrees_, georef[2]);
97  if (k < 0)
98  throw GeographicErr("Bad longitude degree letter in georef " + georef);
99  lon1 = lon1 * tile_ + k;
100  if (len < 4)
101  throw GeographicErr("Missing latitude degree letter in georef "
102  + georef);
103  k = Utility::lookup(degrees_, georef[3]);
104  if (k < 0)
105  throw GeographicErr("Bad latitude degree letter in georef " + georef);
106  lat1 = lat1 * tile_ + k;
107  if (prec1 > 0) {
108  if (georef.find_first_not_of(digits_, baselen_) != string::npos)
109  throw GeographicErr("Non digits in trailing portion of georef "
110  + georef.substr(baselen_));
111  if (len % 2)
112  throw GeographicErr("Georef must end with an even number of digits "
113  + georef.substr(baselen_));
114  if (prec1 == 1)
115  throw GeographicErr("Georef needs at least 4 digits for minutes "
116  + georef.substr(baselen_));
117  if (prec1 > maxprec_)
118  throw GeographicErr("More than " + Utility::str(2*maxprec_)
119  + " digits in georef "
120  + georef.substr(baselen_));
121  for (int i = 0; i < prec1; ++i) {
122  int m = i ? base_ : 6;
123  unit *= m;
124  int
125  x = Utility::lookup(digits_, georef[baselen_ + i]),
126  y = Utility::lookup(digits_, georef[baselen_ + i + prec1]);
127  if (!(i || (x < m && y < m)))
128  throw GeographicErr("Minutes terms in georef must be less than 60 "
129  + georef.substr(baselen_));
130  lon1 = m * lon1 + x;
131  lat1 = m * lat1 + y;
132  }
133  }
134  }
135  if (centerp) {
136  unit *= 2; lat1 = 2 * lat1 + 1; lon1 = 2 * lon1 + 1;
137  }
138  lat = (tile_ * lat1) / unit;
139  lon = (tile_ * lon1) / unit;
140  prec = prec1;
141  }
142 
143 } // namespace GeographicLib
GeographicLib::Math::real real
Definition: GeodSolve.cpp:31
Header for GeographicLib::Georef class.
Header for GeographicLib::Utility class.
Exception handling for GeographicLib.
Definition: Constants.hpp:316
static void Forward(real lat, real lon, int prec, std::string &georef)
Definition: Georef.cpp:27
static void Reverse(const std::string &georef, real &lat, real &lon, int &prec, bool centerp=true)
Definition: Georef.cpp:70
static T AngNormalize(T x)
Definition: Math.cpp:71
static T NaN()
Definition: Math.cpp:250
@ 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