00001
00002
00003
00004
00005
00006
00007
00008
00009 #include "GeographicLib/MGRS.hpp"
00010 #include "GeographicLib/UTMUPS.hpp"
00011 #include <stdexcept>
00012 #include <limits>
00013
00014 namespace {
00015 char RCSID[] = "$Id: MGRS.cpp 6553 2009-02-24 03:10:01Z ckarney $";
00016 char RCSID_H[] = MGRS_HPP;
00017 }
00018
00019 namespace GeographicLib {
00020
00021 using namespace std;
00022
00023 const double MGRS::eps =
00024
00025
00026 pow(0.5, numeric_limits<double>::digits - 25);
00027 const double MGRS::angeps =
00028
00029 pow(0.5, numeric_limits<double>::digits - 7);
00030 const string MGRS::hemispheres = "SN";
00031 const string MGRS::utmcols[3] =
00032 { "ABCDEFGH", "JKLMNPQR", "STUVWXYZ" };
00033 const string MGRS::utmrow = "ABCDEFGHJKLMNPQRSTUV";
00034 const string MGRS::upscols[4] =
00035 { "JKLPQRSTUXYZ", "ABCFGHJKLPQR", "RSTUXYZ", "ABCFGHJ" };
00036 const string MGRS::upsrows[2] =
00037 { "ABCDEFGHJKLMNPQRSTUVWXYZ", "ABCDEFGHJKLMNP" };
00038 const string MGRS::latband = "CDEFGHJKLMNPQRSTUVWX";
00039 const string MGRS::upsband = "ABYZ";
00040 const string MGRS::digits = "0123456789";
00041
00042 const int MGRS::mineasting[4] =
00043 { minupsSind, minupsNind, minutmcol, minutmcol };
00044 const int MGRS::maxeasting[4] =
00045 { maxupsSind, maxupsNind, maxutmcol, maxutmcol };
00046 const int MGRS::minnorthing[4] =
00047 { minupsSind, minupsNind,
00048 minutmSrow, minutmSrow - (maxutmSrow - minutmNrow) };
00049 const int MGRS::maxnorthing[4] =
00050 { maxupsSind, maxupsNind,
00051 maxutmNrow + (maxutmSrow - minutmNrow), maxutmNrow };
00052
00053 void MGRS::Forward(int zone, bool northp, double x, double y, double lat,
00054 int prec, std::string& mgrs) {
00055 bool utmp = zone != 0;
00056 CheckCoords(utmp, northp, x, y);
00057 if (!(zone >= 0 || zone <= 60))
00058 throw out_of_range("Zone " + str(zone) + " not in [0,60]");
00059 if (!(prec >= 0 || prec <= maxprec))
00060 throw out_of_range("MGRS precision " + str(prec) + " not in [0, "
00061 + str(int(maxprec)) + "]");
00062 int
00063 zone1 = zone - 1,
00064 z = utmp ? 2 : 0;
00065
00066 mgrs.resize(z + 3 + 2 * prec);
00067 if (utmp) {
00068 mgrs[0] = digits[ zone / base ];
00069 mgrs[1] = digits[ zone % base ];
00070
00071
00072 }
00073 int
00074 xh = int(floor(x)) / tile,
00075 yh = int(floor(y)) / tile;
00076 double
00077 xf = x - tile * xh,
00078 yf = y - tile * yh;
00079 if (utmp) {
00080 int
00081
00082 iband = abs(lat) > angeps ? LatitudeBand(lat) : (northp ? 0 : -1),
00083 icol = xh - minutmcol,
00084 irow = UTMRow(iband, icol, yh % utmrowperiod);
00085 if (irow != yh - (northp ? minutmNrow : maxutmSrow))
00086 throw out_of_range("Latitude " + str(lat)
00087 + " is inconsistent with UTM coordinates");
00088 mgrs[z++] = latband[10 + iband];
00089 mgrs[z++] = utmcols[zone1 % 3][icol];
00090 mgrs[z++] = utmrow[(yh + (zone1 & 1 ? utmevenrowshift : 0))
00091 % utmrowperiod];
00092 } else {
00093 bool eastp = xh >= upseasting;
00094 int iband = (northp ? 2 : 0) + (eastp ? 1 : 0);
00095 mgrs[z++] = upsband[iband];
00096 mgrs[z++] = upscols[iband][xh - (eastp ? upseasting :
00097 northp ? minupsNind : minupsSind)];
00098 mgrs[z++] = upsrows[northp][yh - (northp ? minupsNind : minupsSind)];
00099 }
00100 double mult = pow(double(base), min(prec - tilelevel, 0));
00101 int
00102 ix = int(floor(xf * mult)),
00103 iy = int(floor(yf * mult));
00104 for (int c = min(prec, int(tilelevel)); c--;) {
00105 mgrs[z + c] = digits[ ix % base ];
00106 ix /= base;
00107 mgrs[z + c + prec] = digits[ iy % base ];
00108 iy /= base;
00109 }
00110 if (prec > tilelevel) {
00111 xf -= floor(xf * mult);
00112 yf -= floor(yf * mult);
00113 mult = pow(double(base), prec - tilelevel);
00114 ix = int(floor(xf * mult));
00115 iy = int(floor(yf * mult));
00116 for (int c = prec - tilelevel; c--;) {
00117 mgrs[z + c + tilelevel] = digits[ ix % base ];
00118 ix /= base;
00119 mgrs[z + c + tilelevel + prec] = digits[ iy % base ];
00120 iy /= base;
00121 }
00122 }
00123 }
00124
00125 void MGRS::Forward(int zone, bool northp, double x, double y,
00126 int prec, std::string& mgrs) {
00127 double lat, lon;
00128 if (zone)
00129 UTMUPS::Reverse(zone, northp, x, y, lat, lon);
00130 else
00131
00132 lat = 0;
00133 Forward(zone, northp, x, y, lat, prec, mgrs);
00134 }
00135
00136 void MGRS::Reverse(const std::string& mgrs,
00137 int& zone, bool& northp, double& x, double& y,
00138 int& prec, bool centerp) {
00139 int
00140 p = 0,
00141 len = int(mgrs.size());
00142 zone = 0;
00143 while (p < len) {
00144 int i = lookup(digits, mgrs[p]);
00145 if (i < 0)
00146 break;
00147 zone = 10 * zone + i;
00148 ++p;
00149 }
00150 if (p > 0 && (zone == 0 || zone > 60))
00151 throw out_of_range("Zone " + str(zone) + " not in [1,60]");
00152 if (p > 2)
00153 throw out_of_range("More than 2 digits at start of MGRS "
00154 + mgrs.substr(0, p));
00155 if (len - p < 3)
00156 throw out_of_range("MGRS string " + mgrs + " too short");
00157 bool utmp = zone != 0;
00158 int zone1 = zone - 1;
00159 const string& band = utmp ? latband : upsband;
00160 int iband = lookup(band, mgrs[p++]);
00161 if (iband < 0)
00162 throw out_of_range("Band letter " + str(mgrs[p-1])
00163 + " not in " + (utmp ? "UTM" : "UPS")
00164 + " set " + band);
00165 northp = iband >= (utmp ? 10 : 2);
00166 const string& col = utmp ? utmcols[zone1 % 3] : upscols[iband];
00167 const string& row = utmp ? utmrow : upsrows[northp];
00168 int icol = lookup(col, mgrs[p++]);
00169 if (icol < 0)
00170 throw out_of_range("Column letter " + str(mgrs[p-1])
00171 + " not in "
00172 + (utmp ? "zone " + mgrs.substr(0, p-2) :
00173 "UPS band " + str(mgrs[p-2]))
00174 + " set " + col );
00175 int irow = lookup(row, mgrs[p++]);
00176 if (irow < 0)
00177 throw out_of_range("Row letter " + str(mgrs[p-1])
00178 + " not in "
00179 + (utmp ? "UTM" :
00180 "UPS " + str(hemispheres[northp]))
00181 + " set " + row);
00182 if (utmp) {
00183 if (zone1 & 1)
00184 irow = (irow + utmrowperiod - utmevenrowshift) % utmrowperiod;
00185 iband -= 10;
00186 irow = UTMRow(iband, icol, irow);
00187 if (irow == maxutmSrow)
00188 throw out_of_range("Block " + mgrs.substr(p-2, 2)
00189 + " not in zone/band " + mgrs.substr(0, p-2));
00190
00191 irow = northp ? irow : irow + 100;
00192 icol = icol + minutmcol;
00193 } else {
00194 bool eastp = iband & 1;
00195 icol += eastp ? upseasting : northp ? minupsNind : minupsSind;
00196 irow += northp ? minupsNind : minupsSind;
00197 }
00198 prec = (len - p)/2;
00199 double unit = tile;
00200 x = unit * icol;
00201 y = unit * irow;
00202 for (int i = 0; i < prec; ++i) {
00203 unit /= base;
00204 int
00205 ix = lookup(digits, mgrs[p + i]),
00206 iy = lookup(digits, mgrs[p + i + prec]);
00207 if (ix < 0 || iy < 0)
00208 throw out_of_range("Encountered a non-digit in " + mgrs.substr(p));
00209 x += unit * ix;
00210 y += unit * iy;
00211 }
00212 if ((len - p) % 2) {
00213 if (lookup(digits, mgrs[len - 1]) < 0)
00214 throw out_of_range("Encountered a non-digit in " + mgrs.substr(p));
00215 else
00216 throw out_of_range("Not an even number of digits in "
00217 + mgrs.substr(p));
00218 }
00219 if (prec > maxprec)
00220 throw out_of_range("More than " + str(2*maxprec) + " digits in "
00221 + mgrs.substr(p));
00222 if (centerp) {
00223 x += unit/2;
00224 y += unit/2;
00225 }
00226 }
00227
00228 void MGRS::CheckCoords(bool utmp, bool& northp, double& x, double& y) {
00229
00230
00231
00232
00233
00234 int
00235 ix = int(floor(x / tile)),
00236 iy = int(floor(y / tile)),
00237 ind = (utmp ? 2 : 0) + (northp ? 1 : 0);
00238 if (! (ix >= mineasting[ind] && ix < maxeasting[ind]) ) {
00239 if (ix == maxeasting[ind] && x == maxeasting[ind] * tile)
00240 x -= eps;
00241 else
00242 throw out_of_range("Easting " + str(int(floor(x/1000)))
00243 + "km not in MGRS/"
00244 + (utmp ? "UTM" : "UPS") + " range for "
00245 + (northp ? "N" : "S" )
00246 + " hemisphere ["
00247 + str(mineasting[ind]*tile/1000) + "km, "
00248 + str(maxeasting[ind]*tile/1000) + "km)");
00249 }
00250 if (! (iy >= minnorthing[ind] && iy < maxnorthing[ind]) ) {
00251 if (iy == maxnorthing[ind] && y == maxnorthing[ind] * tile)
00252 y -= eps;
00253 else
00254 throw out_of_range("Northing " + str(int(floor(y/1000)))
00255 + "km not in MGRS/"
00256 + (utmp ? "UTM" : "UPS") + " range for "
00257 + (northp ? "N" : "S" )
00258 + " hemisphere ["
00259 + str(minnorthing[ind]*tile/1000) + "km, "
00260 + str(maxnorthing[ind]*tile/1000) + "km)");
00261 }
00262
00263
00264 if (utmp) {
00265 if (northp && iy < minutmNrow) {
00266 northp = false;
00267 y += utmNshift;
00268 } else if (!northp && iy >= maxutmSrow) {
00269 if (y == maxutmSrow * tile)
00270
00271 y -= eps;
00272 else {
00273 northp = true;
00274 y -= utmNshift;
00275 }
00276 }
00277 }
00278 }
00279
00280 int MGRS::UTMRow(int iband, int icol, int irow) throw() {
00281
00282
00283
00284
00285
00286
00287
00288 double c = 100 * (8 * iband + 4)/90.0;
00289 bool northp = iband >= 0;
00290 int
00291 minrow = iband > -10 ? int(floor(c - 4.3 - 0.1 * northp)) : -90,
00292 maxrow = iband < 9 ? int(floor(c + 4.4 - 0.1 * northp)) : 94,
00293 baserow = (minrow + maxrow) / 2 - utmrowperiod / 2;
00294
00295 irow = (irow - baserow + maxutmSrow) % utmrowperiod + baserow;
00296 if (irow < minrow || irow > maxrow) {
00297
00298
00299 int
00300
00301 sband = iband >= 0 ? iband : - iband - 1,
00302
00303 srow = irow >= 0 ? irow : -irow - 1,
00304
00305 scol = icol < 4 ? icol : -icol + 7;
00306 if ( ! ( (srow == 70 && sband == 8 && scol >= 2) ||
00307 (srow == 71 && sband == 7 && scol <= 2) ||
00308 (srow == 79 && sband == 9 && scol >= 1) ||
00309 (srow == 80 && sband == 8 && scol <= 1) ) )
00310 irow = maxutmSrow;
00311 }
00312 return irow;
00313 }
00314
00315 }