core/vpgl/vpgl_datum_conversion.cxx
Go to the documentation of this file.
00001 #include "vpgl_datum_conversion.h"
00002 //******************************************************************************
00003 //:
00004 // \file
00005 //
00006 //  This file is a subset of routines that will be used
00007 //  to create and list the datum coordinate shift formulas. Included are
00008 //  the following  examples of datum shifts, demonstrating the functionality
00009 //  of these routines.
00010 //
00011 //  These datum shifts have the following interesting property:
00012 //
00013 //      TO GET:
00014 //         WGS84 from NAD27 ->  compute d_lat, d_lon, d_H using NAD27 lat/lon,
00015 //      THEN
00016 //         lat(WGS84) = lat(NAD27)+d_lat
00017 //         lon(WGS84) = lon(NAD27)+d_lon
00018 //         elev(WGS84) = elev(NAD27)+d_H
00019 //
00020 //      TO GET:
00021 //         NAD27 from WGS84 -> compute d_lat, d_lon, d_H using WGS84 lat/lon,
00022 //      THEN
00023 //         lat(NAD27) = lat(WGS84)-d_lat
00024 //         lon(NAD27) = lon(WGS84)-d_lon <- note "-" signs
00025 //         elev(NAD27) = elev(WGS84)-d_H
00026 //
00027 #include <vcl_cmath.h>
00028 #include <vnl/vnl_math.h>
00029 
00030 #define degree_to_rad   (vnl_math::pi_over_180)      // Degree to rad conv.
00031 #define dcos(x)         vcl_cos((x)*vnl_math::pi_over_180)
00032 #define dsin(x)         vcl_sin((x)*vnl_math::pi_over_180)
00033 #define EPSILON         1.0e-12
00034 
00035 
00036 double ipow(double x, int i)
00037 {
00038   double y = 1.0;
00039   if (i<0) { i *= -1; x = y/x; }
00040   while (i) { if (i%2) y*=x; i/=2; x*=x; }
00041   return y;
00042 }
00043 
00044 
00045 //*************************************************************************
00046 //      FUNCTION: wgs72_to_wgs84_deltas()
00047 //      Computes deltas that are either added or
00048 //      subtracted instead of added to the input lat, lon, elev.
00049 //*************************************************************************
00050 
00051 void wgs72_to_wgs84_deltas
00052   (double phi,                 //!< input lat (degrees)
00053    double *delta_phi,          //!< lat shift (degrees)
00054    double *delta_lamda,        //!< lon shift (degrees)
00055    double *delta_hgt)          //!< elev shift (meters)
00056 {
00057   // Information found from "Supplement to Department of Defense
00058   // world geodetic systems 1984 Technical Report".  (pg 25-8)
00059 
00060   double delta_f, a, delta_a, delta_r;          // constants
00061 
00062   //****************************** Instructions: ******************************
00063   //
00064   // To obtain WGS 84 Coordinates:
00065   // Add the delta_phi, delta_lamda, and delta_height changes calculated
00066   // using WGS 72 Coordinates to the WGS 72 Coordinates (phi, lamda, height)
00067   // given.
00068   //
00069   // NOTE:Latitude is positive North, and Longitude is positive East (0 to 360)
00070   //***************************************************************************
00071 
00072  // Constant definition
00073   delta_f = .3121057e-7;
00074   a       = 6378135;          // meters
00075   delta_a = 2.000;            // meters
00076   delta_r = 1.400;            // meters
00077 
00078   // First compute delta_phi and delta_lamda in arc seconds
00079 
00080   *delta_phi = (4.5 * dcos(phi))/(a * dsin(1/3600.0)) +
00081                (delta_f * dsin(2.0 * phi)) / (dsin(1/3600.0));
00082 
00083   *delta_lamda = .554;
00084 
00085   *delta_hgt = 4.5 * dsin(phi) + a * delta_f * (dsin(phi) * dsin(phi))
00086     - delta_a + delta_r;
00087 
00088   // Convert to decimal degrees
00089   *delta_phi   /= 3600.0;
00090   *delta_lamda /= 3600.0;
00091 }
00092 
00093 //*************************************************************************
00094 //      FUNCTION: wgs72_to_wgs84()
00095 //*************************************************************************
00096 
00097 void wgs72_to_wgs84
00098   (
00099    double phi,                 //!< input lat, lon, elev coord (degrees)
00100    double lamda,
00101    double height,
00102    double *wgs84_phi,          //!< lat shift (degrees)
00103    double *wgs84_lamda,        //!< lon shift (degrees)
00104    double *wgs84_hgt)          //!< elev shift (meters)
00105 {
00106   double delta_phi, delta_lamda, delta_hgt;
00107 
00108   wgs72_to_wgs84_deltas(phi, &delta_phi, &delta_lamda, &delta_hgt);
00109 
00110   *wgs84_phi   = phi + delta_phi;
00111   *wgs84_lamda = lamda + delta_lamda;
00112   *wgs84_hgt   = height + delta_hgt;
00113 }
00114 
00115 //*************************************************************************
00116 //      FUNCTION: wgs84_to_wgs72()
00117 //*************************************************************************
00118 
00119 void wgs84_to_wgs72
00120   (
00121    double phi,                 //!< input lat, lon, elev coord (degrees)
00122    double lamda,
00123    double height,
00124    double *wgs72_phi,          //!< lat shift (degrees)
00125    double *wgs72_lamda,        //!< lon shift (degrees)
00126    double *wgs72_hgt)          //!< elev shift (meters)
00127 {
00128   double delta_phi, delta_lamda, delta_hgt;
00129 
00130   wgs72_to_wgs84_deltas(phi, &delta_phi, &delta_lamda, &delta_hgt);
00131 
00132   *wgs72_phi   = phi - delta_phi;
00133   *wgs72_lamda = lamda - delta_lamda;
00134   *wgs72_hgt   = height - delta_hgt;
00135 }
00136 
00137 //*************************************************************************
00138 //      FUNCTION: nad27m_to_wgs84_deltas()
00139 //      Area:     Mexico/Central America
00140 //*************************************************************************
00141 
00142 void nad27m_to_wgs84_deltas
00143   (double phi,                 //!< input lat (degrees)
00144    double lamda,               //!< input lon (degrees)
00145    double /* height */,        //!< input elv (meters)
00146    double *delta_phi,          //!< lat shift (arc sec)
00147    double *delta_lamda,        //!< lon shift (arc sec)
00148    double *delta_hgt)          //!< elev shift (meters)
00149 {
00150   // Information found from "Supplement to Department of Defense
00151   // world geodetic systems 1984 Technical Report.  (pg 20-36)
00152 
00153 
00154   double U,V, K;
00155 
00156   K = .05235988;
00157 
00158   // Convert the longitude from -180 -> +180  to 0 -> 360
00159   if (lamda < 0)
00160     lamda += 360.0;
00161 
00162   // deta_phi and delta_lamda are in arc seconds
00163 
00164   U = K * (phi - 20);
00165   V = K * (lamda -290);
00166 
00167   *delta_phi = .67775 + 10.02259*U - 18.72391*V + 12.66341*ipow(U,2) - 32.47686*ipow(V,2) - 52.70768*ipow(U,3) - 13.86527*U*ipow(V,2)
00168     - 16.82734*ipow(V,3) - 59.54646*V*ipow(U,3) + 120.64887*ipow(U,5) + 7.23362*ipow(U,2)*ipow(V,3) + 95.89131*ipow(U,5)*V -2.89651*U*ipow(V,5)
00169     - 1.23778*ipow(V,6) + 70.19213*ipow(U,8) + .84596*ipow(U,2)*ipow(V,6) + .14848*ipow(V,8) - 3.83786*ipow(U,3)*ipow(V,6) + .10405*U*ipow(V,9)
00170       + 30.09795*ipow(U,8)*ipow(V,3) + 12.93585*ipow(U,6)*ipow(V,6) - .58747*ipow(U,3)*ipow(V,9) + .7485*ipow(U,4)*ipow(V,9) +
00171         6.32462*ipow(U,8)*ipow(V,7) - .12736*ipow(U,6)*ipow(V,9) - .59993*ipow(U,9)*ipow(V,9);
00172 
00173   *delta_lamda = -.33643 - 3.11777*V -5.16881*ipow(U,2) - 3.17318*ipow(V,2) - 34.59331*ipow(U,3) + .97215*U*ipow(V,2) - .5818*ipow(V,3) +
00174     223.78114*ipow(U,5) + 358.07266*ipow(U,6) + 270.68064*ipow(U,7) - 87.99549*ipow(U,6)*V - .09789*U*ipow(V,6) + 636.41982*ipow(U,7)*V -
00175       729.91514*ipow(U,6)*ipow(V,2) - .60122*ipow(U,3)*ipow(V,5) - 375.38863*ipow(U,6)*ipow(V,3) - 6.97538*ipow(U,4)*ipow(V,5) -
00176         .00138*ipow(V,9) - 363.45977*ipow(U,9)*V + 50.19955*ipow(U,8)*ipow(V,2) - 12.01575*ipow(U,5)*ipow(V,6) - .4314*ipow(U,6)*ipow(V,9) +
00177           .43907*ipow(U,9)*ipow(V,9);
00178 
00179   *delta_hgt = -20.013 + 12.815*U - 8.084*V +74.122*ipow(U,2)*V +39.523*ipow(U,2)*ipow(V,2) - 23.158*ipow(U,4)*V + 194.444*ipow(U,6) +
00180     36.797*ipow(U,5)*V + .181*U*ipow(V,5) - 292.155*ipow(U,8) + 3.749*ipow(U,5)*ipow(V,5) +2.537*ipow(U,8)*ipow(V,6);
00181 
00182   // Convert to decimal degrees
00183   *delta_phi   /= 3600.0;
00184   *delta_lamda /= 3600.0;
00185 }
00186 
00187 
00188 //*************************************************************************
00189 //      FUNCTION: nad27m_to_wgs84()
00190 //*************************************************************************
00191 
00192 void nad27m_to_wgs84
00193   (double phi,                 //!< input lat, lon, elev coord (degrees)
00194    double lamda,
00195    double height,
00196    double *wgs84_phi,          //!< lat shift (degrees)
00197    double *wgs84_lamda,        //!< lon shift (degrees)
00198    double *wgs84_hgt)          //!< elev shift (meters)
00199 {
00200   double delta_phi, delta_lamda, delta_hgt;
00201 
00202   nad27m_to_wgs84_deltas(phi, lamda, height,
00203                          &delta_phi, &delta_lamda, &delta_hgt);
00204 
00205   *wgs84_phi   = phi + delta_phi;
00206   *wgs84_lamda = lamda + delta_lamda;
00207   *wgs84_hgt   = height + delta_hgt;
00208 }
00209 
00210 
00211 //*************************************************************************
00212 //      FUNCTION: wgs84_to_nad27m()
00213 //*************************************************************************
00214 
00215 void wgs84_to_nad27m
00216   (double phi,                 //!< input lat, lon, elev coord (degrees)
00217    double lamda,
00218    double height,
00219    double *nad27m_phi,         //!< lat shift (degrees)
00220    double *nad27m_lamda,       //!< lon shift (degrees)
00221    double *nad27m_hgt)         //!< elev shift (meters)
00222 {
00223   double delta_phi, delta_lamda, delta_hgt;
00224 
00225   nad27m_to_wgs84_deltas(phi, lamda, height,
00226                          &delta_phi, &delta_lamda, &delta_hgt);
00227 
00228   *nad27m_phi   = phi - delta_phi;
00229   *nad27m_lamda = lamda - delta_lamda;
00230   *nad27m_hgt   = height - delta_hgt;
00231 }
00232 
00233 
00234 //*************************************************************************
00235 //      FUNCTION: nad27n_to_wgs84_deltas()
00236 //*************************************************************************
00237 
00238 void nad27n_to_wgs84_deltas
00239   (double phi,                 //!< input lat (degrees)
00240    double lamda,               //!< input lon (degrees)
00241    double /* height */,        //!< input elv (meters)
00242    double *delta_phi,          //!< lat shift (arc sec)
00243    double *delta_lamda,        //!< lon shift (arc sec)
00244    double *delta_hgt)          //!< elev shift (meters)
00245 {
00246   // Information found from "Supplement to Department of Defense
00247   // world geodetic systems 1984 Technical Report.  (pg 19-2)
00248 
00249   double U,V, K;
00250 
00251   K = .05235988;
00252 
00253   // Convert the longitude from -180 -> +180  to 0 -> 360
00254   if (lamda < 0)
00255     lamda += 360.0;
00256 
00257   U = K * (phi - 37.0);
00258   V = K * (lamda -265.0);
00259 
00260   // deta_phi and delta_lamda are in arc seconds
00261 
00262   *delta_phi = .16984 - .76173*U + .09585*V +1.09919*ipow(U,2) - 4.57801*ipow(U,3) - 1.13239*ipow(U,2)*V + .49831*ipow(V,3)
00263     - .98399*ipow(U,3)*V + .12415*U*ipow(V,3) + .1145*ipow(V,4) +27.05396*ipow(U,5) + 2.03449*ipow(U,4)*V +.73357*ipow(U,2)*ipow(V,3)
00264       - .37548*ipow(V,5) - .14197*ipow(V,6) -59.96555*ipow(U,7) + .07439*ipow(V,7) -4.76082*ipow(U,8) + .03385*ipow(V,8) +
00265         49.0432*ipow(U,9) - 1.30575*ipow(U,6)*ipow(V,3) - .07653*ipow(U,3)*ipow(V,9) + .08646*ipow(U,4)*ipow(V,9);
00266 
00267   *delta_lamda = -.88437 + 2.05061*V + .26361*ipow(U,2) - .76804*U*V + .13374*ipow(V,2) - 1.31974*ipow(U,3) - .52162*ipow(U,2)*V -
00268     1.05853*U*ipow(V,2) -.49211*ipow(U,2)*ipow(V,2) + 2.17204*U*ipow(V,3) -.06004*ipow(V,4) +.30139*ipow(U,4)*V + 1.88585*U*ipow(V,4)
00269       - .81162*U*ipow(V,5) -.05183*ipow(V,6) -.96723*U*ipow(V,6) -.12948*ipow(U,3)*ipow(V,5) + 3.41827*ipow(U,9) -.44507*ipow(U,8)*V
00270         +.18882*U*ipow(V,8) -.01444*ipow(V,9) +.04794*U*ipow(V,9) -.59013*ipow(U,9)*ipow(V,3);
00271 
00272   *delta_hgt = -36.526 +3.9*U -4.723*V -21.553*ipow(U,2) +7.294*U*V +8.886*ipow(V,2) -8.44*ipow(U,2)*V -2.93*U*ipow(V,2) +
00273     56.937*ipow(U,4) -58.756*ipow(U,3)*V -4.061*ipow(V,4) +4.447*ipow(U,4)*V +4.903*ipow(U,2)*ipow(V,3) -55.873*ipow(U,6)
00274       +212.005*ipow(U,5)*V + 3.081*ipow(V,6) -254.511*ipow(U,7)*V -.756*ipow(V,8) +30.654*ipow(U,8)*V -.122*U*ipow(V,9);
00275 
00276   // Convert to decimal degrees
00277   *delta_phi   /= 3600.0;
00278   *delta_lamda /= 3600.0;
00279 }
00280 
00281 
00282 //*************************************************************************
00283 //      FUNCTION: nad27n_to_wgs84()
00284 //*************************************************************************
00285 
00286 void nad27n_to_wgs84
00287   (double phi,                 //!< input lat, lon, elev coord (degrees)
00288    double lamda,
00289    double height,
00290    double *wgs84_phi,          //!< lat shift (degrees)
00291    double *wgs84_lamda,        //!< lon shift (degrees)
00292    double *wgs84_hgt)          //!< elev shift (meters)
00293 {
00294   double delta_phi, delta_lamda, delta_hgt;
00295 
00296   nad27n_to_wgs84_deltas(phi, lamda, height,
00297                          &delta_phi, &delta_lamda, &delta_hgt);
00298 
00299   *wgs84_phi   = phi + delta_phi;
00300   *wgs84_lamda = lamda + delta_lamda;
00301   *wgs84_hgt   = height + delta_hgt;
00302 }
00303 
00304 //*************************************************************************
00305 //      FUNCTION: wgs84_to_nad27n()
00306 //*************************************************************************
00307 
00308 void wgs84_to_nad27n
00309   (double phi,           //!< input lat, lon, elev coord (degrees)
00310    double lamda,
00311    double height,
00312    double *nad27n_phi,   //!< lat shift (degrees)
00313    double *nad27n_lamda, //!< lon shift (degrees)
00314    double *nad27n_hgt)   //!< elev shift (meters)
00315 {
00316   double delta_phi, delta_lamda, delta_hgt;
00317 
00318   nad27n_to_wgs84_deltas(phi, lamda, height,
00319                          &delta_phi, &delta_lamda, &delta_hgt);
00320 
00321   *nad27n_phi   = phi - delta_phi;
00322   *nad27n_lamda = lamda - delta_lamda;
00323   *nad27n_hgt   = height - delta_hgt;
00324 }
00325 
00326 //*************************************************************************
00327 //            ROUTINE: nad27_wgs84_alternate
00328 //
00329 //            REVISION:  October 8, 1990  -    Original
00330 //            REVISION:  May 15,    1992  -    Rajiv Gupta
00331 //
00332 //            This program is a test program designed to convert a
00333 //            latitude/longitude coordinate in NAD27 to the WGS84
00334 //            coordinate system.
00335 //*************************************************************************
00336 
00337 
00338 //*************************************************************************
00339 //       FUNCTION: nad27n_to_wgs84_alternate()
00340 //       AUTHOR:   Mark Young
00341 //*************************************************************************
00342 
00343 void nad27n_to_wgs84_alternate
00344   (double nad27_lat, double nad27_lon, double nad27_el,
00345    double *wgs84_lat, double *wgs84_lon, double *wgs84_el)
00346 {
00347   double u,v,k;
00348 
00349   double a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14;
00350   double a15,a16,a17,a18,a19,a20,a21,a22,a23;
00351 
00352   double b1,b2,b3,b4,b5,b6,b7,b8,b9,b10,b11,b12,b13,b14;
00353   double b15,b16,b17,b18,b19,b20,b21,b22,b23;
00354 
00355   double c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14;
00356   double c15,c16,c17,c18,c19,c20;
00357 
00358   double delta_lat;
00359   double delta_lon;
00360   double delta_H;
00361 #ifdef DEBUG_DATUM
00362   double N_height;
00363 #endif
00364 
00365   double delta_lat_p;
00366   double delta_lon_p;
00367 
00368 #ifdef DEBUG_DATUM
00369   double prin_lat_rad;
00370   double ft_lat;
00371   double ft_lon;
00372   double c_error;
00373 #endif
00374 
00375 #if 0
00376   int    lon_int;
00377   double lon_int_fl;
00378   double lon_frac;
00379 #endif
00380 
00381 #ifdef DEBUG_DATUM
00382   double new_lat;
00383   double new_lon;
00384 #endif
00385 
00386   double prin_lat; // Est. lat of principal point
00387   double prin_lon; // Est. lon of principal point
00388 
00389 #ifdef DEBUG_DATUM
00390   //   Display the program title.
00391 
00392   vcl_cout << "\n  NAD27 to WGS84 Conversion routine\n\n"
00393            << "       Enter latitude and longitude : ";
00394   vcl_cin >> nad27_lat >> nad27_lon;
00395 
00396 #endif
00397 
00398   prin_lat = (double) nad27_lat;
00399   prin_lon = (double) nad27_lon;
00400 
00401 #ifdef DEBUG_DATUM
00402   prin_lat_rad = prin_lat * degree_to_rad;
00403 #endif
00404 
00405 //lon_int = (int) prin_lon;
00406 //lon_int_fl = (double)lon_int;
00407 //lon_frac = vcl_fabs(prin_lon - lon_int);
00408 
00409   if (prin_lon < 0)
00410     prin_lon = 360.0 + prin_lon;
00411 //  prin_lon = 360.0 + lon_int_fl + lon_frac;
00412 
00413   k = .05235988;
00414   u = k*(prin_lat - 37);
00415   v = k*(prin_lon - 265);
00416 
00417   // Initialize latitude constants
00418 
00419   a1 = 0.16984;
00420   a2 = -0.76173;
00421   a3 = 0.09585;
00422   a4 = 1.09919;
00423   a5 = -4.57801;
00424   a6 = -1.13239;
00425   a7 = 0.49831;
00426   a8 = -.98399;
00427   a9 = 0.12415;
00428   a10 = 0.11450;
00429   a11 = 27.05396;
00430   a12 = 2.03449;
00431   a13 = 0.73357;
00432   a14 = -0.37548;
00433   a15 = -0.14197;
00434   a16 = -59.96555;
00435   a17 = 0.07439;
00436   a18 = -4.76082;
00437   a19 = 0.03385;
00438   a20 = 49.04320;
00439   a21 = -1.30575;
00440   a22 = -0.07653;
00441   a23 = 0.08646;
00442 
00443   delta_lat = a1 + a2*u + a3*v + a4*u*u + a5*u*u*u + a6*u*u*v
00444     + a7*v*v*v + a8*u*u*u*v + a9*u*v*v*v + a10*v*v*v*v
00445       + a11*u*u*u*u*u + a12*u*u*u*u*v + a13*u*u*v*v*v
00446         + a14*v*v*v*v*v + a15*v*v*v*v*v*v + a16*u*u*u*u*u*u*u
00447           + a17*v*v*v*v*v*v*v + a18*u*u*u*u*u*u*u*u
00448             + a19*v*v*v*v*v*v*v*v + a20*u*u*u*u*u*u*u*u*u
00449               + a21*u*u*u*u*u*u*v*v*v + a22*u*u*u*v*v*v*v*v*v*v*v*v
00450                 + a23*u*u*u*u*v*v*v*v*v*v*v*v*v;
00451 
00452   // Initialize longitude constants
00453 
00454   b1 = -0.88437;
00455   b2 = 2.05061;
00456   b3 = 0.26361;
00457   b4 = -0.76804;
00458   b5 = 0.13374;
00459   b6 = -1.31974;
00460   b7 = -0.52162;
00461   b8 = -1.05853;
00462   b9 = -0.49211;
00463   b10 = 2.17204;
00464   b11 = -0.06004;
00465   b12 = 0.30139;
00466   b13 = 1.88585;
00467   b14 = -0.81162;
00468   b15 = -0.05183;
00469   b16 = -0.96723;
00470   b17 = -.12948;
00471   b18 = 3.41827;
00472   b19 = -0.44507;
00473   b20 = 0.18882;
00474   b21 = -0.01444;
00475   b22 = 0.04794;
00476   b23 = -0.59013;
00477 
00478   delta_lon = b1 + b2*v + b3*u*u + b4*u*v + b5*v*v + b6*u*u*u
00479     + b7*u*u*v + b8*u*v*v + b9*u*u*v*v + b10*u*v*v*v
00480       + b11*v*v*v*v + b12*u*u*u*u*v + b13*u*v*v*v*v
00481         + b14*u*v*v*v*v*v + b15*v*v*v*v*v*v
00482           + b16*u*v*v*v*v*v*v + b17*u*u*u*v*v*v*v*v
00483             + b18*u*u*u*u*u*u*u*u*u + b19*u*u*u*u*u*u*u*u*v
00484               + b20*u*v*v*v*v*v*v*v*v + b21*v*v*v*v*v*v*v*v*v
00485                 + b22*u*v*v*v*v*v*v*v*v*v + b23*u*u*u*u*u*u*u*u*u*v*v*v;
00486 
00487 
00488   c1 = -36.526;
00489   c2 = 3.900;
00490   c3 = -4.723;
00491   c4 = -21.553;
00492   c5 = 7.294;
00493   c6 = 8.886;
00494   c7 = -8.440;
00495   c8 = -2.930;
00496   c9 = 56.937;
00497   c10 = -58.756;
00498   c11 = -4.061;
00499   c12 = 4.447;
00500   c13 = 4.903;
00501   c14 = -55.873;
00502   c15 = 212.005;
00503   c16 = 3.081;
00504   c17 = -254.511;
00505   c18 = -0.756;
00506   c19 = 30.654;
00507   c20 = -0.122;
00508 
00509   delta_H = c1 + c2*u + c3*v + c4*u*u + c5*u*v + c6*v*v + c7*u*u*v
00510     + c8*u*v*v + c9*u*u*u*u + c10*u*u*u*v + c11*v*v*v*v
00511       + c12*u*u*u*u*v + c13*u*u*v*v*v + c14*u*u*u*u*u*u
00512         + c15*u*u*u*u*u*v + c16*v*v*v*v*v*v + c17*u*u*u*u*u*u*u*v
00513           + c18*v*v*v*v*v*v*v*v + c19*u*u*u*u*u*u*u*u*v
00514             + c20*v*v*v*v*v*v*v*v*v;
00515 
00516 
00517 #ifdef DEBUG_DATUM
00518   double d1 = 5.068;
00519   double d2 = -11.570;
00520   double d3 = -8.574;
00521   double d4 = 27.839;
00522   double d5 = -51.911;
00523   double d6 = 29.496;
00524   double d7 = 28.343;
00525   double d8 = 24.481;
00526   double d9 = 11.424;
00527   double d10 = 132.550;
00528   double d11 = -110.232;
00529   double d12 = 41.018;
00530   double d13 = -64.953;
00531   double d14 = -128.293;
00532   double d15 = 51.241;
00533   double d16 = -4.326;
00534   double d17 = 104.097;
00535   double d18 = -128.031;
00536   double d19 = 110.694;
00537   double d20 = 36.330;
00538   double d21 = 243.149;
00539   double d22 = -15.790;
00540   double d23 = -38.043;
00541   double d24 = -40.277;
00542   double d25 = 2.746;
00543   double d26 = -7.321;
00544   double d27 = -394.404;
00545   double d28 = -927.540;
00546   double d29 = 63.390;
00547   double d30 = 10.626;
00548   double d31 = -0.520;
00549   double d32 = -117.207;
00550   double d33 = 16.352;
00551 
00552   N_height = d1 + d2*u + d3*v + d4*u*u + d5*u*v + d6*v*v + d7*u*u*u
00553     + d8*u*v*v + d9*v*v*v + d10*u*u*u*v + d11*u*u*v*v
00554       + d12*u*v*v*v + d13*v*v*v*v + d14*u*u*u*v*v + d15*u*v*v*v*v
00555         + d16*v*v*v*v*v + d17*u*u*u*u*v*v + d18*u*u*u*v*v*v
00556           + d19*u*u*v*v*v*v + d20*v*v*v*v*v*v + d21*u*u*u*u*u*u*v
00557             + d22*u*u*v*v*v*v*v + d23*u*v*v*v*v*v*v + d24*u*u*v*v*v*v*v*v
00558               + d25*u*v*v*v*v*v*v*v + d26*v*v*v*v*v*v*v*v
00559                 + d27*u*u*u*u*u*u*u*u*u + d28*u*u*u*u*u*u*u*u*v
00560                   + d29*u*u*u*u*v*v*v*v*v + d30*u*v*v*v*v*v*v*v*v
00561                     + d31*u*v*v*v*v*v*v*v*v*v + d32*u*u*u*u*u*u*u*u*v*v*v*v
00562                       + d33*u*u*u*u*u*v*v*v*v*v*v*v*v;
00563 #endif
00564 
00565   delta_lat_p = (double)delta_lat; // NOTE: delta_lat is in arc seconds
00566   delta_lon_p = (double)delta_lon; // NOTE: delta_lon is in arc seconds
00567 
00568 #ifdef DEBUG_DATUM
00569   ft_lat = 101.0 * delta_lat_p;
00570   ft_lon = 101.0 * vcl_cos(prin_lat_rad) * delta_lon_p;
00571 
00572   c_error = (double)vcl_sqrt(ft_lat*ft_lat + ft_lon*ft_lon);
00573 #endif
00574 
00575   *wgs84_lat = nad27_lat + (delta_lat_p/3600.0);
00576   *wgs84_lon = nad27_lon + (delta_lon_p/3600.0);
00577   *wgs84_el  = nad27_el + delta_H;
00578 
00579 #ifdef DEBUG_DATUM
00580   new_lat = *wgs84_lat;
00581   new_lon = *wgs84_lon;
00582   vcl_cout << "\n d_lat = " << delta_lat
00583            << "\n d_lon = " << delta_lon
00584            << "\n d_H = " << delta_H
00585            << "\n N = " << N_height
00586            << "\n circular error = " << c_error
00587            << "\n New Lat = " << new_lat << ", New Lon =\n" << new_lon
00588            << '\n';
00589 #endif
00590 }
00591 
00592 //*************************************************************************
00593 //      FUNCTION: wgs84_to_nad27n_alternate()
00594 //      AUTHOR:   Mark Young
00595 //      This is a copy of the above routine in which the deltas are
00596 //      subtracted instead of added to the input lat, lon, elev.
00597 //*************************************************************************
00598 
00599 void wgs84_to_nad27n_alternate
00600   (double wgs84_lat, double wgs84_lon, double wgs84_el,
00601    double *nad27n_lat, double *nad27n_lon, double *nad27n_el)
00602 {
00603   double u,v,k;
00604 
00605   double a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14;
00606   double a15,a16,a17,a18,a19,a20,a21,a22,a23;
00607 
00608   double b1,b2,b3,b4,b5,b6,b7,b8,b9,b10,b11,b12,b13,b14;
00609   double b15,b16,b17,b18,b19,b20,b21,b22,b23;
00610 
00611   double c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14;
00612   double c15,c16,c17,c18,c19,c20;
00613 
00614   double delta_lat;
00615   double delta_lon;
00616   double delta_H;
00617 #ifdef DEBUG_DATUM
00618   double N_height;
00619 #endif
00620 
00621   double delta_lat_p;
00622   double delta_lon_p;
00623 
00624 #ifdef DEBUG_DATUM
00625   double prin_lat_rad;
00626   double ft_lat;
00627   double ft_lon;
00628   double c_error;
00629 #endif
00630 
00631 #if 0
00632   int    lon_int;
00633   double lon_int_fl;
00634   double lon_frac;
00635 #endif
00636 
00637 #ifdef DEBUG_DATUM
00638   double new_lat;
00639   double new_lon;
00640 #endif
00641 
00642   double prin_lat; // Est. lat of principal point
00643   double prin_lon; // Est. lon of principal point
00644 
00645 #ifdef DEBUG_DATUM
00646   //   Display the program title.
00647 
00648   vcl_cout << "\n  wgs84 to NAD27N Conversion routine\n\n";
00649               "       Enter latitude, longitude ";
00650   vcl_cin >> wgs84_lat >> wgs84_lon;
00651 
00652 #endif
00653 
00654   prin_lat = (double) wgs84_lat;
00655   prin_lon = (double) wgs84_lon;
00656 
00657 #ifdef DEBUG_DATUM
00658   prin_lat_rad = prin_lat * degree_to_rad;
00659 #endif
00660 
00661 //lon_int = (int) prin_lon;
00662 //lon_int_fl = (double)lon_int;
00663 //lon_frac = vcl_fabs(prin_lon - lon_int);
00664 
00665   if (prin_lon < 0)
00666     prin_lon = 360.0 + prin_lon;
00667 //  prin_lon = 360.0 + lon_int_fl + lon_frac;
00668 
00669   k = .05235988;
00670   u = k*(prin_lat - 37);
00671   v = k*(prin_lon - 265);
00672 
00673   // Initialize latitude constants
00674 
00675   a1 = 0.16984;
00676   a2 = -0.76173;
00677   a3 = 0.09585;
00678   a4 = 1.09919;
00679   a5 = -4.57801;
00680   a6 = -1.13239;
00681   a7 = 0.49831;
00682   a8 = -.98399;
00683   a9 = 0.12415;
00684   a10 = 0.11450;
00685   a11 = 27.05396;
00686   a12 = 2.03449;
00687   a13 = 0.73357;
00688   a14 = -0.37548;
00689   a15 = -0.14197;
00690   a16 = -59.96555;
00691   a17 = 0.07439;
00692   a18 = -4.76082;
00693   a19 = 0.03385;
00694   a20 = 49.04320;
00695   a21 = -1.30575;
00696   a22 = -0.07653;
00697   a23 = 0.08646;
00698 
00699   delta_lat = a1 + a2*u + a3*v + a4*u*u + a5*u*u*u + a6*u*u*v
00700     + a7*v*v*v + a8*u*u*u*v + a9*u*v*v*v + a10*v*v*v*v
00701       + a11*u*u*u*u*u + a12*u*u*u*u*v + a13*u*u*v*v*v
00702         + a14*v*v*v*v*v + a15*v*v*v*v*v*v + a16*u*u*u*u*u*u*u
00703           + a17*v*v*v*v*v*v*v + a18*u*u*u*u*u*u*u*u
00704             + a19*v*v*v*v*v*v*v*v + a20*u*u*u*u*u*u*u*u*u
00705               + a21*u*u*u*u*u*u*v*v*v + a22*u*u*u*v*v*v*v*v*v*v*v*v
00706                 + a23*u*u*u*u*v*v*v*v*v*v*v*v*v;
00707 
00708   // Initialize longitude constants
00709 
00710   b1 = -0.88437;
00711   b2 = 2.05061;
00712   b3 = 0.26361;
00713   b4 = -0.76804;
00714   b5 = 0.13374;
00715   b6 = -1.31974;
00716   b7 = -0.52162;
00717   b8 = -1.05853;
00718   b9 = -0.49211;
00719   b10 = 2.17204;
00720   b11 = -0.06004;
00721   b12 = 0.30139;
00722   b13 = 1.88585;
00723   b14 = -0.81162;
00724   b15 = -0.05183;
00725   b16 = -0.96723;
00726   b17 = -.12948;
00727   b18 = 3.41827;
00728   b19 = -0.44507;
00729   b20 = 0.18882;
00730   b21 = -0.01444;
00731   b22 = 0.04794;
00732   b23 = -0.59013;
00733 
00734   delta_lon = b1 + b2*v + b3*u*u + b4*u*v + b5*v*v + b6*u*u*u
00735     + b7*u*u*v + b8*u*v*v + b9*u*u*v*v + b10*u*v*v*v
00736       + b11*v*v*v*v + b12*u*u*u*u*v + b13*u*v*v*v*v
00737         + b14*u*v*v*v*v*v + b15*v*v*v*v*v*v
00738           + b16*u*v*v*v*v*v*v + b17*u*u*u*v*v*v*v*v
00739             + b18*u*u*u*u*u*u*u*u*u + b19*u*u*u*u*u*u*u*u*v
00740               + b20*u*v*v*v*v*v*v*v*v + b21*v*v*v*v*v*v*v*v*v
00741                 + b22*u*v*v*v*v*v*v*v*v*v + b23*u*u*u*u*u*u*u*u*u*v*v*v;
00742 
00743 
00744   c1 = -36.526;
00745   c2 = 3.900;
00746   c3 = -4.723;
00747   c4 = -21.553;
00748   c5 = 7.294;
00749   c6 = 8.886;
00750   c7 = -8.440;
00751   c8 = -2.930;
00752   c9 = 56.937;
00753   c10 = -58.756;
00754   c11 = -4.061;
00755   c12 = 4.447;
00756   c13 = 4.903;
00757   c14 = -55.873;
00758   c15 = 212.005;
00759   c16 = 3.081;
00760   c17 = -254.511;
00761   c18 = -0.756;
00762   c19 = 30.654;
00763   c20 = -0.122;
00764 
00765   delta_H = c1 + c2*u + c3*v + c4*u*u + c5*u*v + c6*v*v + c7*u*u*v
00766     + c8*u*v*v + c9*u*u*u*u + c10*u*u*u*v + c11*v*v*v*v
00767       + c12*u*u*u*u*v + c13*u*u*v*v*v + c14*u*u*u*u*u*u
00768         + c15*u*u*u*u*u*v + c16*v*v*v*v*v*v + c17*u*u*u*u*u*u*u*v
00769           + c18*v*v*v*v*v*v*v*v + c19*u*u*u*u*u*u*u*u*v
00770             + c20*v*v*v*v*v*v*v*v*v;
00771 
00772 #ifdef DEBUG_DATUM
00773   d1 = 5.068;
00774   d2 = -11.570;
00775   d3 = -8.574;
00776   d4 = 27.839;
00777   d5 = -51.911;
00778   d6 = 29.496;
00779   d7 = 28.343;
00780   d8 = 24.481;
00781   d9 = 11.424;
00782   d10 = 132.550;
00783   d11 = -110.232;
00784   d12 = 41.018;
00785   d13 = -64.953;
00786   d14 = -128.293;
00787   d15 = 51.241;
00788   d16 = -4.326;
00789   d17 = 104.097;
00790   d18 = -128.031;
00791   d19 = 110.694;
00792   d20 = 36.330;
00793   d21 = 243.149;
00794   d22 = -15.790;
00795   d23 = -38.043;
00796   d24 = -40.277;
00797   d25 = 2.746;
00798   d26 = -7.321;
00799   d27 = -394.404;
00800   d28 = -927.540;
00801   d29 = 63.390;
00802   d30 = 10.626;
00803   d31 = -0.520;
00804   d32 = -117.207;
00805   d33 = 16.352;
00806 
00807   N_height = d1 + d2*u + d3*v + d4*u*u + d5*u*v + d6*v*v + d7*u*u*u
00808     + d8*u*v*v + d9*v*v*v + d10*u*u*u*v + d11*u*u*v*v
00809       + d12*u*v*v*v + d13*v*v*v*v + d14*u*u*u*v*v + d15*u*v*v*v*v
00810         + d16*v*v*v*v*v + d17*u*u*u*u*v*v + d18*u*u*u*v*v*v
00811           + d19*u*u*v*v*v*v + d20*v*v*v*v*v*v + d21*u*u*u*u*u*u*v
00812             + d22*u*u*v*v*v*v*v + d23*u*v*v*v*v*v*v + d24*u*u*v*v*v*v*v*v
00813               + d25*u*v*v*v*v*v*v*v + d26*v*v*v*v*v*v*v*v
00814                 + d27*u*u*u*u*u*u*u*u*u + d28*u*u*u*u*u*u*u*u*v
00815                   + d29*u*u*u*u*v*v*v*v*v + d30*u*v*v*v*v*v*v*v*v
00816                     + d31*u*v*v*v*v*v*v*v*v*v + d32*u*u*u*u*u*u*u*u*v*v*v*v
00817                       + d33*u*u*u*u*u*v*v*v*v*v*v*v*v;
00818 #endif
00819 
00820   delta_lat_p = (double)delta_lat; // NOTE: delta_lat is in arc seconds
00821   delta_lon_p = (double)delta_lon; // NOTE: delta_lon is in arc seconds
00822 
00823 #ifdef DEBUG_DATUM
00824   ft_lat = 101.0 * delta_lat_p;
00825   ft_lon = 101.0 * vcl_cos(prin_lat_rad) * delta_lon_p;
00826 
00827   c_error = (double)vcl_sqrt(ft_lat*ft_lat + ft_lon*ft_lon);
00828 #endif
00829 
00830   // SUBTRACT the deltas
00831   *nad27n_lat = wgs84_lat - (delta_lat_p/3600.0);
00832   *nad27n_lon = wgs84_lon - (delta_lon_p/3600.0);
00833   *nad27n_el  = wgs84_el - delta_H;
00834 
00835 #ifdef DEBUG_DATUM
00836   new_lat = *nad27n_lat;
00837   new_lon = *nad27n_lon;
00838   vcl_cout << "\n d_lat = " << delta_lat
00839            << "\n d_lon = " << delta_lon
00840            << "\n d_H = " << delta_H
00841            << "\n N = " << N_height
00842            << "\n circular error = " << c_error
00843            << "\n New Lat = " << new_lat << ", New Lon =\n" << new_lon
00844            << '\n';
00845 #endif
00846 }
00847 
00848 
00849 //**********************************************************************
00850 // GEO_DETIC2CENTRIC(geodetic_lat, A, B):
00851 //**********************************************************************
00852 double geo_detic2centric
00853   (double geodetic_lat,  //!< geodetic latitude of input point
00854    double A,
00855    double B)             //!< Major and minor axes of earth
00856 {
00857   return(vcl_atan((B/A)*(B/A) * vcl_tan(geodetic_lat)));
00858 }
00859 
00860 //**********************************************************************
00861 // GEO_CENTRIC2DETIC(geocentric_lat, A, B):
00862 //**********************************************************************
00863 double geo_centric2detic
00864   (double geocentric_lat, //!< geocentric latitude of input point
00865    double A,
00866    double B)              //!< Major and minor axes of earth
00867 {
00868   return(vcl_atan((A/B)*(A/B) * vcl_tan(geocentric_lat)));
00869 }
00870 
00871 
00872 //**********************************************************************
00873 //  GRS_to_latlong (x, y, z, lat, lon, el, A, B):
00874 //  Computes, from the GRS coordinates of a point, its the lat, long,
00875 //  and elevation in a GEODETIC coordinate system.
00876 //  It used an iterative method based on an alternate way
00877 //  of computing x, y, z from lat, lon, el based on the equations:
00878 //
00879 //  $N = A/\sqrt{1 - e^2 \sin(\mbox{lat})^2}$
00880 //  $x = (N + \mbox{el}) \cos(\mbox{lat})\cos(\mbox{lon})$
00881 //  $y = (N + \mbox{el}) \cos(\mbox{lat})\sin(\mbox{lon})$
00882 //  $z = (N (1-e^2) + \mbox{el}) \sin(\mbox{lat})$
00883 //
00884 //  Nov. 28, 2000 - JLM
00885 //    The input x, y, z, values are assumed to be in meters
00886 //    The input elevation and A, B values are assumed to be in meters
00887 //    The output lat and lon values are in radians
00888 //
00889 //**********************************************************************
00890 void GRS_to_latlong
00891   (double x, double y, double z, //!< Input GRS coords
00892    double *geodetic_lat,
00893    double *lon,
00894    double *el,                   //!< output coordinates of point
00895    double A,
00896    double B)                     //!< Major and minor axes of earth
00897 {
00898   double new_lat;                //!< used in iteration
00899   double N;                      //!< Local distance to earth center
00900   double xy_dist;                //!< dist in x-y plane
00901   double ee;                     //!< eccentricity square
00902 
00903   xy_dist = vcl_sqrt(x*x + y*y);
00904   ee = 1 - (B/A)*(B/A);
00905 
00906   // Compute geocentric = geodetic longitude from the
00907   // dot-product of (x, y, 0) and (1, 0, 0) and assign
00908   // proper sign to it based on y.
00909 
00910   *lon = vcl_acos(x/xy_dist);
00911   if (y < 0)    // Negative y => West of Greenwich meridian
00912     *lon = - *lon;
00913 
00914   // Compute an initial value for geodetic latitude
00915   new_lat = vcl_atan2(z, xy_dist);
00916 
00917   do
00918   {
00919     *geodetic_lat = new_lat;
00920 
00921     // Compute the sin and cos of the approx geodetic lat
00922 
00923     // Compute the distance N along the height
00924     N = A / vcl_sqrt(1 - ee * vcl_sin(*geodetic_lat) * vcl_sin(*geodetic_lat));
00925 
00926     // new_lat is already in the range -PI to PI
00927     new_lat = vcl_atan2( (z + N * ee * vcl_sin(*geodetic_lat)),  xy_dist );
00928   }
00929   while (vcl_fabs(new_lat - *geodetic_lat) > EPSILON);
00930 
00931   *geodetic_lat = new_lat;
00932   *el = xy_dist/vcl_cos(new_lat)  -  N;
00933 }
00934 
00935 
00936 //**********************************************************************
00937 //  latlong_to_GRS (lat, lon, el, x, y, z, A, B):
00938 //  Computes GRS coordinates of a point from its the lat, long,
00939 //  and elevation in a GEODETIC coordinate system.
00940 //
00941 //    Nov. 28, 2000 - JLM
00942 //    The input lat and lon values are assumed to be in radians
00943 //    The input elevation and A, B values are assumed to be in meters
00944 //    The output x, y, z, values are in meters
00945 //
00946 //
00947 //  An alternate way of doing this computation would be:
00948 //
00949 //  N = A/vcl_sqrt(1 - e*e*vcl_sin(lat)*vcl_sin(lat))
00950 //  x = (N + el)vcl_cos(lat)vcl_cos(lon)
00951 //  y = (N + el)vcl_cos(lat)vcl_sin(lon)
00952 //  z = (N(1-e*e) + el) vcl_sin(lat)
00953 //**********************************************************************
00954 
00955 void latlong_to_GRS
00956   (double geodetic_lat,
00957    double lon,
00958    double el,         //!< Input coordinates of point
00959    double *x,
00960    double *y,
00961    double *z,         //!< Output GRS coords
00962    double A,
00963    double B)          //!< Major and minor axes of earth
00964 {
00965   double geocentric_lat;
00966   double local_radius; //!< Local distance to earth center
00967   double c, s;
00968 
00969   geocentric_lat = geo_detic2centric(geodetic_lat, A, B);
00970 
00971   // Compute the sin and cos of the latitude
00972   s = vcl_sin(geocentric_lat);
00973   c = vcl_cos(geocentric_lat);
00974 
00975 
00976   // Compute the distance to the centre of the earth
00977   local_radius = (A*B) / vcl_sqrt(B*B*c*c + A*A*s*s);
00978 
00979   *x =  (local_radius*c + el*vcl_cos(geodetic_lat))*vcl_cos(lon);
00980   *y =  (local_radius*c + el*vcl_cos(geodetic_lat))*vcl_sin(lon);
00981   *z =  (local_radius*s + el*vcl_sin(geodetic_lat));
00982 
00983 #ifdef DEBUG_DATUM
00984   double tlat, tlon, tel;
00985   GRS_to_latlong(*x, *y, *z, &tlat, &tlon, &tel, A, B);
00986   vcl_cout << "Error in computation: (" << (tlat-geodetic_lat) << ", " << (tlon-lon) << ", " << (tel-el) << ")\n";
00987 #endif
00988 }
00989 
00990 //*************************************************************************
00991 //      FUNCTION: nad27n_to_wgs72()
00992 //*************************************************************************
00993 
00994 void nad27n_to_wgs72
00995   (
00996    double phi,                 //!< input lat, lon, elev coord (degrees)
00997    double lamda,
00998    double height,
00999    double *wgs72_phi,          //!< lat in wgs72 (degrees)
01000    double *wgs72_lamda,        //!< lon in wgs72 (degrees)
01001    double *wgs72_hgt)          //!< elev in wgs72 (meters)
01002 {
01003   double wgs84_phi, wgs84_lamda, wgs84_hgt;
01004 
01005   nad27n_to_wgs84(phi, lamda, height,
01006                   &wgs84_phi, &wgs84_lamda, &wgs84_hgt);
01007 
01008   wgs84_to_wgs72(wgs84_phi, wgs84_lamda, wgs84_hgt,
01009                  wgs72_phi, wgs72_lamda, wgs72_hgt);
01010 }
01011 
01012 
01013 //*************************************************************************
01014 //      FUNCTION: wgs72_to_nad27n()
01015 //*************************************************************************
01016 
01017 void wgs72_to_nad27n
01018   (
01019    double phi,           //!< input lat, lon, elev coord (degrees)
01020    double lamda,
01021    double height,
01022    double *nad27n_phi,   //!< lat in nad27n (degrees)
01023    double *nad27n_lamda, //!< lon in nad27n (degrees)
01024    double *nad27n_hgt)   //!< elev in nad27n (meters)
01025 {
01026   double wgs84_phi, wgs84_lamda, wgs84_hgt;
01027 
01028   wgs72_to_wgs84(phi, lamda, height,
01029                  &wgs84_phi, &wgs84_lamda, &wgs84_hgt);
01030 
01031   wgs84_to_nad27n(wgs84_phi, wgs84_lamda, wgs84_hgt,
01032                   nad27n_phi, nad27n_lamda, nad27n_hgt);
01033 }
01034