00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <GeographicLib/DMS.hpp>
00011 #include <GeographicLib/Utility.hpp>
00012
00013 #if defined(_MSC_VER)
00014
00015 # pragma warning (disable: 4127)
00016 #endif
00017
00018 namespace GeographicLib {
00019
00020 using namespace std;
00021
00022 const string DMS::hemispheres_ = "SNWE";
00023 const string DMS::signs_ = "-+";
00024 const string DMS::digits_ = "0123456789";
00025 const string DMS::dmsindicators_ = "D'\":";
00026 const string DMS::components_[] = {"degrees", "minutes", "seconds"};
00027
00028 Math::real DMS::Decode(const std::string& dms, flag& ind) {
00029 string errormsg;
00030 string dmsa = dms;
00031 replace(dmsa, "\xc2\xb0", 'd');
00032 replace(dmsa, "\xc2\xba", 'd');
00033 replace(dmsa, "\xe2\x81\xb0", 'd');
00034 replace(dmsa, "\xcb\x9a", 'd');
00035 replace(dmsa, "\xe2\x80\xb2", '\'');
00036 replace(dmsa, "\xc2\xb4", '\'');
00037 replace(dmsa, "\xe2\x80\x99", '\'');
00038 replace(dmsa, "\xe2\x80\xb3", '"');
00039 replace(dmsa, "\xe2\x80\x9d", '"');
00040 replace(dmsa, "\xe2\x88\x92", '-');
00041 replace(dmsa, "\xb0", 'd');
00042 replace(dmsa, "\xba", 'd');
00043 replace(dmsa, "\xb4", '\'');
00044 replace(dmsa, "''", '"');
00045 do {
00046 int sign = 1;
00047 unsigned
00048 beg = 0,
00049 end = unsigned(dmsa.size());
00050 while (beg < end && isspace(dmsa[beg]))
00051 ++beg;
00052 while (beg < end && isspace(dmsa[end - 1]))
00053 --end;
00054 flag ind1 = NONE;
00055 int k = -1;
00056 if (end > beg && (k = Utility::lookup(hemispheres_, dmsa[beg])) >= 0) {
00057 ind1 = (k / 2) ? LONGITUDE : LATITUDE;
00058 sign = k % 2 ? 1 : -1;
00059 ++beg;
00060 }
00061 if (end > beg && (k = Utility::lookup(hemispheres_, dmsa[end-1])) >= 0) {
00062 if (k >= 0) {
00063 if (ind1 != NONE) {
00064 if (toupper(dmsa[beg - 1]) == toupper(dmsa[end - 1]))
00065 errormsg = "Repeated hemisphere indicators "
00066 + Utility::str(dmsa[beg - 1])
00067 + " in " + dmsa.substr(beg - 1, end - beg + 1);
00068 else
00069 errormsg = "Contradictory hemisphere indicators "
00070 + Utility::str(dmsa[beg - 1]) + " and "
00071 + Utility::str(dmsa[end - 1]) + " in "
00072 + dmsa.substr(beg - 1, end - beg + 1);
00073 break;
00074 }
00075 ind1 = (k / 2) ? LONGITUDE : LATITUDE;
00076 sign = k % 2 ? 1 : -1;
00077 --end;
00078 }
00079 }
00080 if (end > beg && (k = Utility::lookup(signs_, dmsa[beg])) >= 0) {
00081 if (k >= 0) {
00082 sign *= k ? 1 : -1;
00083 ++beg;
00084 }
00085 }
00086 if (end == beg) {
00087 errormsg = "Empty or incomplete DMS string " + dmsa;
00088 break;
00089 }
00090 real ipieces[] = {0, 0, 0};
00091 real fpieces[] = {0, 0, 0};
00092 unsigned npiece = 0;
00093 real icurrent = 0;
00094 real fcurrent = 0;
00095 unsigned ncurrent = 0, p = beg;
00096 bool pointseen = false;
00097 unsigned digcount = 0, intcount = 0;
00098 while (p < end) {
00099 char x = dmsa[p++];
00100 if ((k = Utility::lookup(digits_, x)) >= 0) {
00101 ++ncurrent;
00102 if (digcount > 0)
00103 ++digcount;
00104 else {
00105 icurrent = 10 * icurrent + k;
00106 ++intcount;
00107 }
00108 } else if (x == '.') {
00109 if (pointseen) {
00110 errormsg = "Multiple decimal points in "
00111 + dmsa.substr(beg, end - beg);
00112 break;
00113 }
00114 pointseen = true;
00115 digcount = 1;
00116 } else if ((k = Utility::lookup(dmsindicators_, x)) >= 0) {
00117 if (k >= 3) {
00118 if (p == end) {
00119 errormsg = "Illegal for : to appear at the end of " +
00120 dmsa.substr(beg, end - beg);
00121 break;
00122 }
00123 k = npiece;
00124 }
00125 if (unsigned(k) == npiece - 1) {
00126 errormsg = "Repeated " + components_[k] +
00127 " component in " + dmsa.substr(beg, end - beg);
00128 break;
00129 } else if (unsigned(k) < npiece) {
00130 errormsg = components_[k] + " component follows "
00131 + components_[npiece - 1] + " component in "
00132 + dmsa.substr(beg, end - beg);
00133 break;
00134 }
00135 if (ncurrent == 0) {
00136 errormsg = "Missing numbers in " + components_[k] +
00137 " component of " + dmsa.substr(beg, end - beg);
00138 break;
00139 }
00140 if (digcount > 1) {
00141 istringstream s(dmsa.substr(p - intcount - digcount - 1,
00142 intcount + digcount));
00143 s >> fcurrent;
00144 icurrent = 0;
00145 }
00146 ipieces[k] = icurrent;
00147 fpieces[k] = icurrent + fcurrent;
00148 if (p < end) {
00149 npiece = k + 1;
00150 icurrent = fcurrent = 0;
00151 ncurrent = digcount = intcount = 0;
00152 }
00153 } else if (Utility::lookup(signs_, x) >= 0) {
00154 errormsg = "Internal sign in DMS string "
00155 + dmsa.substr(beg, end - beg);
00156 break;
00157 } else {
00158 errormsg = "Illegal character " + Utility::str(x) + " in DMS string "
00159 + dmsa.substr(beg, end - beg);
00160 break;
00161 }
00162 }
00163 if (!errormsg.empty())
00164 break;
00165 if (Utility::lookup(dmsindicators_, dmsa[p - 1]) < 0) {
00166 if (npiece >= 3) {
00167 errormsg = "Extra text following seconds in DMS string "
00168 + dmsa.substr(beg, end - beg);
00169 break;
00170 }
00171 if (ncurrent == 0) {
00172 errormsg = "Missing numbers in trailing component of "
00173 + dmsa.substr(beg, end - beg);
00174 break;
00175 }
00176 if (digcount > 1) {
00177 istringstream s(dmsa.substr(p - intcount - digcount,
00178 intcount + digcount));
00179 s >> fcurrent;
00180 icurrent = 0;
00181 }
00182 ipieces[npiece] = icurrent;
00183 fpieces[npiece] = icurrent + fcurrent;
00184 }
00185 if (pointseen && digcount == 0) {
00186 errormsg = "Decimal point in non-terminal component of "
00187 + dmsa.substr(beg, end - beg);
00188 break;
00189 }
00190
00191 if (ipieces[1] >= 60) {
00192 errormsg = "Minutes " + Utility::str(fpieces[1])
00193 + " not in range [0, 60)";
00194 break;
00195 }
00196 if (ipieces[2] >= 60) {
00197 errormsg = "Seconds " + Utility::str(fpieces[2])
00198 + " not in range [0, 60)";
00199 break;
00200 }
00201 ind = ind1;
00202
00203
00204 return real(sign) * (fpieces[0] + (fpieces[1] + fpieces[2] / 60) / 60);
00205 } while (false);
00206 real val = Utility::nummatch<real>(dmsa);
00207 if (val == 0)
00208 throw GeographicErr(errormsg);
00209 else
00210 ind = NONE;
00211 return val;
00212 }
00213
00214 void DMS::DecodeLatLon(const std::string& stra, const std::string& strb,
00215 real& lat, real& lon, bool swaplatlong) {
00216 real a, b;
00217 flag ia, ib;
00218 a = Decode(stra, ia);
00219 b = Decode(strb, ib);
00220 if (ia == NONE && ib == NONE) {
00221
00222 ia = swaplatlong ? LONGITUDE : LATITUDE;
00223 ib = swaplatlong ? LATITUDE : LONGITUDE;
00224 } else if (ia == NONE)
00225 ia = flag(LATITUDE + LONGITUDE - ib);
00226 else if (ib == NONE)
00227 ib = flag(LATITUDE + LONGITUDE - ia);
00228 if (ia == ib)
00229 throw GeographicErr("Both " + stra + " and "
00230 + strb + " interpreted as "
00231 + (ia == LATITUDE ? "latitudes" : "longitudes"));
00232 real
00233 lat1 = ia == LATITUDE ? a : b,
00234 lon1 = ia == LATITUDE ? b : a;
00235 if (abs(lat1) > 90)
00236 throw GeographicErr("Latitude " + Utility::str(lat1)
00237 + "d not in [-90d, 90d]");
00238 if (lon1 < -540 || lon1 >= 540)
00239 throw GeographicErr("Longitude " + Utility::str(lon1)
00240 + "d not in [-540d, 540d)");
00241 lon1 = Math::AngNormalize(lon1);
00242 lat = lat1;
00243 lon = lon1;
00244 }
00245
00246 Math::real DMS::DecodeAngle(const std::string& angstr) {
00247 flag ind;
00248 real ang = Decode(angstr, ind);
00249 if (ind != NONE)
00250 throw GeographicErr("Arc angle " + angstr
00251 + " includes a hemisphere, N/E/W/S");
00252 return ang;
00253 }
00254
00255 Math::real DMS::DecodeAzimuth(const std::string& azistr) {
00256 flag ind;
00257 real azi = Decode(azistr, ind);
00258 if (ind == LATITUDE)
00259 throw GeographicErr("Azimuth " + azistr
00260 + " has a latitude hemisphere, N/S");
00261 if (azi < -540 || azi >= 540)
00262 throw GeographicErr("Azimuth " + azistr + " not in range [-540d, 540d)");
00263 return Math::AngNormalize(azi);
00264 }
00265
00266 string DMS::Encode(real angle, component trailing, unsigned prec, flag ind,
00267 char dmssep) {
00268
00269
00270 if (!Math::isfinite(angle))
00271 return angle < 0 ? string("-inf") :
00272 (angle > 0 ? string("inf") : string("nan"));
00273
00274
00275
00276 prec = min(15 + Math::extra_digits() - 2 * unsigned(trailing), prec);
00277 real scale = 1;
00278 for (unsigned i = 0; i < unsigned(trailing); ++i)
00279 scale *= 60;
00280 for (unsigned i = 0; i < prec; ++i)
00281 scale *= 10;
00282 if (ind == AZIMUTH)
00283 angle -= floor(angle/360) * 360;
00284 int sign = angle < 0 ? -1 : 1;
00285 angle *= sign;
00286
00287
00288
00289 real
00290 idegree = floor(angle),
00291 fdegree = floor((angle - idegree) * scale + real(0.5)) / scale;
00292 if (fdegree >= 1) {
00293 idegree += 1;
00294 fdegree -= 1;
00295 }
00296 real pieces[3] = {fdegree, 0, 0};
00297 for (unsigned i = 1; i <= unsigned(trailing); ++i) {
00298 real
00299 ip = floor(pieces[i - 1]),
00300 fp = pieces[i - 1] - ip;
00301 pieces[i] = fp * 60;
00302 pieces[i - 1] = ip;
00303 }
00304 pieces[0] += idegree;
00305 ostringstream s;
00306 s << fixed << setfill('0');
00307 if (ind == NONE && sign < 0)
00308 s << '-';
00309 switch (trailing) {
00310 case DEGREE:
00311 if (ind != NONE)
00312 s << setw(1 + min(int(ind), 2) + prec + (prec ? 1 : 0));
00313 s << setprecision(prec) << pieces[0];
00314
00315 break;
00316 default:
00317 if (ind != NONE)
00318 s << setw(1 + min(int(ind), 2));
00319 s << setprecision(0) << pieces[0]
00320 << (dmssep ? dmssep : char(tolower(dmsindicators_[0])));
00321 switch (trailing) {
00322 case MINUTE:
00323 s << setw(2 + prec + (prec ? 1 : 0)) << setprecision(prec) << pieces[1];
00324 if (!dmssep)
00325 s << char(tolower(dmsindicators_[1]));
00326 break;
00327 case SECOND:
00328 s << setw(2)
00329 << pieces[1] << (dmssep ? dmssep : char(tolower(dmsindicators_[1])))
00330 << setw(2 + prec + (prec ? 1 : 0)) << setprecision(prec) << pieces[2];
00331 if (!dmssep)
00332 s << char(tolower(dmsindicators_[2]));
00333 break;
00334 default:
00335 break;
00336 }
00337 }
00338 if (ind != NONE && ind != AZIMUTH)
00339 s << hemispheres_[(ind == LATITUDE ? 0 : 2) + (sign < 0 ? 0 : 1)];
00340 return s.str();
00341 }
00342
00343 }