GeographicLib: Georef.cpp Source File

GeographicLib 2.7

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