00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "GeographicLib/DMS.hpp"
00011 #include <algorithm>
00012 #include <cmath>
00013 #include <stdexcept>
00014 #include <iomanip>
00015
00016 namespace {
00017 char RCSID[] = "$Id: DMS.cpp 6572 2009-03-01 22:41:48Z ckarney $";
00018 char RCSID_H[] = DMS_HPP;
00019 }
00020
00021 namespace GeographicLib {
00022
00023 using namespace std;
00024
00025 const string DMS::hemispheres = "SNWE";
00026 const string DMS::signs = "-+";
00027 const string DMS::digits = "0123456789";
00028 const string DMS::dmsindicators = "D'\"";
00029 const string DMS::components[] = {"degrees", "minutes", "seconds"};
00030
00031 double DMS::Decode(const std::string& dms, flag& ind) {
00032 double sign = 1;
00033 unsigned
00034 beg = 0,
00035 end = unsigned(dms.size());
00036 while (beg < end && isspace(dms[beg]))
00037 ++beg;
00038 while (beg < end && isspace(dms[end - 1]))
00039 --end;
00040 ind = NONE;
00041 int k = -1;
00042 if (end > beg && (k = lookup(hemispheres, dms[beg])) >= 0) {
00043 ind = (k / 2) ? LONGITUDE : LATITUDE;
00044 sign = k % 2 ? 1 : -1;
00045 ++beg;
00046 }
00047 if (end > beg && (k = lookup(hemispheres, dms[end-1])) >= 0) {
00048 if (k >= 0) {
00049 if (ind != NONE) {
00050 if (toupper(dms[beg - 1]) == toupper(dms[end - 1]))
00051 throw out_of_range("Repeated hemisphere indicators "
00052 + str(dms[beg - 1]) + " in "
00053 + dms.substr(beg - 1, end - beg + 1));
00054 else
00055 throw out_of_range("Contradictory hemisphere indicators "
00056 + str(dms[beg - 1]) + " and "
00057 + str(dms[end - 1]) + " in "
00058 + dms.substr(beg - 1, end - beg + 1));
00059 }
00060 ind = (k / 2) ? LONGITUDE : LATITUDE;
00061 sign = k % 2 ? 1 : -1;
00062 --end;
00063 }
00064 }
00065 if (end > beg && (k = lookup(signs, dms[beg])) >= 0) {
00066 if (k >= 0) {
00067 sign *= k ? 1 : -1;
00068 ++beg;
00069 }
00070 }
00071 if (end == beg)
00072 throw out_of_range("Empty or incomplete DMS string " + dms);
00073 double ipieces[] = {0, 0, 0};
00074 double fpieces[] = {0, 0, 0};
00075 unsigned npiece = 0;
00076 double icurrent = 0;
00077 double fcurrent = 0;
00078 unsigned ncurrent = 0, p = beg;
00079 bool pointseen = false;
00080 unsigned digcount = 0;
00081 while (p < end) {
00082 char x = dms[p++];
00083 if ((k = lookup(digits, x)) >= 0) {
00084 ++ncurrent;
00085 if (digcount > 0)
00086 ++digcount;
00087 else
00088 icurrent = 10 * icurrent + k;
00089 } else if (x == '.') {
00090 if (pointseen)
00091 throw out_of_range("Multiple decimal points in "
00092 + dms.substr(beg, end - beg));
00093 pointseen = true;
00094 digcount = 1;
00095 } else if ((k = lookup(dmsindicators, x)) >= 0) {
00096 if (unsigned(k) == npiece - 1)
00097 throw out_of_range("Repeated " + components[k]
00098 + " component in "
00099 + dms.substr(beg, end - beg));
00100 else if (unsigned(k) < npiece)
00101 throw out_of_range(components[k] + " component follows "
00102 + components[npiece - 1] + " component in "
00103 + dms.substr(beg, end - beg));
00104 if (ncurrent == 0)
00105 throw out_of_range("Missing numbers in " + components[k]
00106 + " component of "
00107 + dms.substr(beg, end - beg));
00108 if (digcount > 1) {
00109 istringstream s(dms.substr(p-digcount-1, digcount));
00110 s >> fcurrent;
00111 }
00112 ipieces[k] = icurrent;
00113 fpieces[k] = icurrent + fcurrent;
00114 if (p < end) {
00115 npiece = k + 1;
00116 icurrent = fcurrent = 0;
00117 ncurrent = digcount = 0;
00118 }
00119 } else if (lookup(signs, x) >= 0)
00120 throw out_of_range("Internal sign in DMS string "
00121 + dms.substr(beg, end - beg));
00122 else
00123 throw out_of_range("Illegal character " + str(x)
00124 + " in DMS string "
00125 + dms.substr(beg, end - beg));
00126 }
00127 if (lookup(dmsindicators, dms[p - 1]) < 0) {
00128 if (npiece >= 3)
00129 throw out_of_range("Extra text following seconds in DMS string "
00130 + dms.substr(beg, end - beg));
00131 if (ncurrent == 0)
00132 throw out_of_range("Missing numbers in " + components[k]
00133 + " component of "
00134 + dms.substr(beg, end - beg));
00135 if (digcount > 1) {
00136 istringstream s(dms.substr(p - digcount, digcount));
00137 s >> fcurrent;
00138 }
00139 ipieces[npiece] = icurrent;
00140 fpieces[npiece] = icurrent + fcurrent;
00141 }
00142 if (pointseen && digcount == 0)
00143 throw out_of_range("Decimal point in non-terminal component of "
00144 + dms.substr(beg, end - beg));
00145
00146 if (ipieces[1] >= 60)
00147 throw out_of_range("Minutes " + str(fpieces[1])
00148 + " not in range [0, 60)");
00149 if (ipieces[2] >= 60)
00150 throw out_of_range("Seconds " + str(fpieces[2])
00151 + " not in range [0, 60)");
00152
00153
00154 return sign * (fpieces[0] + (fpieces[1] + fpieces[2] / 60) / 60);
00155 }
00156
00157 void DMS::DecodeLatLon(const std::string& stra, const std::string& strb,
00158 double& lat, double& lon) {
00159 double a, b;
00160 flag ia, ib;
00161 a = Decode(stra, ia);
00162 b = Decode(strb, ib);
00163 if (ia == NONE && ib == NONE) {
00164
00165 ia = LATITUDE;
00166 ib = LONGITUDE;
00167 } else if (ia == NONE)
00168 ia = flag(LATITUDE + LONGITUDE - ib);
00169 else if (ib == NONE)
00170 ib = flag(LATITUDE + LONGITUDE - ia);
00171 if (ia == ib)
00172 throw out_of_range("Both " + stra + " and " + strb +
00173 " interpreted as "
00174 + (ia == LATITUDE ? "latitudes"
00175 : "longitudes"));
00176 if (ia == LATITUDE) {
00177 lat = a; lon = b;
00178 } else {
00179 lat = b; lon = a;
00180 }
00181 if (! (lat >= -90 && lat <= 90))
00182 throw out_of_range("Latitude " + str(lat) +
00183 "d not in [-90d, 90d]");
00184 if (! (lon >= -180 && lon <= 360))
00185 throw out_of_range("Latitude " + str(lon) +
00186 "d not in [-180d, 360d]");
00187 if (lon >= 180)
00188 lon -= 360;
00189 }
00190
00191 string DMS::Encode(double angle, component trailing, unsigned prec,
00192 flag ind) {
00193
00194
00195
00196
00197
00198 prec = min(15 - 2 * unsigned(trailing), prec);
00199 double scale = 1;
00200 for (unsigned i = 0; i < unsigned(trailing); ++i)
00201 scale *= 60;
00202 for (unsigned i = 0; i < prec; ++i)
00203 scale *= 10;
00204 if (ind == AZIMUTH)
00205 angle -= floor(angle/360) * 360;
00206 int sign = angle < 0 ? -1 : 1;
00207 angle *= sign;
00208
00209
00210
00211 double
00212 idegree = floor(angle),
00213 fdegree = floor((angle - idegree) * scale + 0.5) / scale;
00214 if (fdegree >= 1) {
00215 idegree += 1;
00216 fdegree -= 1;
00217 }
00218 double pieces[3] = {fdegree, 0, 0};
00219 for (unsigned i = 1; i <= unsigned(trailing); ++i) {
00220 double
00221 ip = floor(pieces[i - 1]),
00222 fp = pieces[i - 1] - ip;
00223 pieces[i] = fp * 60;
00224 pieces[i - 1] = ip;
00225 }
00226 pieces[0] += idegree;
00227 ostringstream s;
00228 s << fixed << setfill('0');
00229 if (ind == NONE && sign < 0)
00230 s << '-';
00231 switch (trailing) {
00232 case DEGREE:
00233 if (ind != NONE)
00234 s << setw(1 + min(int(ind), 2) + prec + (prec ? 1 : 0));
00235 s << setprecision(prec) << pieces[0];
00236 break;
00237 default:
00238 if (ind != NONE)
00239 s << setw(1 + min(int(ind), 2));
00240 s << setprecision(0) << pieces[0] << char(tolower(dmsindicators[0]));
00241 switch (trailing) {
00242 case MINUTE:
00243 s << setw(2 + prec + (prec ? 1 : 0)) << setprecision(prec)
00244 << pieces[1] << char(tolower(dmsindicators[1]));
00245 break;
00246 case SECOND:
00247 s << setw(2) << pieces[1] << char(tolower(dmsindicators[1]))
00248 << setw(2 + prec + (prec ? 1 : 0)) << setprecision(prec)
00249 << pieces[2] << char(tolower(dmsindicators[2]));
00250 break;
00251 default:
00252 break;
00253 }
00254 }
00255 if (ind != NONE && ind != AZIMUTH)
00256 s << hemispheres[(ind == LATITUDE ? 0 : 2) + (sign < 0 ? 0 : 1)];
00257 return s.str();
00258 }
00259
00260 }