UNCLASSIFIED

GeographicTranslator
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
Geocentric.cpp
Go to the documentation of this file.
1 // CLASSIFICATION: UNCLASSIFIED
2 
3 /***************************************************************************/
4 /* RSC IDENTIFIER: GEOCENTRIC
5  *
6  * ABSTRACT
7  *
8  * This component provides conversions between Geodetic coordinates (latitude,
9  * longitude in radians and height in meters) and Geocentric coordinates
10  * (X, Y, Z) in meters.
11  *
12  * ERROR HANDLING
13  *
14  * This component checks parameters for valid values. If an invalid value
15  * is found, the error code is combined with the current error code using
16  * the bitwise or. This combining allows multiple error codes to be
17  * returned. The possible error codes are:
18  *
19  * GEOCENT_NO_ERROR : No errors occurred in function
20  * GEOCENT_LAT_ERROR : Latitude out of valid range
21  * (-90 to 90 degrees)
22  * GEOCENT_LON_ERROR : Longitude out of valid range
23  * (-180 to 360 degrees)
24  * GEOCENT_A_ERROR : Semi-major axis less than or equal to zero
25  * GEOCENT_INV_F_ERROR : Inverse flattening outside of valid range
26  * (250 to 350)
27  *
28  *
29  * REUSE NOTES
30  *
31  * GEOCENTRIC is intended for reuse by any application that performs
32  * coordinate conversions between geodetic coordinates and geocentric
33  * coordinates.
34  *
35  *
36  * REFERENCES
37  *
38  * An Improved Algorithm for Geocentric to Geodetic Coordinate Conversion,
39  * Ralph Toms, February 1996 UCRL-JC-123138.
40  *
41  * Further information on GEOCENTRIC can be found in the Reuse Manual.
42  *
43  * GEOCENTRIC originated from : U.S. Army Topographic Engineering Center
44  * Geospatial Information Division
45  * 7701 Telegraph Road
46  * Alexandria, VA 22310-3864
47  *
48  * LICENSES
49  *
50  * None apply to this component.
51  *
52  * RESTRICTIONS
53  *
54  * GEOCENTRIC has no restrictions.
55  *
56  * ENVIRONMENT
57  *
58  * GEOCENTRIC was tested and certified in the following environments:
59  *
60  * 1. Solaris 2.5 with GCC version 2.8.1
61  * 2. Windows 95 with MS Visual C++ version 6
62  *
63  * MODIFICATIONS
64  *
65  * Date Description
66  * ---- -----------
67  * 25-02-97 Original Code
68  * 3-02-07 Original C++ Code
69  * 01/24/11 I. Krinsky BAEts28121
70  * Terrain Service rearchitecture
71  */
72 
73 
74 /***************************************************************************/
75 /*
76  * INCLUDES
77  */
78 
79 #include <math.h>
80 #include <float.h>
81 #include <stdlib.h>
82 #include "Geocentric.h"
83 #include "CartesianCoordinates.h"
84 #include "GeodeticCoordinates.h"
86 #include "ErrorMessages.h"
87 
88 /*
89  * math.h - is needed for calls to sin, cos, tan and sqrt.
90  * Geocentric.h - is needed for Error codes and prototype error checking.
91  * CartesianCoordinates.h - defines cartesian coordinates
92  * GeodeticCoordinates.h - defines geodetic coordinates
93  * CoordinateConversionException.h - Exception handler
94  * ErrorMessages.h - Contains exception messages
95  */
96 
97 
98 using namespace MSP::CCS;
99 
100 
101 /***************************************************************************/
102 /* DEFINES
103  *
104  */
105 
106 const double PI = 3.14159265358979323e0;
107 const double PI_OVER_2 = (PI / 2.0e0);
108 const int FALSE = 0;
109 const int TRUE = 1;
110 const double COS_67P5 = 0.38268343236508977; /* cosine of 67.5 degrees */
111 const double AD_C = 1.0026000; /* Toms region 1 constant */
112 
113 
114 /************************************************************************/
115 /* FUNCTIONS
116  *
117  */
118 
120  double ellipsoidSemiMajorAxis,
121  double ellipsoidFlattening ) :
123  Geocent_e2( 0.0066943799901413800 ),
124  Geocent_ep2( 0.00673949675658690300 )
125 {
126 /*
127  * The constructor receives the ellipsoid parameters
128  * as inputs and sets the corresponding state variables.
129  *
130  * ellipsoidSemiMajorAxis : Semi-major axis of ellipsoid, in meters. (input)
131  * ellipsoidFlattening : Flattening of ellipsoid. (input)
132  */
133 
134  double inv_f = 1 / ellipsoidFlattening;
135 
136  if (ellipsoidSemiMajorAxis <= 0.0)
138  if ((inv_f < 250) || (inv_f > 350))
139  { /* Inverse flattening must be between 250 and 350 */
141  }
142 
143  semiMajorAxis = ellipsoidSemiMajorAxis;
144  flattening = ellipsoidFlattening;
145 
146  Geocent_e2 = 2 * flattening - flattening * flattening;
147  Geocent_ep2 = (1 / (1 - Geocent_e2)) - 1;
148 
149  // Need to determine algorithm to use
150  Geocent_algorithm = UNDEFINED;
151 }
152 
153 
155 {
158  Geocent_e2 = g.Geocent_e2;
159  Geocent_ep2 = g.Geocent_ep2;
160 }
161 
162 
164 {
165 }
166 
167 
169 {
170  if( this != &g )
171  {
174  Geocent_e2 = g.Geocent_e2;
175  Geocent_ep2 = g.Geocent_ep2;
176  }
177 
178  return *this;
179 }
180 
181 
183  const MSP::CCS::GeodeticCoordinates* geodeticCoordinates )
184 {
185 /*
186  * The function convertFromGeodetic converts geodetic coordinates
187  * (latitude, longitude, and height) to geocentric coordinates (X, Y, Z),
188  * according to the current ellipsoid parameters.
189  *
190  * longitude : Geodetic longitude in radians (input)
191  * latitude : Geodetic latitude in radians (input)
192  * height : Geodetic height, in meters (input)
193  * X : Calculated Geocentric X coordinate, in meters (output)
194  * Y : Calculated Geocentric Y coordinate, in meters (output)
195  * Z : Calculated Geocentric Z coordinate, in meters (output)
196  *
197  */
198 
199  double Rn; /* Earth radius at location */
200  double Sin_Lat; /* sin(Latitude) */
201  double Sin2_Lat; /* Square of sin(Latitude) */
202  double Cos_Lat; /* cos(Latitude) */
203 
204  double longitude = geodeticCoordinates->longitude();
205  double latitude = geodeticCoordinates->latitude();
206  double height = geodeticCoordinates->height();
207 
208  if ((latitude < -PI_OVER_2) || (latitude > PI_OVER_2))
209  { /* Latitude out of range */
211  }
212  if ((longitude < -PI) || (longitude > (2*PI)))
213  { /* Longitude out of range */
215  }
216 
217  if (longitude > PI)
218  longitude -= (2*PI);
219  Sin_Lat = sin(latitude);
220  Cos_Lat = cos(latitude);
221  Sin2_Lat = Sin_Lat * Sin_Lat;
222  Rn = semiMajorAxis / (sqrt(1.0e0 - Geocent_e2 * Sin2_Lat));
223  double X = (Rn + height) * Cos_Lat * cos(longitude);
224  double Y = (Rn + height) * Cos_Lat * sin(longitude);
225  double Z = ((Rn * (1 - Geocent_e2)) + height) * Sin_Lat;
226 
227  return new CartesianCoordinates( CoordinateType::geocentric, X, Y, Z );
228 }
229 
230 
232  MSP::CCS::CartesianCoordinates* cartesianCoordinates )
233 {
234 /*
235  * The function convertToGeodetic converts geocentric
236  * coordinates (X, Y, Z) to geodetic coordinates (latitude, longitude,
237  * and height), according to the current ellipsoid parameters.
238  *
239  * X : Geocentric X coordinate, in meters. (input)
240  * Y : Geocentric Y coordinate, in meters. (input)
241  * Z : Geocentric Z coordinate, in meters. (input)
242  * longitude : Calculated longitude value in radians. (output)
243  * latitude : Calculated latitude value in radians. (output)
244  * height : Calculated height value, in meters. (output)
245  *
246  * The method used here is derived from 'An Improved Algorithm for
247  * Geocentric to Geodetic Coordinate Conversion', by Ralph Toms, Feb 1996
248  */
249 
250 /* Note: Variable names follow the notation used in Toms, Feb 1996 */
251 
252  double X = cartesianCoordinates->x();
253  double Y = cartesianCoordinates->y();
254  double Z = cartesianCoordinates->z();
255  double latitude, longitude, height;
256 
257  if( Geocent_algorithm == UNDEFINED )
258  {
259  Geocent_algorithm = ITERATIVE;
260  char *geotransConv = getenv( "MSPCCS_USE_LEGACY_GEOTRANS" );
261  if( geotransConv != NULL )
262  {
263  Geocent_algorithm = GEOTRANS;
264  }
265  }
266 
267  if( Geocent_algorithm == ITERATIVE )
268  {
269  geocentricToGeodetic( X, Y, Z, latitude, longitude, height );
270  }
271  else
272  {
273  double W; /* distance from Z axis */
274  double W2; /* square of distance from Z axis */
275  double T0; /* initial estimate of vertical component */
276  double T1; /* corrected estimate of vertical component */
277  double S0; /* initial estimate of horizontal component */
278  double S1; /* corrected estimate of horizontal component */
279  double Sin_B0; /* sin(B0), B0 is estimate of Bowring aux variable */
280  double Sin3_B0; /* cube of sin(B0) */
281  double Cos_B0; /* cos(B0) */
282  double Sin_p1; /* sin(phi1), phi1 is estimated latitude */
283  double Cos_p1; /* cos(phi1) */
284  double Rn; /* Earth radius at location */
285  double Sum; /* numerator of cos(phi1) */
286  int At_Pole; /* indicates location is in polar region */
287  double Geocent_b = semiMajorAxis * (1 - flattening); /* Semi-minor axis of ellipsoid, in meters */
288 
289  At_Pole = FALSE;
290  if (X != 0.0)
291  {
292  longitude = atan2(Y,X);
293  }
294  else
295  {
296  if (Y > 0)
297  {
298  longitude = PI_OVER_2;
299  }
300  else if (Y < 0)
301  {
302  longitude = -PI_OVER_2;
303  }
304  else
305  {
306  At_Pole = TRUE;
307  longitude = 0.0;
308  if (Z > 0.0)
309  { /* north pole */
310  latitude = PI_OVER_2;
311  }
312  else if (Z < 0.0)
313  { /* south pole */
314  latitude = -PI_OVER_2;
315  }
316  else
317  { /* center of earth */
318  latitude = PI_OVER_2;
319  height = -Geocent_b;
320  return new GeodeticCoordinates(
321  CoordinateType::geodetic, longitude, latitude, height );
322  }
323  }
324  }
325  W2 = X*X + Y*Y;
326  W = sqrt(W2);
327  T0 = Z * AD_C;
328  S0 = sqrt(T0 * T0 + W2);
329  Sin_B0 = T0 / S0;
330  Cos_B0 = W / S0;
331  Sin3_B0 = Sin_B0 * Sin_B0 * Sin_B0;
332  T1 = Z + Geocent_b * Geocent_ep2 * Sin3_B0;
333  Sum = W - semiMajorAxis * Geocent_e2 * Cos_B0 * Cos_B0 * Cos_B0;
334  S1 = sqrt(T1*T1 + Sum * Sum);
335  Sin_p1 = T1 / S1;
336  Cos_p1 = Sum / S1;
337  Rn = semiMajorAxis / sqrt(1.0 - Geocent_e2 * Sin_p1 * Sin_p1);
338  if (Cos_p1 >= COS_67P5)
339  {
340  height = W / Cos_p1 - Rn;
341  }
342  else if (Cos_p1 <= -COS_67P5)
343  {
344  height = W / -Cos_p1 - Rn;
345  }
346  else
347  {
348  height = Z / Sin_p1 + Rn * (Geocent_e2 - 1.0);
349  }
350  if (At_Pole == FALSE)
351  {
352  latitude = atan(Sin_p1 / Cos_p1);
353  }
354  }
355 
356  return new GeodeticCoordinates(
357  CoordinateType::geodetic, longitude, latitude, height );
358 }
359 
360 void Geocentric::geocentricToGeodetic(
361  const double x,
362  const double y,
363  const double z,
364  double &lat,
365  double &lon,
366  double &ht )
367 {
368  double equatorial_radius = semiMajorAxis;
369  double eccentricity_squared = Geocent_e2;
370 
371  double rho, c, s, ct2, e1, e2a;
372 
373  e1 = 1.0 - eccentricity_squared;
374  e2a = eccentricity_squared * equatorial_radius;
375 
376  rho = sqrt(x * x + y * y);
377 
378  if (z == 0.0)
379  {
380  if (rho < e2a)
381  {
382  ct2 = rho*rho*e1/(e2a*e2a-rho*rho);
383  c = sqrt(ct2 / (1.0 + ct2));
384  s = sqrt(1.0 / (1.0 + ct2));
385  }
386  else
387  {
388  c = 1.0;
389  s = 0.0;
390  }
391 
392  lat = 0.0;
393  }
394  else
395  {
396  double ct, new_ct, zabs;
397  double f, new_f, df_dct, e2;
398 
399  zabs = fabs(z);
400 
401  new_ct = rho / zabs;
402  new_f = DBL_MAX;
403 
404  do
405  {
406  ct = new_ct;
407  f = new_f;
408 
409  e2 = sqrt(e1 + ct*ct);
410 
411  new_f = rho - zabs*ct - e2a*ct/e2;
412 
413  if (new_f == 0.0) break;
414 
415  df_dct = -zabs - (e2a*e1)/(e2*e2*e2);
416 
417  new_ct = ct - new_f / df_dct;
418 
419  if (new_ct < 0.0) new_ct = 0.0;
420  }
421  while (fabs(new_f) < fabs(f));
422 
423  s = 1.0 / sqrt(1.0 + ct * ct);
424  c = ct * s;
425 
426  if (z < 0.0)
427  {
428  s = -s;
429  lat = -atan(1.0 / ct);
430  } else
431  {
432  lat = atan(1.0 / ct);
433  }
434  }
435 
436  lon = atan2(y, x);
437 
438  ht = rho*c + z*s - equatorial_radius*sqrt(1.0 - eccentricity_squared*s*s);
439 
440  return;
441 }
442 
443 
444 // CLASSIFICATION: UNCLASSIFIED