core/vpgl/vpgl_lvcs.cxx
Go to the documentation of this file.
00001 #include "vpgl_lvcs.h"
00002 //:
00003 // \file
00004 #include <vcl_string.h>
00005 #include <vcl_cstring.h>
00006 #include <vsl/vsl_basic_xml_element.h>
00007 #include <vpgl/vpgl_datum_conversion.h>
00008 #include <vpgl/vpgl_earth_constants.h>
00009 #include <vpgl/vpgl_utm.h>
00010 
00011 #define SMALL_STEP 1.0e-6 // assumed to be in radians
00012 
00013 const char* vpgl_lvcs::cs_name_strings[]  = { "wgs84", "nad27n", "wgs72", "utm"};
00014 
00015 vpgl_lvcs::cs_names vpgl_lvcs::str_to_enum(const char* s)
00016 {
00017   for (int i=0; i < vpgl_lvcs::NumNames; i++)
00018     if (vcl_strcmp(s, vpgl_lvcs::cs_name_strings[i]) == 0)
00019       return (vpgl_lvcs::cs_names) i;
00020   return vpgl_lvcs::NumNames;
00021 }
00022 
00023 void vpgl_lvcs::set_angle_conversions(AngUnits ang_unit, double& to_radians,
00024                                       double& to_degrees)
00025 {
00026   to_radians=1.0;
00027   to_degrees=1.0;
00028   if (ang_unit == DEG)
00029     to_radians = DEGREES_TO_RADIANS;
00030   else
00031     to_degrees = RADIANS_TO_DEGREES;
00032 }
00033 
00034 void vpgl_lvcs::set_length_conversions(LenUnits len_unit, double& to_meters,
00035                                        double& to_feet)
00036 {
00037   to_meters = 1.0;
00038   to_feet = 1.0;
00039   if (len_unit == FEET)
00040     to_meters = FEET_TO_METERS;
00041   else
00042     to_feet = METERS_TO_FEET;
00043 }
00044 
00045 vpgl_lvcs::vpgl_lvcs(const vpgl_lvcs& lvcs)
00046  : vbl_ref_count(),
00047    local_cs_name_(lvcs.local_cs_name_),
00048    localCSOriginLat_(lvcs.localCSOriginLat_),
00049    localCSOriginLon_(lvcs.localCSOriginLon_),
00050    localCSOriginElev_(lvcs.localCSOriginElev_),
00051    lat_scale_(lvcs.lat_scale_),
00052    lon_scale_(lvcs.lon_scale_),
00053    geo_angle_unit_(lvcs.geo_angle_unit_),
00054    localXYZUnit_(lvcs.localXYZUnit_),
00055    lox_(lvcs.lox_),
00056    loy_(lvcs.loy_),
00057    theta_(lvcs.theta_),
00058    localUTMOrigin_X_East_(lvcs.localUTMOrigin_X_East_),
00059    localUTMOrigin_Y_North_(lvcs.localUTMOrigin_Y_North_),
00060    localUTMOrigin_Zone_(lvcs.localUTMOrigin_Zone_)
00061 {
00062   if (lat_scale_ == 0.0 || lon_scale_ == 0.0)
00063     this->compute_scale();
00064 }
00065 
00066 
00067 vpgl_lvcs& vpgl_lvcs::operator=(const vpgl_lvcs& lvcs)
00068 {
00069   local_cs_name_ = lvcs.local_cs_name_;
00070   localCSOriginLat_ = lvcs.localCSOriginLat_;
00071   localCSOriginLon_ = lvcs.localCSOriginLon_;
00072   localCSOriginElev_ = lvcs.localCSOriginElev_;
00073   lat_scale_ = lvcs.lat_scale_;
00074   lon_scale_ = lvcs.lon_scale_;
00075   geo_angle_unit_ = lvcs.geo_angle_unit_,
00076   localXYZUnit_ = lvcs.localXYZUnit_;
00077   lox_ = lvcs.lox_;
00078   loy_ = lvcs.loy_;
00079   theta_ = lvcs.theta_;
00080   localUTMOrigin_X_East_ = lvcs.localUTMOrigin_X_East_;
00081   localUTMOrigin_Y_North_ = lvcs.localUTMOrigin_Y_North_;
00082   localUTMOrigin_Zone_ = lvcs.localUTMOrigin_Zone_;
00083   if (lat_scale_ == 0.0 || lon_scale_ == 0.0)
00084   this->compute_scale();
00085   return *this;
00086 }
00087 
00088 vpgl_lvcs::vpgl_lvcs(double orig_lat, double orig_lon, double orig_elev,
00089                      cs_names cs_name,
00090                      double lat_scale, double lon_scale,
00091                      AngUnits ang_unit, // = DEG
00092                      LenUnits len_unit, // = METERS
00093                      double lox,
00094                      double loy,
00095                      double theta)
00096   :local_cs_name_(cs_name),
00097    localCSOriginLat_(orig_lat),
00098    localCSOriginLon_(orig_lon),
00099    localCSOriginElev_(orig_elev),
00100    lat_scale_(lat_scale),
00101    lon_scale_(lon_scale),
00102    geo_angle_unit_(ang_unit),
00103    localXYZUnit_(len_unit),
00104    lox_(lox),
00105    loy_(loy),
00106    theta_(theta)
00107 {
00108   double local_to_meters, local_to_feet, local_to_radians, local_to_degrees;
00109   this->set_angle_conversions(geo_angle_unit_, local_to_radians,
00110                               local_to_degrees);
00111   this->set_length_conversions(localXYZUnit_, local_to_meters, local_to_feet);
00112 
00113   if (cs_name == vpgl_lvcs::utm) {
00114     //: the origin is still given in wgs84
00115     vpgl_utm u;
00116     u.transform(localCSOriginLat_*local_to_degrees, localCSOriginLon_*local_to_degrees, localUTMOrigin_X_East_, localUTMOrigin_Y_North_, localUTMOrigin_Zone_);
00117     vcl_cout << "utm origin zone: " << localUTMOrigin_Zone_ << ' ' << localUTMOrigin_X_East_ << " East " << localUTMOrigin_Y_North_ << " North" << vcl_endl;
00118     lat_scale_ = 0.0; lon_scale_ = 0.0;
00119   }
00120   if (lat_scale_ == 0.0 || lon_scale_ == 0.0)
00121     this->compute_scale();
00122 }
00123 
00124 //--------------------------------------------------------------------------
00125 //: A simplified constructor that takes the origin and specified coordinate system.
00126 //  The units of the input latitude and longitude values are assumed to be
00127 //  the same as specified by ang_unit.
00128 //  Similarly, the unit of elevation is specified by elev_unit.
00129 //  The local cartesian system is aligned with North and East
00130 //
00131 vpgl_lvcs::vpgl_lvcs(double orig_lat, double orig_lon, double orig_elev,
00132                      cs_names cs_name,
00133                      AngUnits  ang_unit,
00134                      LenUnits len_unit)
00135   : local_cs_name_(cs_name),
00136     localCSOriginLat_(orig_lat), localCSOriginLon_(orig_lon),
00137     localCSOriginElev_(orig_elev),
00138     geo_angle_unit_(ang_unit), localXYZUnit_(len_unit), lox_(0), loy_(0), theta_(0)
00139 {
00140   double local_to_meters, local_to_feet, local_to_radians, local_to_degrees;
00141   this->set_angle_conversions(geo_angle_unit_, local_to_radians,
00142                               local_to_degrees);
00143   this->set_length_conversions(localXYZUnit_, local_to_meters, local_to_feet);
00144 
00145   if (cs_name == vpgl_lvcs::utm) {
00146     //: the origin is still given in wgs84
00147     vpgl_utm u;
00148     u.transform(localCSOriginLat_*local_to_degrees, localCSOriginLon_*local_to_degrees, localUTMOrigin_X_East_, localUTMOrigin_Y_North_, localUTMOrigin_Zone_);
00149     vcl_cout << "utm origin zone: " << localUTMOrigin_Zone_ << ' ' << localUTMOrigin_X_East_ << " East  " << localUTMOrigin_Y_North_ << " North  elev: " << localCSOriginElev_ << vcl_endl;
00150   }
00151   lat_scale_ = 0;
00152   lon_scale_ = 0;
00153   this->compute_scale();
00154 }
00155 
00156 //--------------------------------------------------------------------------
00157 //: This constructor takes a lat-lon bounding box and erects a local vertical coordinate system at the center.
00158 //  The units of the input latitude and longitude values are assumed to be
00159 //  the same as specified by ang_unit.
00160 //  Similarly, the unit of elevation is specified by elev_unit.
00161 //  The local cartesian system is aligned with North and East
00162 //
00163 vpgl_lvcs::vpgl_lvcs(double lat_low, double lon_low,
00164                      double lat_high, double lon_high,
00165                      double elev,
00166                      cs_names cs_name, AngUnits ang_unit, LenUnits elev_unit)
00167   :   local_cs_name_(cs_name), localCSOriginElev_(elev),
00168       geo_angle_unit_(ang_unit), localXYZUnit_(elev_unit)
00169 {
00170   double average_lat = (lat_low + lat_high)/2.0;
00171   double average_lon = (lon_low + lon_high)/2.0;
00172   localCSOriginLat_ = average_lat;
00173   localCSOriginLon_ = average_lon;
00174 
00175   double local_to_meters, local_to_feet, local_to_radians, local_to_degrees;
00176   this->set_angle_conversions(geo_angle_unit_, local_to_radians,
00177                               local_to_degrees);
00178   this->set_length_conversions(localXYZUnit_, local_to_meters, local_to_feet);
00179 
00180   if (cs_name == vpgl_lvcs::utm) {
00181     //: the origin is still given in wgs84
00182     vpgl_utm u;
00183     u.transform(localCSOriginLat_*local_to_degrees, localCSOriginLon_*local_to_degrees, localUTMOrigin_X_East_, localUTMOrigin_Y_North_, localUTMOrigin_Zone_);
00184     vcl_cout << "utm origin zone: " << localUTMOrigin_Zone_ << ' ' << localUTMOrigin_X_East_ << " East  " << localUTMOrigin_Y_North_ << " North" << vcl_endl;
00185   }
00186 
00187 
00188   lat_scale_ = 0;
00189   lon_scale_ = 0;
00190   this->compute_scale();
00191 }
00192 
00193 double vpgl_lvcs::radians_to_degrees(const double val)
00194 {
00195   return val*RADIANS_TO_DEGREES;
00196 }
00197 
00198 void  vpgl_lvcs::radians_to_degrees(double& x, double& y, double& z)
00199 {
00200   x = x * RADIANS_TO_DEGREES;
00201   y = y * RADIANS_TO_DEGREES;
00202   z = z * RADIANS_TO_DEGREES;
00203 }
00204 
00205 void vpgl_lvcs::degrees_to_dms(double geoval, int& degrees, int& minutes, double& seconds)
00206 {
00207   double fmin = vcl_fabs(geoval - (int)geoval)*60.0;
00208   int isec = (int) ((fmin - (int)fmin)*60.0 + .5);
00209   int imin = (int) ((isec == 60) ? fmin+1 : fmin) ;
00210   int extra = (geoval>0) ? 1 : -1;
00211   degrees = (int) ( (imin == 60) ? geoval+extra : geoval);
00212   minutes = ( imin== 60 ? 0 : imin);
00213   seconds = (fmin - (int)fmin)*60.0;
00214 }
00215 
00216 // compute the scales for the given coordinate system.
00217 void vpgl_lvcs::compute_scale()
00218 {
00219   double wgs84_phi, wgs84_lamda, wgs84_hgt;  // WGS84 coords of the origin
00220   double grs80_x, grs80_y, grs80_z;          // GRS80 coords of the origin
00221   double grs80_x1, grs80_y1, grs80_z1;
00222   double to_meters, to_feet, to_radians, to_degrees;
00223   this->set_angle_conversions(geo_angle_unit_, to_radians, to_degrees);
00224   this->set_length_conversions(localXYZUnit_, to_meters, to_feet);
00225   // Convert origin to WGS84
00226   switch (local_cs_name_)
00227   {
00228    case vpgl_lvcs::utm:
00229    case vpgl_lvcs::wgs84:
00230     wgs84_phi = localCSOriginLat_*to_radians;
00231     wgs84_lamda = localCSOriginLon_*to_radians;
00232     wgs84_hgt = localCSOriginElev_*to_meters;
00233     break;
00234 
00235    case vpgl_lvcs::nad27n:
00236     //The inputs, phi and lamda, are assumed to be in degrees
00237     nad27n_to_wgs84(localCSOriginLat_*to_degrees,
00238                     localCSOriginLon_*to_degrees,
00239                     localCSOriginElev_*to_meters,
00240                     &wgs84_phi, &wgs84_lamda, &wgs84_hgt);
00241     wgs84_phi *= to_radians;
00242     wgs84_lamda *= to_radians;
00243     break;
00244    case vpgl_lvcs::wgs72:
00245     //The inputs, phi and lamda, are assumed to be in degrees
00246     wgs72_to_wgs84(localCSOriginLat_*to_degrees,
00247                    localCSOriginLon_*to_degrees,
00248                    localCSOriginElev_*to_meters,
00249                    &wgs84_phi, &wgs84_lamda, &wgs84_hgt);
00250     wgs84_phi *= to_radians;
00251     wgs84_lamda *= to_radians;
00252     break;
00253    case vpgl_lvcs::NumNames:
00254    default:
00255      wgs84_phi = wgs84_lamda = wgs84_hgt = 0.0;  // dummy initialisation
00256     break;
00257   }
00258 
00259   //The inputs, wgs84_phi, wgs84_lamda, are assumed to be in radians
00260   //The inputs wgs84_hgt, GRS80a, GRS80b, are assumed to be in meters
00261   //The outputs grs80_x, grs80_y, grs80_z, are in meters
00262   latlong_to_GRS(wgs84_phi, wgs84_lamda, wgs84_hgt,
00263                  &grs80_x, &grs80_y, &grs80_z, GRS80_a, GRS80_b);
00264 
00265   if (lat_scale_ == 0.0)
00266   {
00267     switch (local_cs_name_)
00268     {
00269      case vpgl_lvcs::nad27n:
00270       //lat, lon inputs are in degrees. elevation is in meters.
00271       //SMALL_STEP is in radians
00272       nad27n_to_wgs84(
00273                       (localCSOriginLat_*to_radians+SMALL_STEP)*to_degrees,
00274                       localCSOriginLon_*to_degrees,
00275                       localCSOriginElev_*to_meters,
00276                       &wgs84_phi, &wgs84_lamda, &wgs84_hgt);
00277       wgs84_phi *= to_radians;
00278       wgs84_lamda *= to_radians;
00279       break;
00280      case vpgl_lvcs::utm:
00281      case vpgl_lvcs::wgs84:
00282       wgs84_phi = localCSOriginLat_*to_radians + SMALL_STEP;
00283       wgs84_lamda = localCSOriginLon_*to_radians;
00284       wgs84_hgt = localCSOriginElev_*to_meters;
00285       break;
00286      case vpgl_lvcs::wgs72://Why empty?
00287       break;
00288      case vpgl_lvcs::NumNames:
00289       break;
00290      default:
00291       break;
00292     }
00293 
00294     latlong_to_GRS(wgs84_phi, wgs84_lamda, wgs84_hgt,
00295                    &grs80_x1, &grs80_y1, &grs80_z1, GRS80_a, GRS80_b);
00296 
00297     lat_scale_ = SMALL_STEP/vcl_sqrt((grs80_x - grs80_x1)*(grs80_x - grs80_x1) +
00298                                      (grs80_y - grs80_y1)*(grs80_y - grs80_y1) +
00299                                      (grs80_z - grs80_z1)*(grs80_z - grs80_z1));
00300     //lat_scale_ is in radians/meter.
00301   }
00302 
00303   if (lon_scale_ == 0.0)
00304   {
00305     switch (local_cs_name_)
00306     {
00307      case vpgl_lvcs::nad27n:
00308       nad27n_to_wgs84(localCSOriginLat_*to_degrees,
00309                       (localCSOriginLon_*to_radians+SMALL_STEP)*to_degrees,
00310                       localCSOriginElev_*to_meters,
00311                       &wgs84_phi, &wgs84_lamda, &wgs84_hgt);
00312       wgs84_phi *= to_radians;
00313       wgs84_lamda *= to_radians;
00314       break;
00315      case vpgl_lvcs::utm:
00316      case vpgl_lvcs::wgs84:
00317       wgs84_phi = localCSOriginLat_*to_radians;
00318       wgs84_lamda = localCSOriginLon_*to_radians + SMALL_STEP;
00319       wgs84_hgt = localCSOriginElev_*to_meters;
00320       break;
00321      case vpgl_lvcs::wgs72:
00322       break;
00323      case vpgl_lvcs::NumNames:
00324       break;
00325      default:
00326       break;
00327     }
00328 
00329     latlong_to_GRS(wgs84_phi, wgs84_lamda, wgs84_hgt,
00330                    &grs80_x1, &grs80_y1, &grs80_z1, GRS80_a, GRS80_b);
00331 
00332     lon_scale_ = SMALL_STEP/vcl_sqrt((grs80_x - grs80_x1)*(grs80_x - grs80_x1) +
00333                                      (grs80_y - grs80_y1)*(grs80_y - grs80_y1) +
00334                                      (grs80_z - grs80_z1)*(grs80_z - grs80_z1));
00335     //lon_scale_ is in radians/meter
00336   }
00337 }
00338 
00339 //------------------------------------------------------------------------------
00340 //: Converts pointin, given in local vertical coord system, to pointout in the global coord system given by the string lobalcs_name.
00341 //  X, Y, Z in pointin are assumed to be lengths, in the units specified
00342 //  by this->localXYZUnit_.
00343 //  pointout is written out in [angle, angle, length], as specified by
00344 //  the specified units
00345 //  If global_cs_name == UTM, pointout_lon is X_East, pointout_lat is Y_North
00346 void vpgl_lvcs::local_to_global(const double pointin_x,
00347                                 const double pointin_y,
00348                                 const double pointin_z,
00349                                 cs_names global_cs_name,
00350                                 double& pointout_lon,
00351                                 double& pointout_lat,
00352                                 double& pointout_z,
00353                                 AngUnits output_ang_unit,
00354                                 LenUnits output_len_unit
00355                                )
00356 {
00357   double local_to_meters, local_to_feet, local_to_radians, local_to_degrees;
00358   this->set_angle_conversions(geo_angle_unit_, local_to_radians,
00359                               local_to_degrees);
00360   this->set_length_conversions(localXYZUnit_, local_to_meters, local_to_feet);
00361 
00362   double local_lat, local_lon, local_elev;
00363   double global_lat, global_lon, global_elev;
00364 
00365   // First apply transform to align axes with compass.
00366   double aligned_x = pointin_x;
00367   double aligned_y = pointin_y;
00368   local_transform(aligned_x, aligned_y);
00369 
00370   if (local_cs_name_ == vpgl_lvcs::utm) {
00371 
00372 
00373     if (global_cs_name == vpgl_lvcs::utm) {
00374       if (output_len_unit == METERS) {
00375         pointout_lon = aligned_x*local_to_meters + localUTMOrigin_X_East_;
00376         pointout_lat = aligned_y*local_to_meters + localUTMOrigin_Y_North_;
00377         pointout_z = pointin_z*local_to_meters + localCSOriginElev_*local_to_meters;
00378       }
00379       else {
00380         pointout_lon = aligned_x*local_to_feet + localUTMOrigin_X_East_*local_to_feet;
00381         pointout_lat = aligned_y*local_to_feet + localUTMOrigin_Y_North_*local_to_feet;
00382         pointout_z = pointin_z*local_to_feet + localCSOriginElev_*local_to_feet;
00383       }
00384       return;
00385     }
00386 
00387     vpgl_utm u;
00388     u.transform(localUTMOrigin_Zone_, pointin_x*local_to_meters + localUTMOrigin_X_East_,
00389                                       pointin_y*local_to_meters + localUTMOrigin_Y_North_,
00390                                       pointin_z*local_to_meters + localCSOriginElev_*local_to_meters,
00391                 local_lat, local_lon, local_elev);
00392 
00393     if (global_cs_name == vpgl_lvcs::wgs84) {  // global values will be in degrees and in meters
00394       global_lat = local_lat;
00395       global_lon = local_lon;
00396       global_elev = local_elev;
00397     }
00398     else if (global_cs_name == vpgl_lvcs::nad27n)
00399     {
00400       wgs84_to_nad27n(local_lat,
00401                       local_lon,
00402                       local_elev,
00403                       &global_lat, &global_lon, &global_elev);
00404     }
00405     else if (global_cs_name == vpgl_lvcs::wgs72)
00406     {
00407       wgs84_to_wgs72(local_lat,
00408                      local_lon,
00409                      local_elev,
00410                      &global_lat, &global_lon, &global_elev);
00411     }
00412     else {
00413       vcl_cout << "Error: Global CS " << vpgl_lvcs::cs_name_strings[global_cs_name]
00414                << " unrecognized." << '\n';
00415       global_lat = global_lon = global_elev = 0.0; // dummy initialisation
00416     }
00417   }
00418   else {
00419     // Now compute the lat, lon, elev of the output point in Local CS
00420     local_lat = aligned_y*local_to_meters*lat_scale_ + localCSOriginLat_*local_to_radians;
00421     local_lon = aligned_x*local_to_meters*lon_scale_ + localCSOriginLon_*local_to_radians;
00422     local_elev = pointin_z*local_to_meters           + localCSOriginElev_*local_to_meters;
00423 
00424     local_lat *= RADIANS_TO_DEGREES;
00425     local_lon *= RADIANS_TO_DEGREES;
00426 
00427     //at this point local_lat, local_lon are in degrees
00428     //local_elev is in meters
00429     if (local_cs_name_ == global_cs_name)
00430     {
00431       // Local and global coord systems are the same
00432       global_lat = local_lat;
00433       global_lon = local_lon;
00434       global_elev = local_elev;
00435     }
00436     else if (local_cs_name_ ==  vpgl_lvcs::nad27n)
00437     {
00438       // Convert from "nad27n" to whatever
00439       if (global_cs_name == vpgl_lvcs::wgs84)
00440       {
00441         nad27n_to_wgs84(local_lat,
00442                         local_lon,
00443                         local_elev,
00444                         &global_lat, &global_lon, &global_elev);
00445       }
00446       else if (global_cs_name ==  vpgl_lvcs::wgs72)
00447       {
00448         nad27n_to_wgs72(local_lat, local_lon,
00449                         local_elev,
00450                         &global_lat, &global_lon, &global_elev);
00451       }
00452       else {
00453         vcl_cout << "Error: Global CS " << vpgl_lvcs::cs_name_strings[global_cs_name]
00454                  << " unrecognized." << '\n';
00455         global_lat = global_lon = global_elev = 0.0; // dummy initialisation
00456       }
00457     }
00458     else if (local_cs_name_ == vpgl_lvcs::wgs72)
00459     {
00460       // Convert from "wgs72" to whatever
00461       if (global_cs_name == vpgl_lvcs::nad27n)
00462       {
00463         wgs72_to_nad27n(local_lat,
00464                         local_lon,
00465                         local_elev,
00466                         &global_lat, &global_lon, &global_elev);
00467       }
00468       else if (global_cs_name == vpgl_lvcs::wgs84)
00469       {
00470         wgs72_to_wgs84(local_lat,
00471                        local_lon,
00472                        local_elev,
00473                        &global_lat, &global_lon, &global_elev);
00474       }
00475       else {
00476         vcl_cout << "Error: Global CS " << vpgl_lvcs::cs_name_strings[global_cs_name]
00477                  << " unrecognized." << '\n';
00478         global_lat = global_lon = global_elev = 0.0; // dummy initialisation
00479       }
00480     }
00481     else if (local_cs_name_ == vpgl_lvcs::wgs84)
00482     {
00483       // Convert from "wgs84" to whatever
00484       if (global_cs_name == vpgl_lvcs::nad27n)
00485       {
00486         wgs84_to_nad27n(local_lat,
00487                         local_lon,
00488                         local_elev,
00489                         &global_lat, &global_lon, &global_elev);
00490       }
00491       else if (global_cs_name == vpgl_lvcs::wgs72)
00492       {
00493         wgs84_to_wgs72(local_lat,
00494                        local_lon,
00495                        local_elev,
00496                        &global_lat, &global_lon, &global_elev);
00497       }
00498       else {
00499         vcl_cout << "Error: Global CS " << vpgl_lvcs::cs_name_strings[global_cs_name]
00500                  << " unrecognized." << '\n';
00501         global_lat = global_lon = global_elev = 0.0; // dummy initialisation
00502       }
00503     }
00504     else {
00505       vcl_cout << "Error: Local CS " << vpgl_lvcs::cs_name_strings[local_cs_name_]
00506                << " unrecognized." << '\n';
00507       global_lat = global_lon = global_elev = 0.0; // dummy initialisation
00508     }
00509   }
00510 
00511   //at this point, global_lat and global_lon are in degrees.
00512 
00513   if (output_ang_unit==DEG)
00514   {
00515     pointout_lon = global_lon;
00516     pointout_lat = global_lat;
00517   }
00518   else
00519   {
00520     pointout_lon = global_lon*DEGREES_TO_RADIANS;
00521     pointout_lat = global_lat*DEGREES_TO_RADIANS;
00522   }
00523 
00524   if (output_len_unit == METERS)
00525     pointout_z = global_elev;
00526   else
00527     pointout_z = global_elev*METERS_TO_FEET;
00528 
00529 #ifdef LVCS_DEBUG
00530   vcl_cout << "Local " << vpgl_lvcs::cs_name_strings[local_cs_name_]
00531            << " [" << pointin_y << ", " << pointin_x << ", "  << pointin_z
00532            << "]  MAPS TO Global "
00533            << vpgl_lvcs::cs_name_strings[global_cs_name]
00534            << " [" << pointout_lat << ", " << pointout_lon << ", " << pointout_z << "]\n";
00535 #endif
00536 }
00537 
00538 
00539 //----------------------------------------------------------------------------
00540 //: Converts pointin, given in a global coord system described by global_cs_name, to pointout in the local vertical coord system.
00541 //  The units of X, Y, Z are specified by input_ang_unit and input_len_unit
00542 //  to define lon, lat, elev in (angle, angle, length).
00543 //  The output point is returned in the units specified by
00544 //  this->localXYZUnit_.
00545 //  If global_cs_name == UTM, pointin_lon is X_East, pointin_lat is Y_North
00546 void vpgl_lvcs::global_to_local(const double pointin_lon,
00547                                 const double pointin_lat,
00548                                 const double pointin_z,
00549                                 cs_names global_cs_name,
00550                                 double& pointout_x,
00551                                 double& pointout_y,
00552                                 double& pointout_z,
00553                                 AngUnits input_ang_unit,
00554                                 LenUnits input_len_unit)
00555 {
00556   double local_to_meters, local_to_feet, local_to_radians, local_to_degrees;
00557   this->set_angle_conversions(geo_angle_unit_, local_to_radians,
00558                               local_to_degrees);
00559   this->set_length_conversions(localXYZUnit_, local_to_meters, local_to_feet);
00560   double global_lat, global_lon, global_elev;
00561   double local_lat, local_lon, local_elev;
00562 
00563   global_lat  = pointin_lat;
00564   global_lon  = pointin_lon;
00565   global_elev = pointin_z;
00566 
00567   if (global_cs_name == vpgl_lvcs::utm) {
00568       if (local_cs_name_ == vpgl_lvcs::utm) {
00569         if (input_len_unit == METERS) {
00570           pointout_x = pointin_lon - localUTMOrigin_X_East_;  // these are always in meters
00571           pointout_y = pointin_lat - localUTMOrigin_Y_North_;
00572           pointout_z = pointin_z - localCSOriginElev_*local_to_meters;
00573         }
00574         else {
00575           pointout_x = pointin_lon*FEET_TO_METERS + localUTMOrigin_X_East_;
00576           pointout_y = pointin_lat*FEET_TO_METERS + localUTMOrigin_Y_North_;
00577           pointout_z = pointin_z*FEET_TO_METERS + localCSOriginElev_*local_to_meters;
00578         }
00579         if (localXYZUnit_==FEET)
00580         {
00581           pointout_x *= METERS_TO_FEET;
00582           pointout_y *= METERS_TO_FEET;
00583           pointout_z *= METERS_TO_FEET;
00584         }
00585         // Transform from compass aligned into local co-ordinates.
00586         inverse_local_transform(pointout_x,pointout_y);
00587         return;
00588       }
00589       else {
00590         vcl_cerr << "global cs UTM is not supported with other local cs like wgs84, etc.!\n";
00591         return;
00592       }
00593   }
00594 
00595   //convert input global point to degrees and meters
00596 
00597   if (input_ang_unit==RADIANS)
00598   {
00599    global_lat *= RADIANS_TO_DEGREES;
00600    global_lon *= RADIANS_TO_DEGREES;
00601   }
00602 
00603   if (input_len_unit==FEET)
00604     global_elev *= FEET_TO_METERS;
00605 
00606   // Convert from global CS to local CS of the origin of LVCS
00607   if (global_cs_name == local_cs_name_)
00608   {
00609     // Global and local coord systems are the same
00610     local_lat  = global_lat;
00611     local_lon  = global_lon;
00612     local_elev = global_elev;
00613   }
00614   else if (global_cs_name == vpgl_lvcs::nad27n)
00615   {
00616     // Convert from "nad27n" to whatever
00617     if (local_cs_name_ == vpgl_lvcs::wgs84)
00618     {
00619       nad27n_to_wgs84(global_lat, global_lon, global_elev,
00620                       &local_lat, &local_lon, &local_elev);
00621     }
00622     else if (local_cs_name_ == vpgl_lvcs::wgs72)
00623     {
00624       nad27n_to_wgs72(global_lat, global_lon, global_elev,
00625                       &local_lat, &local_lon, &local_elev);
00626     }
00627     else if (local_cs_name_ == vpgl_lvcs::utm)
00628     {
00629       nad27n_to_wgs84(global_lat, global_lon, global_elev,
00630                       &local_lat, &local_lon, &local_elev);
00631 
00632       vpgl_utm u; int zone;
00633       u.transform(local_lat, local_lon, pointout_x, pointout_y, zone);
00634       if (zone != localUTMOrigin_Zone_) {
00635         vcl_cerr << "In vpgl_lvcs::global_to_local() -- the UTM zone of the input point is not the same as the zone of the lvcs origin!\n";
00636         return;
00637       }
00638       pointout_x -= localUTMOrigin_X_East_;
00639       pointout_y -= localUTMOrigin_Y_North_;
00640       pointout_z = global_elev - localCSOriginElev_*local_to_meters;
00641       if (localXYZUnit_==FEET)
00642       {
00643         pointout_x *= METERS_TO_FEET;
00644         pointout_y *= METERS_TO_FEET;
00645         pointout_z *= METERS_TO_FEET;
00646       }
00647       // Transform from compass aligned into local co-ordinates.
00648       inverse_local_transform(pointout_x,pointout_y);
00649       return;
00650     }
00651     else {
00652       vcl_cout << "Error: Local CS " << vpgl_lvcs::cs_name_strings[local_cs_name_]
00653                << " unrecognized." << '\n';
00654       local_lat = local_lon = local_elev = 0.0; // dummy initialisation
00655     }
00656   }
00657   else if (global_cs_name == vpgl_lvcs::wgs72)
00658   {
00659     // Convert from "wgs72" to whatever
00660     if (local_cs_name_ == vpgl_lvcs::nad27n)
00661     {
00662       wgs72_to_nad27n(global_lat, global_lon, global_elev,
00663                       &local_lat, &local_lon, &local_elev);
00664     }
00665     else if (local_cs_name_ == vpgl_lvcs::wgs84)
00666     {
00667       wgs72_to_wgs84(global_lat, global_lon, global_elev,
00668                      &local_lat, &local_lon, &local_elev);
00669     }
00670     else if (local_cs_name_ == vpgl_lvcs::utm)
00671     {
00672       wgs72_to_wgs84(global_lat, global_lon, global_elev,
00673                      &local_lat, &local_lon, &local_elev);
00674 
00675       vpgl_utm u; int zone;
00676       u.transform(local_lat, local_lon, pointout_x, pointout_y, zone);
00677       if (zone != localUTMOrigin_Zone_) {
00678         vcl_cerr << "In vpgl_lvcs::global_to_local() -- the UTM zone of the input point is not the same as the zone of the lvcs origin!\n";
00679         return;
00680       }
00681       pointout_x -= localUTMOrigin_X_East_;
00682       pointout_y -= localUTMOrigin_Y_North_;
00683       pointout_z = global_elev - localCSOriginElev_*local_to_meters;
00684       if (localXYZUnit_==FEET)
00685       {
00686         pointout_x *= METERS_TO_FEET;
00687         pointout_y *= METERS_TO_FEET;
00688         pointout_z *= METERS_TO_FEET;
00689       }
00690       // Transform from compass aligned into local co-ordinates.
00691       inverse_local_transform(pointout_x,pointout_y);
00692       return;
00693     }
00694     else {
00695       vcl_cout << "Error: Local CS " << vpgl_lvcs::cs_name_strings[local_cs_name_]
00696                << " unrecognized." << '\n';
00697       local_lat = local_lon = local_elev = 0.0; // dummy initialisation
00698     }
00699   }
00700   else if (global_cs_name == vpgl_lvcs::wgs84)
00701   {
00702     // Convert from "wgs84" to whatever
00703     if (local_cs_name_ == vpgl_lvcs::nad27n)
00704     {
00705       wgs84_to_nad27n(global_lat, global_lon, global_elev,
00706                       &local_lat, &local_lon, &local_elev);
00707     }
00708     else if (local_cs_name_ ==  vpgl_lvcs::wgs72)
00709     {
00710       wgs84_to_wgs72(global_lat, global_lon, global_elev,
00711                      &local_lat, &local_lon, &local_elev);
00712     }
00713     else if (local_cs_name_ == vpgl_lvcs::utm)
00714     {
00715       vpgl_utm u; int zone;
00716       u.transform(global_lat, global_lon, pointout_x, pointout_y, zone);
00717       if (zone != localUTMOrigin_Zone_) {
00718         vcl_cerr << "In vpgl_lvcs::global_to_local() -- the UTM zone of the input point is not the same as the zone of the lvcs origin!\n";
00719         return;
00720       }
00721       pointout_x -= localUTMOrigin_X_East_;
00722       pointout_y -= localUTMOrigin_Y_North_;
00723       pointout_z = global_elev - localCSOriginElev_*local_to_meters;
00724       if (localXYZUnit_ == FEET)
00725       {
00726         pointout_x *= METERS_TO_FEET;
00727         pointout_y *= METERS_TO_FEET;
00728         pointout_z *= METERS_TO_FEET;
00729       }
00730       // Transform from compass aligned into local co-ordinates.
00731       inverse_local_transform(pointout_x,pointout_y);
00732       return;
00733     }
00734     else {
00735       vcl_cout << "Error: Local CS " << vpgl_lvcs::cs_name_strings[local_cs_name_]
00736                << " unrecognized." << '\n';
00737       local_lat = local_lon = local_elev = 0.0; // dummy initialisation
00738     }
00739   }
00740   else {
00741     vcl_cout << "Error: Global CS " <<  vpgl_lvcs::cs_name_strings[global_cs_name]
00742              << " unrecognized." << '\n';
00743     local_lat = local_lon = local_elev = 0.0; // dummy initialisation
00744   }
00745 
00746   // Now compute the x, y, z of the point in local vetical CS
00747   //first convert the local_lat to radians and local cs origin to meters
00748   pointout_y =
00749     (local_lat*DEGREES_TO_RADIANS -
00750      localCSOriginLat_*local_to_radians)/lat_scale_;
00751   pointout_x =
00752     (local_lon*DEGREES_TO_RADIANS -
00753      localCSOriginLon_*local_to_radians)/lon_scale_;
00754 
00755   pointout_z = local_elev - localCSOriginElev_*local_to_meters;
00756 
00757   if (localXYZUnit_==FEET)
00758   {
00759     pointout_x *= METERS_TO_FEET;
00760     pointout_y *= METERS_TO_FEET;
00761     pointout_z *= METERS_TO_FEET;
00762   }
00763   // Transform from compass aligned into local co-ordinates.
00764   inverse_local_transform(pointout_x,pointout_y);
00765 
00766 #ifdef LVCS_DEBUG
00767   vcl_cout << "Global " << vpgl_lvcs::cs_name_strings[global_cs_name]
00768            << " [" << pointin_lon << ", " << pointin_lat << ", "  << pointin_z
00769            << "]  MAPS TO Local "
00770            << vpgl_lvcs::cs_name_strings[local_cs_name_]
00771            << " [" << pointout_x << ", " << pointout_lat << ", " << pointout_z << "]\n";
00772 #endif
00773 }
00774 
00775 
00776 //: Print internals on strm.
00777 void vpgl_lvcs::print(vcl_ostream& strm) const
00778 {
00779   vcl_string len_u = "meters", ang_u="degrees";
00780   if (localXYZUnit_ == FEET)
00781     len_u = "feet";
00782   if (geo_angle_unit_ == RADIANS)
00783     ang_u= "radians";
00784   strm << "lvcs [\n"
00785        << "coordinate system name : " << cs_name_strings[local_cs_name_] << '\n'
00786        << "angle unit " << ang_u << '\n'
00787        << "length unit " << len_u << '\n'
00788        << "local origin(lat, lon, elev) : (" <<  localCSOriginLat_ << ' '
00789        << localCSOriginLon_ << ' ' << localCSOriginElev_  << ")\n"
00790        << "scales(lat lon) : (" << lat_scale_ << ' ' << lon_scale_ << ")\n"
00791        << "local transform(lox loy theta) : (" << lox_ << ' ' << loy_
00792        << ' ' << theta_ << ")\n]\n";
00793 }
00794 
00795 //: Read internals from strm.
00796 void vpgl_lvcs::read(vcl_istream& strm)
00797 {
00798   vcl_string len_u = "meters", ang_u="degrees";
00799 
00800   vcl_string local_cs_name_str;
00801   strm >> local_cs_name_str;
00802   if (local_cs_name_str.compare("wgs84") == 0)
00803     local_cs_name_ = wgs84;
00804   else if (local_cs_name_str.compare("nad27n") == 0)
00805     local_cs_name_ = nad27n;
00806   else if (local_cs_name_str.compare("wgs72") == 0)
00807     local_cs_name_ = wgs72;
00808   else if (local_cs_name_str.compare("utm") == 0)
00809     local_cs_name_ = utm;
00810   else
00811     vcl_cerr << "undefined local_cs_name\n";
00812 
00813   strm >> len_u >> ang_u;
00814   if (len_u.compare("feet") == 0)
00815     localXYZUnit_ = FEET;
00816   else if (len_u.compare("meters") == 0)
00817     localXYZUnit_ = METERS;
00818   else
00819     vcl_cerr << "undefined localXYZUnit_ " << len_u << '\n';
00820 
00821   if (ang_u.compare("degrees") == 0)
00822     geo_angle_unit_ = DEG;
00823   else if (ang_u.compare("radians") == 0)
00824     geo_angle_unit_ = RADIANS;
00825   else
00826     vcl_cerr << "undefined geo_angle_unit_ " << ang_u << '\n';
00827 
00828   strm >> localCSOriginLat_ >> localCSOriginLon_ >> localCSOriginElev_;
00829   strm >> lat_scale_ >> lon_scale_;
00830   strm >> lox_ >> loy_ >> theta_;
00831 
00832   if (local_cs_name_ == vpgl_lvcs::utm) {
00833     double local_to_meters, local_to_feet, local_to_radians, local_to_degrees;
00834     this->set_angle_conversions(geo_angle_unit_, local_to_radians,
00835                                 local_to_degrees);
00836     this->set_length_conversions(localXYZUnit_, local_to_meters, local_to_feet);
00837 
00838     //: the origin is still given in wgs84
00839     vpgl_utm u;
00840     u.transform(localCSOriginLat_*local_to_degrees, localCSOriginLon_*local_to_degrees, localUTMOrigin_X_East_, localUTMOrigin_Y_North_, localUTMOrigin_Zone_);
00841     vcl_cout << "utm origin zone: " << localUTMOrigin_Zone_ << ' ' << localUTMOrigin_X_East_ << " East  " << localUTMOrigin_Y_North_ << " North" << vcl_endl;
00842   }
00843 
00844   if (lat_scale_==0.0 && lon_scale_==0.0) {
00845     this->compute_scale();
00846   }
00847 }
00848 
00849 //------------------------------------------------------------
00850 //: Transform from local co-ordinates to north=y,east=x.
00851 void vpgl_lvcs::local_transform(double& x, double& y)
00852 {
00853   double theta=theta_;
00854   if (geo_angle_unit_ == DEG)
00855     theta=theta_*DEGREES_TO_RADIANS;
00856 
00857   // Offset to real origin - ie. the point whose lat/long was given.
00858   double xo = x - lox_;
00859   double yo = y - loy_;
00860 
00861   // Rotate about that point to align y with north.
00862   double ct,st;
00863   if (vcl_fabs(theta) < 1e-5)
00864   {
00865     ct = 1.0;
00866     st = theta;
00867   }
00868   else
00869   {
00870     ct = vcl_cos(-theta);
00871     st = vcl_sin(-theta);
00872   }
00873   x = ct*xo + st*yo;
00874   y = -st*xo + ct*yo;
00875 }
00876 
00877 //------------------------------------------------------------
00878 //: Transform from north=y,east=x aligned axes to local co-ordinates.
00879 void vpgl_lvcs::inverse_local_transform(double& x, double& y)
00880 {
00881   double theta=theta_;
00882   if (geo_angle_unit_ == DEG)
00883     theta=theta_*DEGREES_TO_RADIANS;
00884 
00885   // Rotate about that point to align y with north.
00886   double ct,st;
00887   if (vcl_fabs(theta) < 1e-5)
00888   {
00889     ct = 1.0;
00890     st = theta;
00891   }
00892   else
00893   {
00894     ct = vcl_cos(-theta);
00895     st = vcl_sin(-theta);
00896   }
00897   double xo = ct*x + st*y;
00898   double yo = -st*x + ct*y;
00899 
00900   // Offset to local co-ordinate system origin.
00901   x = xo + lox_;
00902   y = yo + loy_;
00903 }
00904 
00905 vcl_ostream& operator << (vcl_ostream& os, const vpgl_lvcs& local_coord_sys)
00906 {
00907   local_coord_sys.print(os);
00908   return os;
00909 }
00910 
00911 vcl_istream& operator >> (vcl_istream& is, vpgl_lvcs& local_coord_sys)
00912 {
00913   local_coord_sys.read(is);
00914   return is;
00915 }
00916 
00917 bool vpgl_lvcs::operator==(vpgl_lvcs const& r) const
00918 {
00919   bool eq = true;
00920   eq = eq && (this->local_cs_name_ == r.local_cs_name_);
00921   eq = eq && (this->localCSOriginLat_ == r.localCSOriginLat_);
00922   eq = eq && (this->localCSOriginLon_ == r.localCSOriginLon_);
00923   eq = eq && (this->localCSOriginElev_ == r.localCSOriginElev_);
00924   eq = eq && (this->lat_scale_ == r.lat_scale_);
00925   eq = eq && (this->lon_scale_ == r.lon_scale_);
00926   eq = eq && (this->geo_angle_unit_ == r.geo_angle_unit_);
00927   eq = eq && (this->localXYZUnit_ == r.localXYZUnit_);
00928   eq = eq && (this->lox_ == r.lox_);
00929   eq = eq && (this->loy_ == r.loy_);
00930   eq = eq && (this->theta_ == r.theta_);
00931   return eq;
00932 }