UTMUPS.cpp
Go to the documentation of this file.
00001 /**
00002  * \file UTMUPS.cpp
00003  * \brief Implementation for GeographicLib::UTMUPS class
00004  *
00005  * Copyright (c) Charles Karney (2008) <charles@karney.com>
00006  * and licensed under the LGPL.
00007  **********************************************************************/
00008 
00009 #include <cmath>
00010 #include <algorithm>
00011 #include <stdexcept>
00012 #include <limits>
00013 #include "GeographicLib/UTMUPS.hpp"
00014 #include "GeographicLib/MGRS.hpp"
00015 #include "GeographicLib/PolarStereographic.hpp"
00016 #include "GeographicLib/TransverseMercator.hpp"
00017 
00018 namespace {
00019   char RCSID[] = "$Id: UTMUPS.cpp 6535 2009-02-10 22:37:07Z ckarney $";
00020   char RCSID_H[] = UTMUPS_HPP;
00021 }
00022 
00023 namespace GeographicLib {
00024 
00025   using namespace std;
00026 
00027   const double UTMUPS::falseeasting[4] =
00028     { MGRS::upseasting * MGRS::tile, MGRS::upseasting * MGRS::tile,
00029       MGRS::utmeasting * MGRS::tile, MGRS::utmeasting* MGRS::tile };
00030   const double UTMUPS::falsenorthing[4] =
00031     { MGRS::upseasting * MGRS::tile, MGRS::upseasting * MGRS::tile,
00032       MGRS::maxutmSrow * MGRS::tile, MGRS::minutmNrow * MGRS::tile };
00033   const double UTMUPS::mineasting[4] =
00034     { MGRS::minupsSind * MGRS::tile,  MGRS::minupsNind * MGRS::tile,
00035       MGRS::minutmcol * MGRS::tile,  MGRS::minutmcol * MGRS::tile };
00036   const double UTMUPS::maxeasting[4] =
00037     { MGRS::maxupsSind * MGRS::tile,  MGRS::maxupsNind * MGRS::tile,
00038       MGRS::maxutmcol * MGRS::tile,  MGRS::maxutmcol * MGRS::tile };
00039   const double UTMUPS::minnorthing[4] =
00040     { MGRS::minupsSind * MGRS::tile,  MGRS::minupsNind * MGRS::tile,
00041       MGRS::minutmSrow * MGRS::tile,
00042       (MGRS::minutmNrow + MGRS::minutmSrow - MGRS::maxutmSrow) * MGRS::tile };
00043   const double UTMUPS::maxnorthing[4] =
00044     { MGRS::maxupsSind * MGRS::tile,  MGRS::maxupsNind * MGRS::tile,
00045       (MGRS::maxutmSrow + MGRS::maxutmNrow - MGRS::minutmNrow) * MGRS::tile,
00046       MGRS::maxutmNrow * MGRS::tile };
00047 
00048   int UTMUPS::StandardZone(double lat, double lon)  throw() {
00049     // Assume lon is in [-180, 360].
00050     int zone;
00051     int ilat = int(floor(lat));
00052     if (ilat >= 84 || ilat < -80)
00053       zone = 0;
00054     else {
00055       int ilon = int(floor(lon));
00056       if (ilon >= 180)
00057         ilon -= 360;
00058       zone = (ilon + 186)/6;
00059       int band = MGRS::LatitudeBand(lat);
00060       if (band == 7 && zone == 31 && ilon >= 3)
00061         zone = 32;
00062       else if (band == 9 && ilon >= 0 && ilon < 42)
00063         zone = 2 * ((ilon + 183)/12) + 1;
00064     }
00065     return zone;
00066   }
00067 
00068   void UTMUPS::Forward(double lat, double lon,
00069                        int& zone, bool& northp, double& x, double& y,
00070                        double& gamma, double& k,
00071                        int setzone) {
00072     CheckLatLon(lat, lon);
00073     northp = lat >= 0;
00074     zone = setzone >= 0 ? setzone : StandardZone(lat, lon);
00075     if (setzone > 60)
00076       throw out_of_range("Illegal UTM zone requested " + setzone);
00077     double x1, y1;
00078     bool utmp = zone > 0;
00079     if (utmp) {
00080       double
00081         lon0 = CentralMeridian(zone),
00082         dlon = lon - lon0;
00083       dlon = abs(dlon - 360 * floor((dlon + 180)/360));
00084       if (dlon > 60)
00085         // Check isn't really necessary because CheckCoords catches this case.
00086         // But this allows a more meaningful error message to be given.
00087         throw out_of_range("Longitude " + str(lon)
00088                                 + "d more than 60d from center of UTM zone "
00089                                 + str(zone));
00090       TransverseMercator::UTM.Forward(lon0, lat, lon, x1, y1, gamma, k);
00091     } else {
00092       if (abs(lat) < 70)
00093         // Check isn't really necessary ... (see above).
00094         throw out_of_range("Latitude " + str(lat)
00095                                 + "d more than 20d from "
00096                                 + (northp ? "N" : "S") + " pole");
00097       PolarStereographic::UPS.Forward(northp, lat, lon, x1, y1, gamma, k);
00098     }
00099     int ind = (utmp ? 2 : 0) + (northp ? 1 : 0);
00100     x = x1 + falseeasting[ind];
00101     y = y1 + falsenorthing[ind];
00102     if (! CheckCoords(zone > 0, northp, x, y, false) )
00103       throw out_of_range("Latitude " + str(lat) +
00104                               ", longitude " + str(lon) +
00105                               " out of legal range for " +
00106                               (utmp ? "UTM zone " + str(zone) : "UPS"));
00107   }
00108 
00109   void UTMUPS::Reverse(int zone, bool northp, double x, double y,
00110                        double& lat, double& lon, double& gamma, double& k) {
00111     if (! (zone >= 0 && zone <= 60))
00112       throw out_of_range("Illegal UTM zone " + str(zone));
00113     CheckCoords(zone > 0, northp, x, y);
00114     bool utmp = zone > 0;
00115     int ind = (utmp ? 2 : 0) + (northp ? 1 : 0);
00116     x -= falseeasting[ind];
00117     y -= falsenorthing[ind];
00118     if (utmp)
00119       TransverseMercator::UTM.Reverse(CentralMeridian(zone),
00120                                       x, y, lat, lon, gamma, k);
00121     else
00122       PolarStereographic::UPS.Reverse(northp, x, y, lat, lon, gamma, k);
00123   }
00124 
00125   void UTMUPS::CheckLatLon(double lat, double lon) {
00126     if (! (lat >= -90 && lat <= 90))
00127       throw out_of_range("Latitude " + str(lat) +
00128                               "d not in [-90d, 90d]");
00129     if (! (lon >= -180 && lon <= 360))
00130       throw out_of_range("Latitude " + str(lon) +
00131                               "d not in [-180d, 360d]");
00132     }
00133 
00134   bool UTMUPS::CheckCoords(bool utmp, bool northp, double x, double y,
00135                            bool throwp) {
00136     // Limits are all multiples of 100km and are all closed on the both ends.
00137     // Failure tests are all negated success tests so that NaNs fail.
00138     double slop = MGRS::tile;
00139     int ind = (utmp ? 2 : 0) + (northp ? 1 : 0);
00140     if (! (x >= mineasting[ind] - slop && x <= maxeasting[ind] + slop) ) {
00141       if (!throwp) return false;
00142       throw out_of_range("Easting " + str(x/1000)
00143                               + "km not in "
00144                               + (utmp ? "UTM" : "UPS") + " range for "
00145                               + (northp ? "N" : "S" )
00146                               + " hemisphere ["
00147                               + str((mineasting[ind] - slop)/1000) + "km, "
00148                               + str((maxeasting[ind] + slop)/1000) + "km]");
00149     }
00150     if (! (y >= minnorthing[ind] - slop && y <= maxnorthing[ind] + slop) ) {
00151       if (!throwp) return false;
00152       throw out_of_range("Northing " + str(y/1000)
00153                               + "km not in "
00154                               + (utmp ? "UTM" : "UPS") + " range for "
00155                               + (northp ? "N" : "S" )
00156                               + " hemisphere ["
00157                               + str((minnorthing[ind] - slop)/1000) + "km, "
00158                               + str((maxnorthing[ind] + slop)/1000) + "km]");
00159     }
00160     return true;
00161   }
00162 
00163 } // namespace GeographicLib