1 /** 2 * \file GARS.cpp 3 * \brief Implementation for GeographicLib::GARS class 4 * 5 * Copyright (c) Charles Karney (2015-2020) <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> 11 #include <GeographicLib/Utility.hpp> 12 13 namespace GeographicLib { 14 15 using namespace std; 16 17 const char* const GARS::digits_ = "0123456789"; 18 const char* const GARS::letters_ = "ABCDEFGHJKLMNPQRSTUVWXYZ"; 19 Forward(real lat,real lon,int prec,string & gars)20 void GARS::Forward(real lat, real lon, int prec, string& gars) { 21 using std::isnan; // Needed for Centos 7, ubuntu 14 22 if (abs(lat) > 90) 23 throw GeographicErr("Latitude " + Utility::str(lat) 24 + "d not in [-90d, 90d]"); 25 if (isnan(lat) || isnan(lon)) { 26 gars = "INVALID"; 27 return; 28 } 29 lon = Math::AngNormalize(lon); 30 if (lon == 180) lon = -180; // lon now in [-180,180) 31 if (lat == 90) lat *= (1 - numeric_limits<real>::epsilon() / 2); 32 prec = max(0, min(int(maxprec_), prec)); 33 int 34 x = int(floor(lon * m_)) - lonorig_ * m_, 35 y = int(floor(lat * m_)) - latorig_ * m_, 36 ilon = x * mult1_ / m_, 37 ilat = y * mult1_ / m_; 38 x -= ilon * m_ / mult1_; y -= ilat * m_ / mult1_; 39 char gars1[maxlen_]; 40 ++ilon; 41 for (int c = lonlen_; c--;) { 42 gars1[c] = digits_[ ilon % baselon_]; ilon /= baselon_; 43 } 44 for (int c = latlen_; c--;) { 45 gars1[lonlen_ + c] = letters_[ilat % baselat_]; ilat /= baselat_; 46 } 47 if (prec > 0) { 48 ilon = x / mult3_; ilat = y / mult3_; 49 gars1[baselen_] = digits_[mult2_ * (mult2_ - 1 - ilat) + ilon + 1]; 50 if (prec > 1) { 51 ilon = x % mult3_; ilat = y % mult3_; 52 gars1[baselen_ + 1] = digits_[mult3_ * (mult3_ - 1 - ilat) + ilon + 1]; 53 } 54 } 55 gars.resize(baselen_ + prec); 56 copy(gars1, gars1 + baselen_ + prec, gars.begin()); 57 } 58 Reverse(const string & gars,real & lat,real & lon,int & prec,bool centerp)59 void GARS::Reverse(const string& gars, real& lat, real& lon, 60 int& prec, bool centerp) { 61 int len = int(gars.length()); 62 if (len >= 3 && 63 toupper(gars[0]) == 'I' && 64 toupper(gars[1]) == 'N' && 65 toupper(gars[2]) == 'V') { 66 lat = lon = Math::NaN(); 67 return; 68 } 69 if (len < baselen_) 70 throw GeographicErr("GARS must have at least 5 characters " + gars); 71 if (len > maxlen_) 72 throw GeographicErr("GARS can have at most 7 characters " + gars); 73 int prec1 = len - baselen_; 74 int ilon = 0; 75 for (int c = 0; c < lonlen_; ++c) { 76 int k = Utility::lookup(digits_, gars[c]); 77 if (k < 0) 78 throw GeographicErr("GARS must start with 3 digits " + gars); 79 ilon = ilon * baselon_ + k; 80 } 81 if (!(ilon >= 1 && ilon <= 720)) 82 throw GeographicErr("Initial digits in GARS must lie in [1, 720] " + 83 gars); 84 --ilon; 85 int ilat = 0; 86 for (int c = 0; c < latlen_; ++c) { 87 int k = Utility::lookup(letters_, gars[lonlen_ + c]); 88 if (k < 0) 89 throw GeographicErr("Illegal letters in GARS " + gars.substr(3,2)); 90 ilat = ilat * baselat_ + k; 91 } 92 if (!(ilat < 360)) 93 throw GeographicErr("GARS letters must lie in [AA, QZ] " + gars); 94 real 95 unit = mult1_, 96 lat1 = ilat + latorig_ * unit, 97 lon1 = ilon + lonorig_ * unit; 98 if (prec1 > 0) { 99 int k = Utility::lookup(digits_, gars[baselen_]); 100 if (!(k >= 1 && k <= mult2_ * mult2_)) 101 throw GeographicErr("6th character in GARS must [1, 4] " + gars); 102 --k; 103 unit *= mult2_; 104 lat1 = mult2_ * lat1 + (mult2_ - 1 - k / mult2_); 105 lon1 = mult2_ * lon1 + (k % mult2_); 106 if (prec1 > 1) { 107 k = Utility::lookup(digits_, gars[baselen_ + 1]); 108 if (!(k >= 1 /* && k <= mult3_ * mult3_ */)) 109 throw GeographicErr("7th character in GARS must [1, 9] " + gars); 110 --k; 111 unit *= mult3_; 112 lat1 = mult3_ * lat1 + (mult3_ - 1 - k / mult3_); 113 lon1 = mult3_ * lon1 + (k % mult3_); 114 } 115 } 116 if (centerp) { 117 unit *= 2; lat1 = 2 * lat1 + 1; lon1 = 2 * lon1 + 1; 118 } 119 lat = lat1 / unit; 120 lon = lon1 / unit; 121 prec = prec1; 122 } 123 124 } // namespace GeographicLib 125