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