aboutsummaryrefslogtreecommitdiff
path: root/src/geocent.c
diff options
context:
space:
mode:
authorFrank Warmerdam <warmerdam@pobox.com>2004-05-03 16:28:01 +0000
committerFrank Warmerdam <warmerdam@pobox.com>2004-05-03 16:28:01 +0000
commita3fd6bde1d41c668bfc9a2e36e261c00b14f3b43 (patch)
tree8b76d247b9d501f0632e10051000b1c64dc8b142 /src/geocent.c
parent7a29405d1fc2b2aeeed6cd77427e1f193e2b6d8d (diff)
downloadPROJ-a3fd6bde1d41c668bfc9a2e36e261c00b14f3b43.tar.gz
PROJ-a3fd6bde1d41c668bfc9a2e36e261c00b14f3b43.zip
Apply iterative solution to geocentric_to_geodetic as suggestion from
Lothar Gorling. http://bugzilla.remotesensing.org/show_bug.cgi?id=563 git-svn-id: http://svn.osgeo.org/metacrs/proj/trunk@1182 4e78687f-474d-0410-85f9-8d5e500ac6b2
Diffstat (limited to 'src/geocent.c')
-rw-r--r--src/geocent.c290
1 files changed, 210 insertions, 80 deletions
diff --git a/src/geocent.c b/src/geocent.c
index fbe6d75a..5d6a104a 100644
--- a/src/geocent.c
+++ b/src/geocent.c
@@ -65,6 +65,11 @@
* 25-02-97 Original Code
*
* $Log$
+ * Revision 1.4 2004/05/03 16:28:01 warmerda
+ * Apply iterative solution to geocentric_to_geodetic as suggestion from
+ * Lothar Gorling.
+ * http://bugzilla.remotesensing.org/show_bug.cgi?id=563
+ *
* Revision 1.3 2002/01/08 15:04:08 warmerda
* The latitude clamping fix from September in Convert_Geodetic_To_Geocentric
* was botched. Fixed up now.
@@ -223,13 +228,6 @@ long Convert_Geodetic_To_Geocentric (double Latitude,
return (Error_Code);
} /* END OF Convert_Geodetic_To_Geocentric */
-void Convert_Geocentric_To_Geodetic (double X,
- double Y,
- double Z,
- double *Latitude,
- double *Longitude,
- double *Height)
-{ /* BEGIN Convert_Geocentric_To_Geodetic */
/*
* The function Convert_Geocentric_To_Geodetic converts geocentric
* coordinates (X, Y, Z) to geodetic coordinates (latitude, longitude,
@@ -241,90 +239,222 @@ void Convert_Geocentric_To_Geodetic (double X,
* Latitude : Calculated latitude value in radians. (output)
* Longitude : Calculated longitude value in radians. (output)
* Height : Calculated height value, in meters. (output)
- *
+ */
+
+#define USE_ITERATIVE_METHOD
+
+void Convert_Geocentric_To_Geodetic (double X,
+ double Y,
+ double Z,
+ double *Latitude,
+ double *Longitude,
+ double *Height)
+{ /* BEGIN Convert_Geocentric_To_Geodetic */
+#if !defined(USE_ITERATIVE_METHOD)
+/*
* The method used here is derived from 'An Improved Algorithm for
* Geocentric to Geodetic Coordinate Conversion', by Ralph Toms, Feb 1996
*/
/* Note: Variable names follow the notation used in Toms, Feb 1996 */
- double W; /* distance from Z axis */
- double W2; /* square of distance from Z axis */
- double T0; /* initial estimate of vertical component */
- double T1; /* corrected estimate of vertical component */
- double S0; /* initial estimate of horizontal component */
- double S1; /* corrected estimate of horizontal component */
- double Sin_B0; /* sin(B0), B0 is estimate of Bowring aux variable */
- double Sin3_B0; /* cube of sin(B0) */
- double Cos_B0; /* cos(B0) */
- double Sin_p1; /* sin(phi1), phi1 is estimated latitude */
- double Cos_p1; /* cos(phi1) */
- double Rn; /* Earth radius at location */
- double Sum; /* numerator of cos(phi1) */
- int At_Pole; /* indicates location is in polar region */
-
- At_Pole = FALSE;
- if (X != 0.0)
- {
- *Longitude = atan2(Y,X);
- }
- else
- {
- if (Y > 0)
+ double W; /* distance from Z axis */
+ double W2; /* square of distance from Z axis */
+ double T0; /* initial estimate of vertical component */
+ double T1; /* corrected estimate of vertical component */
+ double S0; /* initial estimate of horizontal component */
+ double S1; /* corrected estimate of horizontal component */
+ double Sin_B0; /* sin(B0), B0 is estimate of Bowring aux variable */
+ double Sin3_B0; /* cube of sin(B0) */
+ double Cos_B0; /* cos(B0) */
+ double Sin_p1; /* sin(phi1), phi1 is estimated latitude */
+ double Cos_p1; /* cos(phi1) */
+ double Rn; /* Earth radius at location */
+ double Sum; /* numerator of cos(phi1) */
+ int At_Pole; /* indicates location is in polar region */
+
+ At_Pole = FALSE;
+ if (X != 0.0)
+ {
+ *Longitude = atan2(Y,X);
+ }
+ else
{
- *Longitude = PI_OVER_2;
+ if (Y > 0)
+ {
+ *Longitude = PI_OVER_2;
+ }
+ else if (Y < 0)
+ {
+ *Longitude = -PI_OVER_2;
+ }
+ else
+ {
+ At_Pole = TRUE;
+ *Longitude = 0.0;
+ if (Z > 0.0)
+ { /* north pole */
+ *Latitude = PI_OVER_2;
+ }
+ else if (Z < 0.0)
+ { /* south pole */
+ *Latitude = -PI_OVER_2;
+ }
+ else
+ { /* center of earth */
+ *Latitude = PI_OVER_2;
+ *Height = -Geocent_b;
+ return;
+ }
+ }
}
- else if (Y < 0)
+ W2 = X*X + Y*Y;
+ W = sqrt(W2);
+ T0 = Z * AD_C;
+ S0 = sqrt(T0 * T0 + W2);
+ Sin_B0 = T0 / S0;
+ Cos_B0 = W / S0;
+ Sin3_B0 = Sin_B0 * Sin_B0 * Sin_B0;
+ T1 = Z + Geocent_b * Geocent_ep2 * Sin3_B0;
+ Sum = W - Geocent_a * Geocent_e2 * Cos_B0 * Cos_B0 * Cos_B0;
+ S1 = sqrt(T1*T1 + Sum * Sum);
+ Sin_p1 = T1 / S1;
+ Cos_p1 = Sum / S1;
+ Rn = Geocent_a / sqrt(1.0 - Geocent_e2 * Sin_p1 * Sin_p1);
+ if (Cos_p1 >= COS_67P5)
{
- *Longitude = -PI_OVER_2;
+ *Height = W / Cos_p1 - Rn;
+ }
+ else if (Cos_p1 <= -COS_67P5)
+ {
+ *Height = W / -Cos_p1 - Rn;
}
else
{
- At_Pole = TRUE;
- *Longitude = 0.0;
- if (Z > 0.0)
- { /* north pole */
- *Latitude = PI_OVER_2;
- }
- else if (Z < 0.0)
- { /* south pole */
- *Latitude = -PI_OVER_2;
- }
- else
- { /* center of earth */
- *Latitude = PI_OVER_2;
- *Height = -Geocent_b;
- return;
- }
+ *Height = Z / Sin_p1 + Rn * (Geocent_e2 - 1.0);
}
- }
- W2 = X*X + Y*Y;
- W = sqrt(W2);
- T0 = Z * AD_C;
- S0 = sqrt(T0 * T0 + W2);
- Sin_B0 = T0 / S0;
- Cos_B0 = W / S0;
- Sin3_B0 = Sin_B0 * Sin_B0 * Sin_B0;
- T1 = Z + Geocent_b * Geocent_ep2 * Sin3_B0;
- Sum = W - Geocent_a * Geocent_e2 * Cos_B0 * Cos_B0 * Cos_B0;
- S1 = sqrt(T1*T1 + Sum * Sum);
- Sin_p1 = T1 / S1;
- Cos_p1 = Sum / S1;
- Rn = Geocent_a / sqrt(1.0 - Geocent_e2 * Sin_p1 * Sin_p1);
- if (Cos_p1 >= COS_67P5)
- {
- *Height = W / Cos_p1 - Rn;
- }
- else if (Cos_p1 <= -COS_67P5)
- {
- *Height = W / -Cos_p1 - Rn;
- }
- else
- {
- *Height = Z / Sin_p1 + Rn * (Geocent_e2 - 1.0);
- }
- if (At_Pole == FALSE)
- {
- *Latitude = atan(Sin_p1 / Cos_p1);
- }
+ if (At_Pole == FALSE)
+ {
+ *Latitude = atan(Sin_p1 / Cos_p1);
+ }
+#else /* defined(USE_ITERATIVE_METHOD) */
+/*
+* Reference...
+* ============
+* Wenzel, H.-G.(1985): Hochauflösende Kugelfunktionsmodelle für
+* das Gravitationspotential der Erde. Wiss. Arb. Univ. Hannover
+* Nr. 137, p. 130-131.
+
+* Programmed by GGA- Leibniz-Institue of Applied Geophysics
+* Stilleweg 2
+* D-30655 Hannover
+* Federal Republic of Germany
+* Internet: www.gga-hannover.de
+*
+* Hannover, March 1999, April 2004.
+* see also: comments in statements
+* remarks:
+* Mathematically exact and because of symmetry of rotation-ellipsoid,
+* each point (X,Y,Z) has at least two solutions (Latitude1,Longitude1,Height1) and
+* (Latitude2,Longitude2,Height2). Is point=(0.,0.,Z) (P=0.), so you get even
+* four solutions, every two symmetrical to the semi-minor axis.
+* Here Height1 and Height2 have at least a difference in order of
+* radius of curvature (e.g. (0,0,b)=> (90.,0.,0.) or (-90.,0.,-2b);
+* (a+100.)*(sqrt(2.)/2.,sqrt(2.)/2.,0.) => (0.,45.,100.) or
+* (0.,225.,-(2a+100.))).
+* The algorithm always computes (Latitude,Longitude) with smallest |Height|.
+* For normal computations, that means |Height|<10000.m, algorithm normally
+* converges after to 2-3 steps!!!
+* But if |Height| has the amount of length of ellipsoid's axis
+* (e.g. -6300000.m), algorithm needs about 15 steps.
+*/
+
+/* local defintions and variables */
+/* end-criterium of loop, accuracy of sin(Latitude) */
+#define genau 1.E-12
+#define genau2 (genau*genau)
+#define maxiter 30
+
+ double P; /* distance between semi-minor axis and location */
+ double RR; /* distance between center and location */
+ double CT; /* sin of geocentric latitude */
+ double ST; /* cos of geocentric latitude */
+ double RX;
+ double RK;
+ double RN; /* Earth radius at location */
+ double CPHI0; /* cos of start or old geodetic latitude in iterations */
+ double SPHI0; /* sin of start or old geodetic latitude in iterations */
+ double CPHI; /* cos of searched geodetic latitude */
+ double SPHI; /* sin of searched geodetic latitude */
+ double SDPHI; /* end-criterium: addition-theorem of sin(Latitude(iter)-Latitude(iter-1)) */
+ int At_Pole; /* indicates location is in polar region */
+ int iter; /* # of continous iteration, max. 30 is always enough (s.a.) */
+
+ At_Pole = FALSE;
+ P = sqrt(X*X+Y*Y);
+ RR = sqrt(X*X+Y*Y+Z*Z);
+
+/* special cases for latitude and longitude */
+ if (P/Geocent_a < genau) {
+
+/* special case, if P=0. (X=0., Y=0.) */
+ At_Pole = TRUE;
+ *Longitude = 0.;
+
+/* if (X,Y,Z)=(0.,0.,0.) then Height becomes semi-minor axis
+ * of ellipsoid (=center of mass), Latitude becomes PI/2 */
+ if (RR/Geocent_a < genau) {
+ *Latitude = PI_OVER_2;
+ *Height = -Geocent_b;
+ return ;
+
+ }
+ }
+ else {
+/* ellipsoidal (geodetic) longitude
+ * interval: -PI < Longitude <= +PI */
+ *Longitude=atan2(Y,X);
+ }
+
+/* --------------------------------------------------------------
+ * Following iterative algorithm was developped by
+ * "Institut für Erdmessung", University of Hannover, July 1988.
+ * Internet: www.ife.uni-hannover.de
+ * Iterative computation of CPHI,SPHI and Height.
+ * Iteration of CPHI and SPHI to 10**-12 radian resp.
+ * 2*10**-7 arcsec.
+ * --------------------------------------------------------------
+ */
+ CT = Z/RR;
+ ST = P/RR;
+ RX = 1.0/sqrt(1.0-Geocent_e2*(2.0-Geocent_e2)*ST*ST);
+ CPHI0 = ST*(1.0-Geocent_e2)*RX;
+ SPHI0 = CT*RX;
+ iter = 0;
+
+/* loop to find sin(Latitude) resp. Latitude
+ * until |sin(Latitude(iter)-Latitude(iter-1))| < genau */
+ do
+ {
+ iter++;
+ RN = Geocent_a/sqrt(1.0-Geocent_e2*SPHI0*SPHI0);
+
+/* ellipsoidal (geodetic) height */
+ *Height = P*CPHI0+Z*SPHI0-RN*(1.0-Geocent_e2*SPHI0*SPHI0);
+
+ RK = Geocent_e2*RN/(RN+*Height);
+ RX = 1.0/sqrt(1.0-RK*(2.0-RK)*ST*ST);
+ CPHI = ST*(1.0-RK)*RX;
+ SPHI = CT*RX;
+ SDPHI = SPHI*CPHI0-CPHI*SPHI0;
+ CPHI0 = CPHI;
+ SPHI0 = SPHI;
+ }
+ while (SDPHI*SDPHI > genau2 && iter < maxiter);
+
+/* ellipsoidal (geodetic) latitude */
+ *Latitude=atan(SPHI/fabs(CPHI));
+
+ return;
+#endif /* defined(USE_ITERATIVE_METHOD) */
} /* END OF Convert_Geocentric_To_Geodetic */