vpgl_utm.cxx
Go to the documentation of this file.
1 // stub file for IUE class IUE_UTM_geodetic_transform
2 // generated by the IUE LateX macros from file
3 // UTM_geodetic_transform.tex
4 
5 // Copyright (c) 1993 - 1997 Amerinex Applied Imaging, Inc.
6 // 409 Main St Amherst, MA 01002 All Rights Reserved
7 // Reproduction rights limited as described in the COPYRIGHT file.
8 
9 // 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.
10 #include <iostream>
11 #include <limits>
12 #include "vpgl_utm.h"
13 #ifdef _MSC_VER
14 # include <vcl_msvc_warnings.h>
15 #endif
16 #include <vnl/vnl_math.h>
17 #define DBLLONG 4.61168601e18
18 #define EPSLN 1.0e-10
19 #define D2R 1.745329251994328e-2
20 #define MAX_VAL 4
21 
22 // *****************************************************************************
23 // NAME UNIVERSAL TRANSVERSE MERCATOR - GEODETIC
24 //
25 // PURPOSE: Transforms UTM (x, y, z) to geodetic (phi, lambda, h).
26 // x,y,z values must be in meters, and (phi, lambda, h) will
27 // be returned in radians.
28 //
29 // NOTE: UTM coordinate system has both zone and central_meridian slots.
30 // If zone is defined (i.e. zone <> 0), then zone is used.
31 // Otherwise, central_meridian is used.
32 //
33 // When dealing with Northern Hemisphere, is_southern_hemisphere
34 // slot must be 0, and phi must be positive or equal to 0.
35 // When dealing with Southern Hemisphere, is_southern_hemisphere
36 // slot must be 1, and phi must be negative or equal to 0.
37 //
38 // ALGORITHM REFERENCES
39 //
40 // 1. Snyder, John P., "Map Projections--A Working Manual", U.S. Geological
41 // Survey Professional Paper 1395 (Supersedes USGS Bulletin 1532), United
42 // State Government Printing Office, Washington D.C., 1987.
43 //
44 // 2. Snyder, John P. and Voxland, Philip M., "An Album of Map Projections",
45 // U.S. Geological Survey Professional Paper 1453 , United State Government
46 // Printing Office, Washington D.C., 1989.
47 //
48 // 3. Code adapted from "USGS General Cartographic Transformation Package -
49 // C code (GCTPC).
50 // *****************************************************************************
51 static double scale_factor2;
52 static double es2, esp2;
53 static double ml02;
54 static double false_easting2;
55 static double false_northing2 ;
56 static int ind2;
57 static double e02,e12,e22,e32;
58 
59 // Function to return the sign of an argument
60 // ------------------------------------------
61 static inline int sign(double x) { return (x < 0.0) ? -1 : 1;}
62 
63 static inline double sqr(double x) { return x*x; }
64 // Function to adjust a longitude angle to range from -180 to 180 radians
65 // added if statements
66 // ----------------------------------------------------------------------
67 static double adjust_lon2(double x) // Angle in radians
68 {
69  for (long count = 0; count <= MAX_VAL; ++count)
70  {
71  if (vnl_math::abs(x)<=vnl_math::pi)
72  break;
73  else if (long(vnl_math::abs(x / vnl_math::pi)) < 2)
74  x -= (sign(x) * vnl_math::twopi);
75  else if (long(vnl_math::abs(x / (vnl_math::twopi))) < std::numeric_limits<long>::max())
76  x -= long((x/vnl_math::twopi)*vnl_math::twopi);
77  else if (((long) vnl_math::abs(x / (std::numeric_limits<long>::max() * vnl_math::twopi))) < std::numeric_limits<long>::max())
78  x -= (((long)(x / (std::numeric_limits<long>::max() * vnl_math::twopi))) * (vnl_math::twopi * std::numeric_limits<long>::max()));
79  else if (((long) vnl_math::abs(x / (DBLLONG * vnl_math::twopi))) < std::numeric_limits<long>::max())
80  x -= (((long)(x / (DBLLONG * vnl_math::twopi))) * (vnl_math::twopi * DBLLONG));
81  else
82  x -= (sign(x) * vnl_math::twopi);
83  }
84 
85  return x;
86 }
87 
88 static double adjust_lat2(double x) // Angle in radians
89 {
90  for (long count = 0; count <= MAX_VAL; ++count)
91  {
92  if (vnl_math::abs(x)<=vnl_math::pi_over_2)
93  break;
94  else if (((long) vnl_math::abs(x / vnl_math::pi_over_2)) < 2)
95  x -= (sign(x) * vnl_math::pi);
96  else if (((long) vnl_math::abs(x / vnl_math::pi)) < std::numeric_limits<long>::max())
97  x -= ((long)(x / vnl_math::pi))*vnl_math::pi;
98  else if (((long) vnl_math::abs(x / (std::numeric_limits<long>::max() * vnl_math::pi))) < std::numeric_limits<long>::max())
99  x -= (((long)(x / (std::numeric_limits<long>::max() * vnl_math::pi))) * (vnl_math::pi * std::numeric_limits<long>::max()));
100  else if (((long) vnl_math::abs(x / (DBLLONG * vnl_math::pi))) < std::numeric_limits<long>::max())
101  x -= (((long)(x / (DBLLONG * vnl_math::pi))) * (vnl_math::pi * DBLLONG));
102  else
103  x -= (sign(x) *vnl_math::pi);
104  }
105 
106  return x;
107 }
108 
109 // Function computes the value of M which is the distance along a meridian
110 // from the Equator to latitude phi.
111 // ---------------------------------------------
112 static inline double mlfn2(double e0, double e1, double e2, double e3, double phi)
113 {
114  return e0*phi-e1*std::sin(2.0*phi)+e2*std::sin(4.0*phi)-e3*std::sin(6.0*phi);
115 }
116 
117 // Initialization function of UTM transform
118 // ----------------------------------------
119 static void UTM_init2(double lat_center2, double r_major, double e, int south_flag)
120 {
121  scale_factor2 = 0.9996;
122 
123  es2 = e * (double) e;
124  e02 = 1.0-0.25*es2*(1.0+es2/16.0*(3.0+1.25*es2));
125  e12 = 0.375*es2*(1.0+0.25*es2*(1.0+0.46875*es2));
126  e22 = 0.05859375*es2*es2*(1.0+0.75*es2);
127  e32 = es2*es2*es2*(35.0/3072.0);
128  esp2 = es2/(1.0 - es2);
129  ml02 = r_major * mlfn2(e02, e12, e22, e32, lat_center2);
130  false_easting2 = 500000.0;
131 
132  false_northing2 = (south_flag) ? 10000000.0 : 0.0;
133 
134  if (es2 < .00001)
135  ind2 = 1;
136  else
137  ind2 = 0;
138 }
139 
140 // Constructors
141 
142 // other constructors (from doc)
143 
144 // Construct a transform with the given coordinate systems.
145 
147  : a_(6378137), b_(6356752.3142) //WGS-84 by default
148 {}
149 
150 vpgl_utm::vpgl_utm(const vpgl_utm& t) = default;
151 
152 vpgl_utm::~vpgl_utm() = default;
153 
154 // 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).
155 void vpgl_utm::transform(int utm_zone, double x, double y, double z,
156  double& lat, double& lon , double& elev,
157  bool south_flag,
158  double utm_central_meridian)
159 {
160  //double D2R = vnl_math::pi_over_180;
161  double e = std::sqrt((sqr(a_) - sqr(b_))/sqr(a_));
162 
163  if (utm_zone != 0)
164  utm_central_meridian = (6 * utm_zone) - 183;
165 
166  double lon_center2 = adjust_lon2(utm_central_meridian * D2R);
167  double lat_center2 = 0.0;
168 
169  double lambda, phi;
170 
171  UTM_init2(lat_center2, a_, e, south_flag);
172 
173  double con,temp_phi; // temporary angles
174  double delta_phi; // difference between longitudes
175  long i; // counter variable
176  double sin_phi, cos_phi, tan_phi; // sin cos and tangent values
177  double c, cs, t, ts, n, r, d, ds; // temporary variables
178  double f, h, g, temp; // temporary variables
179  long max_iter = 6; // maximum number of iterations, I changed from 6 to 20
180 
181  if (ind2 != 0)
182  {
183  f = std::exp(x/(a_ * scale_factor2));
184  g = .5 * (f - 1/f);
185  temp = lat_center2 + y/(a_ * scale_factor2);
186  h = std::cos(temp);
187  con = std::sqrt((1.0 - h * h)/(1.0 + g * g));
188  if (vnl_math::abs(con) > 1.0)
189  {
190  if (con > 1.0)
191  phi = std::asin(1.0);
192  else
193  phi = std::asin(-1.0);
194  }
195  else
196  phi = std::asin(con);
197 
198  if (temp < 0)
199  phi = -phi;
200  if ((g == 0) && (h == 0))
201  {
202  lambda = lon_center2;
203  }
204  else
205  {
206  lambda = adjust_lon2(std::atan2(g,h) + lon_center2);
207  }
208  }
209  else
210  {
211  x -= false_easting2;
212  y -= false_northing2;
213 
214  con = (ml02 + y / scale_factor2) / a_;
215  temp_phi = con;
216  for (i=0;;i++)
217  {
218  delta_phi =
219  ((con + e12 * std::sin(2.0*temp_phi) - e22 * std::sin(4.0*temp_phi) + e32 *
220  std::sin(6.0*temp_phi)) / e02) - temp_phi;
221 #if 0
222  delta_phi = ((con + e12 * std::sin(2.0*phi) - e22 * std::sin(4.0*phi)) / e02) - phi;
223 #endif
224  temp_phi += delta_phi;
225 
226  if (vnl_math::abs(delta_phi)<1E-6) break;
227 
228  if (i >= max_iter)
229  std::cout << "Transform failed to converge" << std::endl;
230  }
231 
232  if (vnl_math::abs(temp_phi) < vnl_math::pi_over_2)
233  {
234  sin_phi = std::sin(temp_phi);
235  cos_phi = std::cos(temp_phi);
236  tan_phi = std::tan(temp_phi);
237  c = esp2 * sqr(cos_phi);
238  cs = sqr(c);
239  t = sqr(tan_phi);
240  ts = sqr(t);
241  con = 1.0 - es2 * sqr(sin_phi);
242  n = a_ / std::sqrt(con);
243  r = n * (1.0 - es2) / con;
244  d = x / (n * scale_factor2);
245  ds = sqr(d);
246  phi =
247  temp_phi - (n * tan_phi * ds / r) *
248  (0.5 - ds / 24.0 * (5.0 + 3.0 * t + 10.0 * c - 4.0 * cs - 9.0 *
249  esp2 - ds / 30.0 * (61.0 + 90.0 * t + 298.0 *
250  c + 45.0 * ts - 252.0 *
251  esp2 - 3.0 * cs)));
252  lambda =
253  adjust_lon2(lon_center2 +
254  (d * (1.0 - ds / 6.0 * (1.0 + 2.0 * t + c - ds / 20.0
255  * (5.0 - 2.0 * c + 28.0 * t -
256  3.0 * cs + 8.0 * esp2 +
257  24.0 * ts))) / cos_phi));
258  }
259  else
260  {
261  phi = vnl_math::pi_over_2 * sign(y);
262  lambda = lon_center2;
263  }
264  }
265 
266  lat = phi/D2R; lon = lambda/D2R, elev = z;
267 }
268 
269 void vpgl_utm::transform(int utm_zone, double x, double y,
270  double& lat, double& lon,
271  bool south_flag,
272  double utm_central_meridian)
273 {
274  double elev;
275  this->transform(utm_zone, x, y, 0.0,
276  lat, lon, elev,
277  south_flag, utm_central_meridian);
278  if (elev) { /* TODO */ }
279 }
280 
281 void vpgl_utm::transform(double lat, double lon,
282  double& x, double& y, int& utm_zone)
283 {
284  // double D2R = vnl_math::pi_over_180;
285  utm_zone = int((lon+180)/6.0) + 1;
286  double e = std::sqrt(1.0 - b_*b_/(a_*a_));
287  // This value must eventually set by user. lon_zone stands for
288  // longitudinal zone, and it must be between 1 and 60.
289  int south_flag;
290  double utm_central_meridian = 0;
291 
292  utm_central_meridian = (6 * utm_zone) - 183;
293 
294  south_flag = lat<0;
295 
296  double lon_center = adjust_lon2(utm_central_meridian * D2R);
297  double lat_center = 0.0;
298 
299  double phi = adjust_lat2(lat*D2R);
300  double lambda = adjust_lon2(lon*D2R);
301 
302  UTM_init2(lat_center, a_, e, south_flag);
303 
304  // double bl = -1000000.0;
305 
306  double delta_lon = adjust_lon2(lambda - lon_center);
307 
308  double sin_phi = std::sin(phi);
309  double cos_phi = std::cos(phi);
310 
311  double al = cos_phi * delta_lon;
312  double als = sqr(al);
313  double c = esp2 * sqr(cos_phi);
314  double tq = std::tan(phi);
315  double t = sqr(tq);
316  double con = 1.0 - es2 * sqr(sin_phi);
317  double n = a_ / std::sqrt(con);
318  double ml = a_ * mlfn2(e02, e12, e22, e32, phi);
319 
320  x = scale_factor2 * n * al * (1.0 + (als / 6.0) *
321  (1.0 - t + c + (als / 20.0) *
322  (5.0 - (18.0 * t) + sqr(t) + (72.0 * c)
323  - (58.0 * esp2))))
324  + false_easting2;
325 
326  y = scale_factor2 * (ml - ml02 + n * tq * (als * (0.5 + (als / 24.0) *
327  (5.0 - t + (9.0 * c)
328  + (4.0 * sqr(c))
329  + (als / 30.0) *
330  (61.0 - (58.0 * t) +
331  sqr(t) +
332  (600.0 * c) -
333  330.0 * esp2)))))
334  + false_northing2;
335 }
double a_
Definition: vpgl_utm.h:46
#define MAX_VAL
Definition: vpgl_utm.cxx:20
A rip-off of the IUE utm_geodedic and geodetic_utm transform classes which allows the GeoPt to suppor...
double b_
Definition: vpgl_utm.h:46
#define D2R
Definition: vpgl_utm.cxx:19
vnl_bignum abs(vnl_bignum const &x)
#define DBLLONG
Definition: vpgl_utm.cxx:17
vnl_bignum sqr(vnl_bignum const &x)
void transform(int utm_zone, double x, double y, double z, double &lat, double &lon, double &elev, bool south_flag=false, double utm_central_meridian=0)
Definition: vpgl_utm.cxx:155