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  * 06/06/17 M. Thakkar LSC-5523
72  * Updated copy and equal operator to assign Geocent_algorithm
73  */
74 
75 
76 /***************************************************************************/
77 /*
78  * INCLUDES
79  */
80 
81 #include <math.h>
82 #include <float.h>
83 #include <stdlib.h>
84 #include "Geocentric.h"
85 #include "CartesianCoordinates.h"
86 #include "GeodeticCoordinates.h"
88 #include "ErrorMessages.h"
89 
90 /*
91  * math.h - is needed for calls to sin, cos, tan and sqrt.
92  * Geocentric.h - is needed for Error codes and prototype error checking.
93  * CartesianCoordinates.h - defines cartesian coordinates
94  * GeodeticCoordinates.h - defines geodetic coordinates
95  * CoordinateConversionException.h - Exception handler
96  * ErrorMessages.h - Contains exception messages
97  */
98 
99 
100 using namespace MSP::CCS;
101 
102 
103 /***************************************************************************/
104 /* DEFINES
105  *
106  */
107 
108 const double PI = 3.14159265358979323e0;
109 const double PI_OVER_2 = (PI / 2.0e0);
110 const int FALSE = 0;
111 const int TRUE = 1;
112 const double COS_67P5 = 0.38268343236508977; /* cosine of 67.5 degrees */
113 const double AD_C = 1.0026000; /* Toms region 1 constant */
114 
115 
116 /************************************************************************/
117 /* FUNCTIONS
118  *
119  */
120 
122  double ellipsoidSemiMajorAxis,
123  double ellipsoidFlattening ) :
125  Geocent_e2( 0.0066943799901413800 ),
126  Geocent_ep2( 0.00673949675658690300 )
127 {
128 /*
129  * The constructor receives the ellipsoid parameters
130  * as inputs and sets the corresponding state variables.
131  *
132  * ellipsoidSemiMajorAxis : Semi-major axis of ellipsoid, in meters. (input)
133  * ellipsoidFlattening : Flattening of ellipsoid. (input)
134  */
135 
136  double inv_f = 1 / ellipsoidFlattening;
137 
138  if (ellipsoidSemiMajorAxis <= 0.0)
140  if ((inv_f < 250) || (inv_f > 350))
141  { /* Inverse flattening must be between 250 and 350 */
143  }
144 
145  semiMajorAxis = ellipsoidSemiMajorAxis;
146  flattening = ellipsoidFlattening;
147 
148  Geocent_e2 = 2 * flattening - flattening * flattening;
149  Geocent_ep2 = (1 / (1 - Geocent_e2)) - 1;
150 
151  // Need to determine algorithm to use
152  Geocent_algorithm = UNDEFINED;
153 }
154 
155 
157 {
160  Geocent_e2 = g.Geocent_e2;
161  Geocent_ep2 = g.Geocent_ep2;
162  Geocent_algorithm = g.Geocent_algorithm; // LSC-5523
163 }
164 
165 
167 {
168 }
169 
170 
172 {
173  if( this != &g )
174  {
177  Geocent_e2 = g.Geocent_e2;
178  Geocent_ep2 = g.Geocent_ep2;
179  Geocent_algorithm = g.Geocent_algorithm; // LSC-5523
180  }
181 
182  return *this;
183 }
184 
185 
187  const MSP::CCS::GeodeticCoordinates* geodeticCoordinates )
188 {
189 /*
190  * The function convertFromGeodetic converts geodetic coordinates
191  * (latitude, longitude, and height) to geocentric coordinates (X, Y, Z),
192  * according to the current ellipsoid parameters.
193  *
194  * longitude : Geodetic longitude in radians (input)
195  * latitude : Geodetic latitude in radians (input)
196  * height : Geodetic height, in meters (input)
197  * X : Calculated Geocentric X coordinate, in meters (output)
198  * Y : Calculated Geocentric Y coordinate, in meters (output)
199  * Z : Calculated Geocentric Z coordinate, in meters (output)
200  *
201  */
202 
203  double Rn; /* Earth radius at location */
204  double Sin_Lat; /* sin(Latitude) */
205  double Sin2_Lat; /* Square of sin(Latitude) */
206  double Cos_Lat; /* cos(Latitude) */
207 
208  double longitude = geodeticCoordinates->longitude();
209  double latitude = geodeticCoordinates->latitude();
210  double height = geodeticCoordinates->height();
211 
212  if ((latitude < -PI_OVER_2) || (latitude > PI_OVER_2))
213  { /* Latitude out of range */
215  }
216  if ((longitude < -PI) || (longitude > (2*PI)))
217  { /* Longitude out of range */
219  }
220 
221  if (longitude > PI)
222  longitude -= (2*PI);
223  Sin_Lat = sin(latitude);
224  Cos_Lat = cos(latitude);
225  Sin2_Lat = Sin_Lat * Sin_Lat;
226  Rn = semiMajorAxis / (sqrt(1.0e0 - Geocent_e2 * Sin2_Lat));
227  double X = (Rn + height) * Cos_Lat * cos(longitude);
228  double Y = (Rn + height) * Cos_Lat * sin(longitude);
229  double Z = ((Rn * (1 - Geocent_e2)) + height) * Sin_Lat;
230 
231  return new CartesianCoordinates( CoordinateType::geocentric, X, Y, Z );
232 }
233 
234 
236  MSP::CCS::CartesianCoordinates* cartesianCoordinates )
237 {
238 /*
239  * The function convertToGeodetic converts geocentric
240  * coordinates (X, Y, Z) to geodetic coordinates (latitude, longitude,
241  * and height), according to the current ellipsoid parameters.
242  *
243  * X : Geocentric X coordinate, in meters. (input)
244  * Y : Geocentric Y coordinate, in meters. (input)
245  * Z : Geocentric Z coordinate, in meters. (input)
246  * longitude : Calculated longitude value in radians. (output)
247  * latitude : Calculated latitude value in radians. (output)
248  * height : Calculated height value, in meters. (output)
249  *
250  * The method used here is derived from 'An Improved Algorithm for
251  * Geocentric to Geodetic Coordinate Conversion', by Ralph Toms, Feb 1996
252  */
253 
254 /* Note: Variable names follow the notation used in Toms, Feb 1996 */
255 
256  double X = cartesianCoordinates->x();
257  double Y = cartesianCoordinates->y();
258  double Z = cartesianCoordinates->z();
259  double latitude, longitude, height;
260 
261  if( Geocent_algorithm == UNDEFINED )
262  {
263  Geocent_algorithm = ITERATIVE;
264  char *geotransConv = getenv( "MSPCCS_USE_LEGACY_GEOTRANS" );
265  if( geotransConv != NULL )
266  {
267  Geocent_algorithm = GEOTRANS;
268  }
269  }
270 
271  if( Geocent_algorithm == ITERATIVE )
272  {
273  geocentricToGeodetic( X, Y, Z, latitude, longitude, height );
274  }
275  else
276  {
277  double W; /* distance from Z axis */
278  double W2; /* square of distance from Z axis */
279  double T0; /* initial estimate of vertical component */
280  double T1; /* corrected estimate of vertical component */
281  double S0; /* initial estimate of horizontal component */
282  double S1; /* corrected estimate of horizontal component */
283  double Sin_B0; /* sin(B0), B0 is estimate of Bowring aux variable */
284  double Sin3_B0; /* cube of sin(B0) */
285  double Cos_B0; /* cos(B0) */
286  double Sin_p1; /* sin(phi1), phi1 is estimated latitude */
287  double Cos_p1; /* cos(phi1) */
288  double Rn; /* Earth radius at location */
289  double Sum; /* numerator of cos(phi1) */
290  int At_Pole; /* indicates location is in polar region */
291  double Geocent_b = semiMajorAxis * (1 - flattening); /* Semi-minor axis of ellipsoid, in meters */
292 
293  At_Pole = FALSE;
294  if (X != 0.0)
295  {
296  longitude = atan2(Y,X);
297  }
298  else
299  {
300  if (Y > 0)
301  {
302  longitude = PI_OVER_2;
303  }
304  else if (Y < 0)
305  {
306  longitude = -PI_OVER_2;
307  }
308  else
309  {
310  At_Pole = TRUE;
311  longitude = 0.0;
312  if (Z > 0.0)
313  { /* north pole */
314  latitude = PI_OVER_2;
315  }
316  else if (Z < 0.0)
317  { /* south pole */
318  latitude = -PI_OVER_2;
319  }
320  else
321  { /* center of earth */
322  latitude = PI_OVER_2;
323  height = -Geocent_b;
324  return new GeodeticCoordinates(
325  CoordinateType::geodetic, longitude, latitude, height );
326  }
327  }
328  }
329  W2 = X*X + Y*Y;
330  W = sqrt(W2);
331  T0 = Z * AD_C;
332  S0 = sqrt(T0 * T0 + W2);
333  Sin_B0 = T0 / S0;
334  Cos_B0 = W / S0;
335  Sin3_B0 = Sin_B0 * Sin_B0 * Sin_B0;
336  T1 = Z + Geocent_b * Geocent_ep2 * Sin3_B0;
337  Sum = W - semiMajorAxis * Geocent_e2 * Cos_B0 * Cos_B0 * Cos_B0;
338  S1 = sqrt(T1*T1 + Sum * Sum);
339  Sin_p1 = T1 / S1;
340  Cos_p1 = Sum / S1;
341  Rn = semiMajorAxis / sqrt(1.0 - Geocent_e2 * Sin_p1 * Sin_p1);
342  if (Cos_p1 >= COS_67P5)
343  {
344  height = W / Cos_p1 - Rn;
345  }
346  else if (Cos_p1 <= -COS_67P5)
347  {
348  height = W / -Cos_p1 - Rn;
349  }
350  else
351  {
352  height = Z / Sin_p1 + Rn * (Geocent_e2 - 1.0);
353  }
354  if (At_Pole == FALSE)
355  {
356  latitude = atan(Sin_p1 / Cos_p1);
357  }
358  double Sin_Lat = sin(latitude);
359  double Cos_Lat = cos(latitude);
360  double Sin2_Lat = Sin_Lat * Sin_Lat;
361  double Rnn = semiMajorAxis / (sqrt(1.0e0 - Geocent_e2 * Sin2_Lat));
362  double X1 = (Rnn + height) * Cos_Lat * cos(longitude);
363  double Y1 = (Rnn + height) * Cos_Lat * sin(longitude);
364  double Z1 = ((Rnn * (1 - Geocent_e2)) + height) * Sin_Lat;
365 
366  if( ( (X-X1) * (X-X1) + (Y-Y1) * (Y-Y1) + (Z-Z1) * (Z-Z1) ) > 1 )
367  {
368  throw CoordinateConversionException( "Legacy geocentric-to-geodetic is not accurate at this height." );
369  }
370  }
371 
372  return new GeodeticCoordinates(
373  CoordinateType::geodetic, longitude, latitude, height );
374 }
375 
376 void Geocentric::geocentricToGeodetic(
377  const double x,
378  const double y,
379  const double z,
380  double &lat,
381  double &lon,
382  double &ht )
383 {
384  double equatorial_radius = semiMajorAxis;
385  double eccentricity_squared = Geocent_e2;
386 
387  double rho, c, s, ct2, e1, e2a;
388 
389  e1 = 1.0 - eccentricity_squared;
390  e2a = eccentricity_squared * equatorial_radius;
391 
392  rho = sqrt(x * x + y * y);
393 
394  if (z == 0.0)
395  {
396  c = 1.0;
397  s = 0.0;
398  lat = 0.0;
399  }
400  else
401  {
402  double ct, new_ct, zabs;
403  double f, new_f, df_dct, e2;
404 
405  zabs = fabs(z);
406 
407  new_ct = rho / zabs;
408  new_f = DBL_MAX;
409 
410  do
411  {
412  ct = new_ct;
413  f = new_f;
414 
415  e2 = sqrt(e1 + ct*ct);
416 
417  new_f = rho - zabs*ct - e2a*ct/e2;
418 
419  if (new_f == 0.0) break;
420 
421  df_dct = -zabs - (e2a*e1)/(e2*e2*e2);
422 
423  new_ct = ct - new_f / df_dct;
424 
425  if (new_ct < 0.0) new_ct = 0.0;
426  }
427  while (fabs(new_f) < fabs(f));
428 
429  s = 1.0 / sqrt(1.0 + ct * ct);
430  c = ct * s;
431 
432  if (z < 0.0)
433  {
434  s = -s;
435  lat = -atan(1.0 / ct);
436  } else
437  {
438  lat = atan(1.0 / ct);
439  }
440  }
441 
442  lon = atan2(y, x);
443 
444  ht = rho*c + z*s - equatorial_radius*sqrt(1.0 - eccentricity_squared*s*s);
445 
446  return;
447 }
448 
449 
450 // CLASSIFICATION: UNCLASSIFIED
#define PI_OVER_2
Definition: MGRS.cpp:165
static const char * ellipsoidFlattening
Definition: ErrorMessages.h:47
#define TRUE
Definition: MGRS.cpp:162
Geocentric(double ellipsoidSemiMajorAxis, double ellipsoidFlattening)
Definition: Geocentric.cpp:121
static const char * longitude
Definition: ErrorMessages.h:76
MSP::CCS::GeodeticCoordinates * convertToGeodetic(MSP::CCS::CartesianCoordinates *cartesianCoordinates)
Definition: Geocentric.cpp:235
static const char * semiMajorAxis
Definition: ErrorMessages.h:46
static const char * latitude
Definition: ErrorMessages.h:75
#define PI
Definition: MGRS.cpp:164
MSP::CCS::CartesianCoordinates * convertFromGeodetic(const MSP::CCS::GeodeticCoordinates *geodeticCoordinates)
Definition: Geocentric.cpp:186
#define FALSE
Definition: MGRS.cpp:163
Geocentric & operator=(const Geocentric &g)
Definition: Geocentric.cpp:171
const double AD_C
Definition: Geocentric.cpp:113
const double COS_67P5
Definition: Geocentric.cpp:112