00001
00002
00003
00004
00005
00006
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
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
00086
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
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
00137
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 }