]> git.mxchange.org Git - simgear.git/blobdiff - simgear/math/sg_geodesy.cxx
Ignore generated binaries.
[simgear.git] / simgear / math / sg_geodesy.cxx
index 61fc8cffa2b57ffee0986e1de12a3101acb72489..4de126d5848c145ea3448574a3bbc491c63b1adf 100644 (file)
@@ -1,4 +1,10 @@
+
+#ifdef HAVE_CONFIG_H
+#  include <simgear_config.h>
+#endif
+
 #include <simgear/constants.h>
+#include "SGMath.hxx"
 #include "sg_geodesy.hxx"
 
 // Notes:
@@ -40,171 +46,6 @@ static const double STRETCH = 1.0033640898209764189003079;
 static const double POLRAD  = 6356752.3142451794975639668;
 #endif
 
-// Returns a "local" geodetic latitude: an approximation that will be
-// correct only at zero altitude.
-static double localLat(double r, double z)
-{
-    // Squash to a spherical earth, compute a tangent vector to the
-    // surface circle (in squashed space, the surface is a perfect
-    // sphere) by swapping the components and negating one, stretch to
-    // real coordinates, and take an inverse-tangent/perpedicular
-    // vector to get a local geodetic "up" vector.  (Those steps all
-    // cook down to just a few multiplies).  Then just turn it into an
-    // angle.
-    double upr = r * SQUASH;
-    double upz = z * STRETCH;
-    return atan2(upz, upr);
-}
-
-// This is the inverse of the algorithm in localLat().  It returns the
-// (cylindrical) coordinates of a surface latitude expressed as an
-// "up" unit vector.
-static void surfRZ(double upr, double upz, double* r, double* z)
-{
-    // We are
-    // converting a (2D, cylindrical) "up" vector defined by the
-    // geodetic latitude into unitless R and Z coordinates in
-    // cartesian space.
-    double R = upr * STRETCH;
-    double Z = upz * SQUASH;
-
-    // Now we need to turn R and Z into a surface point.  That is,
-    // pick a coefficient C for them such that the point is on the
-    // surface when converted to "squashed" space:
-    //  (C*R*SQUASH)^2 + (C*Z)^2 = POLRAD^2
-    //   C^2 = POLRAD^2 / ((R*SQUASH)^2 + Z^2)
-    double sr = R * SQUASH;
-    double c = POLRAD / sqrt(sr*sr + Z*Z);
-    R *= c;
-    Z *= c;
-
-    *r = R; *z = Z;
-}
-
-// Returns the insersection of the line joining the center of the
-// earth and the specified cylindrical point with the surface of the
-// WGS84 ellipsoid.  Works by finding a normalization constant (in
-// squashed space) that places the squashed point on the surface of
-// the sphere.
-static double seaLevelRadius(double r, double z)
-{
-    double sr = r * SQUASH;
-    double norm = POLRAD/sqrt(sr*sr + z*z);
-    r *= norm;
-    z *= norm;
-    return sqrt(r*r + z*z);
-}
-
-// Convert a cartexian XYZ coordinate to a geodetic lat/lon/alt.  This
-// is a "recursion relation".  In essence, it iterates over the 2D
-// part of sgGeodToCart refining its approximation at each step.  The
-// MAX_LAT_ERROR threshold is picked carefully to allow us to reach
-// the full precision of an IEEE double.  While this algorithm might
-// look slow, it's not.  It actually converges very fast indeed --
-// I've never seen it take more than six iterations under normal
-// conditions.  Three or four is more typical. (It gets slower as the
-// altitude/error gets larger; at 50000m altitude, it starts to need
-// seven loops.)  One caveat is that at *very* large altitudes, it
-// starts making very poor guesses as to latitude.  As altitude
-// approaches infinity, it should be guessing with geocentric
-// coordinates, not "local geodetic up" ones.
-void sgCartToGeod(double* xyz, double* lat, double* lon, double* alt)
-{
-    // The error is expressed as a radian angle, and we want accuracy
-    // to 1 part in 2^50 (an IEEE double has between 51 and 52
-    // significant bits of magnitude due to the "hidden" digit; leave
-    // at least one bit free for potential slop).  In real units, this
-    // works out to about 6 nanometers.
-    static const double MAX_LAT_ERROR = 8.881784197001252e-16;
-    double x = xyz[0], y = xyz[1], z = xyz[2];
-
-    // Longitude is trivial.  Convert to cylindrical "(r, z)"
-    // coordinates while we're at it.
-    *lon = atan2(y, x);
-    double r = sqrt(x*x + y*y);
-
-    double lat1, lat2 = localLat(r, z);
-    double r2, z2, dot;
-    do {
-        lat1 = lat2;
-
-        // Compute an "up" vector
-        double upr = cos(lat1);
-        double upz = sin(lat1);
-
-        // Find the surface point with that latitude
-        surfRZ(upr, upz, &r2, &z2);
-
-        // Convert r2z2 to the vector pointing from the surface to rz
-        r2 = r - r2;
-        z2 = z - z2;
-
-        // Dot it with "up" to get an approximate altitude
-        dot = r2*upr + z2*upz;
-
-        // And compute an approximate geodetic surface coordinate
-        // using that altitude, so now: R2Z2 = RZ - ((RZ - SURF) dot
-        // UP)
-        r2 = r - dot * upr;
-        z2 = z - dot * upz;
-
-        // Find the latitude of *that* point, and iterate
-        lat2 = localLat(r2, z2);
-    } while(fabs(lat2 - lat1) > MAX_LAT_ERROR);
-
-    // All done!  We have an accurate geodetic lattitude, now
-    // calculate the altitude as a cartesian distance between the
-    // final geodetic surface point and the initial r/z coordinate.
-    *lat = lat1;
-    double dr = r - r2;
-    double dz = z - z2;
-    double altsign = (dot > 0) ? 1 : -1;
-    *alt = altsign * sqrt(dr*dr + dz*dz);
-}
-
-void sgGeodToCart(double lat, double lon, double alt, double* xyz)
-{
-    // This is the inverse of the algorithm in localLat().  We are
-    // converting a (2D, cylindrical) "up" vector defined by the
-    // geodetic latitude into unitless R and Z coordinates in
-    // cartesian space.
-    double upr = cos(lat);
-    double upz = sin(lat);
-    double r, z;
-    surfRZ(upr, upz, &r, &z);
-
-    // Add the altitude using the "up" unit vector we calculated
-    // initially.
-    r += upr * alt;
-    z += upz * alt;
-
-    // Finally, convert from cylindrical to cartesian
-    xyz[0] = r * cos(lon);
-    xyz[1] = r * sin(lon);
-    xyz[2] = z;
-}
-
-void sgGeocToGeod(double lat_geoc, double radius,
-                  double *lat_geod, double *alt, double *sea_level_r)
-{
-    // Build a fake cartesian point, and run it through CartToGeod
-    double lon_dummy, xyz[3];
-    xyz[0] = cos(lat_geoc) * radius;
-    xyz[1] = 0;
-    xyz[2] = sin(lat_geoc) * radius;
-    sgCartToGeod(xyz, lat_geod, &lon_dummy, alt);
-    *sea_level_r = seaLevelRadius(xyz[0], xyz[2]);
-}
-
-void sgGeodToGeoc(double lat_geod, double alt,
-                  double *sl_radius, double *lat_geoc)
-{
-    double xyz[3];
-    sgGeodToCart(lat_geod, 0, alt, xyz);
-    *lat_geoc = atan2(xyz[2], xyz[0]);
-    *sl_radius = seaLevelRadius(xyz[0], xyz[2]);
-}
-
 ////////////////////////////////////////////////////////////////////////
 //
 // Direct and inverse distance functions