core/vpgl/vpgl_utm.cxx
Go to the documentation of this file.
00001 // stub file for IUE class IUE_UTM_geodetic_transform
00002 // generated by the IUE LateX macros from file
00003 // UTM_geodetic_transform.tex
00004 
00005 // Copyright (c) 1993 - 1997 Amerinex Applied Imaging, Inc.
00006 // 409 Main St Amherst, MA 01002 All Rights Reserved
00007 // Reproduction rights limited as described in the COPYRIGHT file.
00008 
00009 // A @class{IUE_UTM_geodetic_transform} is one that relates UTM coordinates to a geodetic lat_long coordinate_system (phi, lambda, h) using a reference spheroid to define the shape of the earth (or other celestial object). where in the geodetic lat_long coordinate system, coordinates phi and lambda are the values of latitude and longitude, respectively.
00010 #include "vpgl_utm.h"
00011 #include <vcl_iostream.h>
00012 #include <vcl_limits.h>
00013 #include <vnl/vnl_math.h>
00014 #define DBLLONG 4.61168601e18
00015 #define EPSLN   1.0e-10
00016 #define D2R     1.745329251994328e-2
00017 #define MAX_VAL  4
00018 
00019 // *****************************************************************************
00020 // NAME UNIVERSAL TRANSVERSE MERCATOR - GEODETIC
00021 //
00022 // PURPOSE: Transforms UTM (x, y, z) to geodetic (phi, lambda, h).
00023 //                 x,y,z values must be in meters, and (phi, lambda, h) will
00024 //         be returned in radians.
00025 //
00026 // NOTE: UTM coordinate system has both zone and central_meridian slots.
00027 //                 If zone is defined (i.e. zone <> 0), then zone is used.
00028 //         Otherwise, central_meridian is used.
00029 //
00030 //         When dealing with Northern Hemisphere, is_southern_hemisphere
00031 //         slot must be 0, and phi must be positive or equal to 0.
00032 //         When dealing with Southern Hemisphere, is_southern_hemisphere
00033 //         slot must be 1, and phi must be negative or equal to 0.
00034 //
00035 // ALGORITHM REFERENCES
00036 //
00037 // 1. Snyder, John P., "Map Projections--A Working Manual", U.S. Geological
00038 //     Survey Professional Paper 1395 (Supersedes USGS Bulletin 1532), United
00039 //     State Government Printing Office, Washington D.C., 1987.
00040 //
00041 // 2. Snyder, John P. and Voxland, Philip M., "An Album of Map Projections",
00042 //     U.S. Geological Survey Professional Paper 1453 , United State Government
00043 //     Printing Office, Washington D.C., 1989.
00044 //
00045 // 3. Code adapted from "USGS General Cartographic Transformation Package -
00046 //       C code (GCTPC).
00047 // *****************************************************************************
00048 static double scale_factor2;
00049 static double es2, esp2;
00050 static double ml02;
00051 static double false_easting2;
00052 static double false_northing2 ;
00053 static int ind2;
00054 static double e02,e12,e22,e32;
00055 
00056 // Function to return the sign of an argument
00057 // ------------------------------------------
00058 static inline int sign(double x) { return (x < 0.0) ? -1 : 1;}
00059 
00060 static inline double sqr(double x) { return x*x; }
00061 // Function to adjust a longitude angle to range from -180 to 180 radians
00062 // added if statements
00063 // ----------------------------------------------------------------------
00064 static double adjust_lon2(double x) // Angle in radians
00065 {
00066   for (long count = 0; count <= MAX_VAL; ++count)
00067   {
00068     if (vnl_math_abs(x)<=vnl_math::pi)
00069       break;
00070     else if (long(vnl_math_abs(x / (vnl_math::pi))) < 2)
00071       x -= (sign(x) * 2*vnl_math::pi);
00072     else if (long(vnl_math_abs(x / (2*vnl_math::pi))) < vcl_numeric_limits<long>::max())
00073       x -= long((x /(2*vnl_math::pi))*2*vnl_math::pi);
00074     else if (((long) vnl_math_abs(x / (vcl_numeric_limits<long>::max() * 2*vnl_math::pi))) < vcl_numeric_limits<long>::max())
00075       x -= (((long)(x / (vcl_numeric_limits<long>::max() * 2*vnl_math::pi))) * (2*vnl_math::pi * vcl_numeric_limits<long>::max()));
00076     else if (((long) vnl_math_abs(x / (DBLLONG * 2*vnl_math::pi))) < vcl_numeric_limits<long>::max())
00077       x -= (((long)(x / (DBLLONG * 2*vnl_math::pi))) * (2*vnl_math::pi * DBLLONG));
00078     else
00079       x -= (sign(x) *2*vnl_math::pi);
00080   }
00081 
00082   return x;
00083 }
00084 
00085 static double adjust_lat2(double x) // Angle in radians
00086 {
00087   for (long count = 0; count <= MAX_VAL; ++count)
00088   {
00089     if (vnl_math_abs(x)<=vnl_math::pi_over_2)
00090       break;
00091     else if (((long) vnl_math_abs(x / vnl_math::pi_over_2)) < 2)
00092       x -= (sign(x) * vnl_math::pi);
00093     else if (((long) vnl_math_abs(x / vnl_math::pi)) < vcl_numeric_limits<long>::max())
00094       x -= (((long)(x / vnl_math::pi))*vnl_math::pi);
00095     else if (((long) vnl_math_abs(x / (vcl_numeric_limits<long>::max() * vnl_math::pi))) < vcl_numeric_limits<long>::max())
00096       x -= (((long)(x / (vcl_numeric_limits<long>::max() * vnl_math::pi))) * (vnl_math::pi * vcl_numeric_limits<long>::max()));
00097     else if (((long) vnl_math_abs(x / (DBLLONG * vnl_math::pi))) < vcl_numeric_limits<long>::max())
00098       x -= (((long)(x / (DBLLONG * vnl_math::pi))) * (vnl_math::pi * DBLLONG));
00099     else
00100       x -= (sign(x) *vnl_math::pi);
00101   }
00102 
00103   return x;
00104 }
00105 
00106 // Function computes the value of M which is the distance along a meridian
00107 // from the Equator to latitude phi.
00108 // ---------------------------------------------
00109 static inline double mlfn2(double e0, double e1, double e2, double e3, double phi)
00110 {
00111   return e0*phi-e1*vcl_sin(2.0*phi)+e2*vcl_sin(4.0*phi)-e3*vcl_sin(6.0*phi);
00112 }
00113 
00114 // Initialization function of UTM transform
00115 // ----------------------------------------
00116 static void UTM_init2(double lat_center2, double r_major, double e, int south_flag)
00117 {
00118   scale_factor2 = 0.9996;
00119 
00120   es2 = (double) e * (double) e;
00121   e02 = 1.0-0.25*es2*(1.0+es2/16.0*(3.0+1.25*es2));
00122   e12 = 0.375*es2*(1.0+0.25*es2*(1.0+0.46875*es2));
00123   e22 = 0.05859375*es2*es2*(1.0+0.75*es2);
00124   e32 = es2*es2*es2*(35.0/3072.0);
00125   esp2 = es2/(1.0 - es2);
00126   ml02 = r_major * mlfn2(e02, e12, e22, e32, lat_center2);
00127   false_easting2 = 500000.0;
00128 
00129   false_northing2 = (south_flag) ? 10000000.0 : 0.0;
00130 
00131   if (es2 < .00001)
00132     ind2 = 1;
00133   else
00134     ind2 = 0;
00135 }
00136 
00137 // Constructors
00138 
00139 // other constructors (from doc)
00140 
00141 // Construct a transform with the given coordinate systems.
00142 
00143 vpgl_utm::vpgl_utm()
00144   :   a_(6378137), b_(6356752.3142) //WGS-84 by default
00145 {}
00146 
00147 vpgl_utm::vpgl_utm(const vpgl_utm& t)
00148   :    a_(t.a_), b_(t.b_)
00149 {}
00150 
00151 vpgl_utm::~vpgl_utm()
00152 {}
00153 
00154 // Applies the transform to the instance of 3d point representing a location in the from_coordinate_system (UTM_coordinate_system) and creates a point represented the transformed location in the to_coordinate_system(geodetic_coordinate_system).
00155 void vpgl_utm::transform(int utm_zone, double x, double y, double z,
00156                          double& lat, double& lon , double& elev,
00157                          bool south_flag,
00158                          double utm_central_meridian)
00159 {
00160   //double D2R = vnl_math::pi_over_180;
00161   double e = vcl_sqrt((sqr(a_) - sqr(b_))/sqr(a_));
00162 
00163   if (utm_zone != 0)
00164     utm_central_meridian = (6 * utm_zone) - 183;
00165 
00166   double lon_center2 = adjust_lon2(utm_central_meridian * D2R);
00167   double lat_center2 = 0.0;
00168 
00169   double lambda, phi;
00170 
00171   UTM_init2(lat_center2, a_, e, south_flag);
00172 
00173   double con,temp_phi;    // temporary angles
00174   double delta_phi;    // difference between longitudes
00175   long i;    // counter variable
00176   double sin_phi, cos_phi, tan_phi;    // sin cos and tangent values
00177   double c, cs, t, ts, n, r, d, ds;    // temporary variables
00178   double f, h, g, temp;    // temporary variables
00179   long max_iter = 6;    // maximum number of iterations, I changed from 6 to 20
00180 
00181   if (ind2 != 0)
00182   {
00183     f = vcl_exp(x/(a_ * scale_factor2));
00184     g = .5 * (f - 1/f);
00185     temp = lat_center2 + y/(a_ * scale_factor2);
00186     h = vcl_cos(temp);
00187     con = vcl_sqrt((1.0 - h * h)/(1.0 + g * g));
00188     if (vnl_math_abs(con) > 1.0)
00189     {
00190       if (con > 1.0)
00191         phi = vcl_asin(1.0);
00192       else
00193         phi = vcl_asin(-1.0);
00194     }
00195     else
00196       phi = vcl_asin(con);
00197 
00198     if (temp < 0)
00199       phi = -phi;
00200     if ((g == 0) && (h == 0))
00201     {
00202       lambda = lon_center2;
00203     }
00204     else
00205     {
00206       lambda = adjust_lon2(vcl_atan2(g,h) + lon_center2);
00207     }
00208   }
00209   else
00210   {
00211     x -= false_easting2;
00212     y -= false_northing2;
00213 
00214     con = (ml02 + y / scale_factor2) / a_;
00215     temp_phi = con;
00216     for (i=0;;i++)
00217     {
00218       delta_phi =
00219         ((con + e12 * vcl_sin(2.0*temp_phi) - e22 * vcl_sin(4.0*temp_phi) + e32 *
00220           vcl_sin(6.0*temp_phi)) / e02) - temp_phi;
00221 #if 0
00222       delta_phi = ((con + e12 * vcl_sin(2.0*phi) - e22 * vcl_sin(4.0*phi)) / e02) - phi;
00223 #endif
00224       temp_phi += delta_phi;
00225 
00226       if (vnl_math_abs(delta_phi)<1E-6) break;
00227 
00228       if (i >= max_iter)
00229         vcl_cout << "Transform failed to converge" << vcl_endl;
00230     }
00231 
00232     if (vnl_math_abs(temp_phi) < vnl_math::pi_over_2)
00233     {
00234       sin_phi = vcl_sin(temp_phi);
00235       cos_phi = vcl_cos(temp_phi);
00236       tan_phi = vcl_tan(temp_phi);
00237       c = esp2 * sqr(cos_phi);
00238       cs = sqr(c);
00239       t = sqr(tan_phi);
00240       ts = sqr(t);
00241       con = 1.0 - es2 * sqr(sin_phi);
00242       n = a_ / vcl_sqrt(con);
00243       r = n * (1.0 - es2) / con;
00244       d = x / (n * scale_factor2);
00245       ds = sqr(d);
00246       phi =
00247         temp_phi - (n * tan_phi * ds / r) *
00248         (0.5 - ds / 24.0 * (5.0 + 3.0 * t + 10.0 * c - 4.0 * cs - 9.0 *
00249                             esp2 - ds / 30.0 * (61.0 + 90.0 * t + 298.0 *
00250                                                 c + 45.0 * ts - 252.0 *
00251                                                 esp2 - 3.0 * cs)));
00252       lambda =
00253         adjust_lon2(lon_center2 +
00254                     (d * (1.0 - ds / 6.0 * (1.0 + 2.0 * t + c - ds / 20.0
00255                                             * (5.0 - 2.0 * c + 28.0 * t -
00256                                                3.0 * cs + 8.0 * esp2 +
00257                                                24.0 * ts))) / cos_phi));
00258     }
00259     else
00260     {
00261       phi = vnl_math::pi_over_2 * sign(y);
00262       lambda = lon_center2;
00263     }
00264   }
00265 
00266   lat = phi/D2R; lon = lambda/D2R, elev = z;
00267 }
00268 
00269 void vpgl_utm::transform(int utm_zone, double x, double y,
00270                          double& lat, double& lon,
00271                          bool south_flag,
00272                          double utm_central_meridian)
00273 {
00274   double elev;
00275   this->transform(utm_zone, x, y, 0.0,
00276                   lat, lon, elev,
00277                   south_flag, utm_central_meridian);
00278   if (elev) { /* TODO */ }
00279 }
00280 
00281 void vpgl_utm::transform(double lat, double lon,
00282                          double& x, double& y, int& utm_zone)
00283 {
00284   // double D2R = vnl_math::pi_over_180;
00285   utm_zone = int((lon+180)/6.0) + 1;
00286   double e = vcl_sqrt(1.0 - b_*b_/(a_*a_));
00287   // This value must eventually set by user. lon_zone stands for
00288   // longitudinal zone, and it must be between 1 and 60.
00289   int south_flag;
00290   double utm_central_meridian = 0;
00291 
00292   utm_central_meridian = (6 * utm_zone) - 183;
00293 
00294   south_flag = lat<0;
00295 
00296   double lon_center = adjust_lon2(utm_central_meridian * D2R);
00297   double lat_center = 0.0;
00298 
00299   double phi = adjust_lat2(lat*D2R);
00300   double lambda = adjust_lon2(lon*D2R);
00301 
00302   UTM_init2(lat_center, a_, e, south_flag);
00303 
00304   //  double bl = -1000000.0;
00305 
00306   double delta_lon = adjust_lon2(lambda - lon_center);
00307 
00308   double sin_phi = vcl_sin(phi);
00309   double cos_phi = vcl_cos(phi);
00310 
00311   double al = cos_phi * delta_lon;
00312   double als = sqr(al);
00313   double c = esp2 * sqr(cos_phi);
00314   double tq = vcl_tan(phi);
00315   double t = sqr(tq);
00316   double con = 1.0 - es2 * sqr(sin_phi);
00317   double n = a_ / vcl_sqrt(con);
00318   double ml = a_ * mlfn2(e02, e12, e22, e32, phi);
00319 
00320   x = scale_factor2 * n * al * (1.0 + (als / 6.0) *
00321                                 (1.0 - t + c + (als / 20.0) *
00322                                  (5.0 - (18.0 * t) + sqr(t) + (72.0 * c)
00323                                   - (58.0 * esp2))))
00324       + false_easting2;
00325 
00326   y = scale_factor2 * (ml - ml02 + n * tq * (als * (0.5 + (als / 24.0) *
00327                                              (5.0 - t + (9.0 * c)
00328                                               + (4.0 * sqr(c))
00329                                               + (als / 30.0) *
00330                                               (61.0 - (58.0 * t) +
00331                                                sqr(t) +
00332                                                (600.0 * c) -
00333                                                330.0 * esp2)))))
00334       + false_northing2;
00335 }