core/vpgl/file_formats/vpgl_geo_camera.cxx
Go to the documentation of this file.
00001 // This is core/vpgl/file_formats/vpgl_geo_camera.cxx
00002 #include "vpgl_geo_camera.h"
00003 //:
00004 // \file
00005 
00006 #include <vcl_vector.h>
00007 #include <vcl_cassert.h>
00008 
00009 #include <vnl/vnl_vector.h>
00010 #include <vnl/vnl_inverse.h>
00011 
00012 #include <vpgl/vpgl_lvcs.h>
00013 #include <vpgl/vpgl_utm.h>
00014 
00015 #include <vil/file_formats/vil_geotiff_header.h>
00016 #include <vil/file_formats/vil_tiff.h>
00017 #include <vil/file_formats/vil_nitf2_image.h>
00018 
00019 vpgl_geo_camera::vpgl_geo_camera()
00020 {
00021   trans_matrix_.set_size(4,4);
00022   trans_matrix_.fill(0);
00023   trans_matrix_.fill_diagonal(1);
00024   is_utm = false;
00025   scale_tag_ = false;
00026 }
00027 
00028 vpgl_geo_camera::vpgl_geo_camera(vpgl_geo_camera const& rhs)
00029 {
00030   this->trans_matrix_ = rhs.trans_matrix_;
00031   this->lvcs_ = new vpgl_lvcs(*(rhs.lvcs_));
00032   this->is_utm = rhs.is_utm;
00033   this->utm_zone_ = rhs.utm_zone_;
00034   this->scale_tag_ = rhs.scale_tag_;
00035 }
00036 
00037 bool vpgl_geo_camera::init_geo_camera(vil_image_resource_sptr const geotiff_img,
00038                                       vpgl_lvcs_sptr lvcs,
00039                                       vpgl_geo_camera*& camera)
00040 {
00041   // check if the image is tiff
00042   vil_tiff_image* geotiff_tiff = dynamic_cast<vil_tiff_image*> (geotiff_img.ptr());
00043   if (!geotiff_tiff) {
00044       vcl_cerr << "vpgl_geo_camera::init_geo_camera : Error casting vil_image_resource to a tiff image.\n";
00045       return false;
00046   }
00047   if (!geotiff_tiff->get_geotiff_header()) {
00048     vcl_cerr << "no geotiff header!\n";
00049     return false;
00050   }
00051 
00052   // check if the tiff file is geotiff
00053   if (!geotiff_tiff->is_GEOTIFF()) {
00054     vcl_cerr << "vpgl_geo_camera::init_geo_camera -- The image should be a GEOTIFF!\n";
00055     return false;
00056   }
00057 
00058   vil_geotiff_header* gtif = geotiff_tiff->get_geotiff_header();
00059   int utm_zone;
00060   vil_geotiff_header::GTIF_HEMISPH h;
00061 
00062   vcl_vector<vcl_vector<double> > tiepoints;
00063   gtif->gtif_tiepoints(tiepoints);
00064 
00065   // create a transformation matrix
00066   // if there is a transformation matrix in GEOTIFF, use that
00067   vnl_matrix<double> trans_matrix;
00068   double* trans_matrix_values;
00069   double sx1, sy1, sz1;
00070   bool scale_tag=false;
00071   if (gtif->gtif_trans_matrix(trans_matrix_values)) {
00072     vcl_cout << "Transfer matrix is given, using that...." << vcl_endl;
00073     trans_matrix.copy_in(trans_matrix_values);
00074     vcl_cout << "Warning LIDAR sample spacing different than 1 meter will not be handled correctly!\n";
00075   }
00076   else if (gtif->gtif_pixelscale(sx1, sy1, sz1)) {
00077     scale_tag = true;
00078     vpgl_geo_camera::comp_trans_matrix(sx1, sy1, sz1, tiepoints,
00079                                        trans_matrix, scale_tag);
00080   }
00081   else {
00082     vcl_cout << "vpgl_geo_camera::init_geo_camera comp_trans_matrix -- Transform matrix cannot be formed..\n";
00083     return false;
00084   }
00085 
00086   // create the camera
00087   camera = new vpgl_geo_camera(trans_matrix, lvcs);
00088   camera->set_scale_format(scale_tag);
00089 
00090   // check if the model type is geographic and also the units
00091   if (gtif->GCS_WGS84_MET_DEG())
00092     return true;
00093 
00094   // otherwise check if it is projected to UTM and figure out the zone
00095   if (gtif->PCS_WGS84_UTM_zone(utm_zone, h) || gtif->PCS_NAD83_UTM_zone(utm_zone, h))
00096   {
00097     camera->set_utm(utm_zone, h);
00098     return true;
00099   }
00100   else {
00101     vcl_cout << "vpgl_geo_camera::init_geo_camera()-- if UTM only PCS_WGS84_UTM and PCS_NAD83_UTM, if geographic (GCS_WGS_84) only linear units in meters, angular units in degrees are supported, please define otherwise!" << vcl_endl;
00102     return false;
00103   }
00104 }
00105 
00106 //: transforms a given local 3d world point to image plane
00107 void vpgl_geo_camera::project(const double x, const double y, const double z,
00108                               double& u, double& v) const
00109 {
00110   vnl_vector<double> vec(4), res(4);
00111   double lat, lon, gz;
00112 
00113   if (lvcs_) {
00114     if (lvcs_->get_cs_name() == vpgl_lvcs::utm) {
00115       if (is_utm) {  // geo cam is also utm so keep using utm
00116         double gx, gy;
00117         lvcs_->local_to_global(x, y, z, vpgl_lvcs::utm, gx, gy, gz);
00118         this->global_utm_to_img(gx, gy, utm_zone_, gz, u, v);
00119       }
00120       else {  // geo cam is not utm, convert to wgs84 as global
00121         lvcs_->local_to_global(x, y, z, vpgl_lvcs::wgs84, lon, lat, gz);
00122         this->global_to_img(lon, lat, gz, u, v);
00123       }
00124     }
00125     else {
00126       lvcs_->local_to_global(x, y, z, vpgl_lvcs::wgs84, lon, lat, gz);
00127       this->global_to_img(lon, lat, gz, u, v);
00128     }
00129   }
00130   else // if there is no lvcs, then we assume global coords are given in wgs84, i.e. x is lon and y is lat
00131     this->global_to_img(x, y, z, u, v);
00132 }
00133 
00134 //: backprojects an image point into local coordinates (based on lvcs_)
00135 void vpgl_geo_camera::backproject(const double u, const double v,
00136                                   double& x, double& y, double& z)
00137 {
00138   vnl_vector<double> vec(4), res(4);
00139   if (scale_tag_) {
00140     vec[0] = trans_matrix_[0][3] + trans_matrix_[0][0]*u;
00141     vec[1] = trans_matrix_[1][3] + trans_matrix_[1][1]*v;
00142   }
00143   else { // assumes scale is 1
00144     vec[0] = trans_matrix_[0][3] + u;
00145     vec[1] = trans_matrix_[1][3] - v;
00146   }
00147   vec[2] = 0;
00148   vec[3] = 1;
00149   //vcl_cout << '\n' << vec << vcl_endl;
00150 
00151   double lat, lon, elev;
00152   if (is_utm) {
00153     if (lvcs_) {
00154       if (lvcs_->get_cs_name() == vpgl_lvcs::utm) { // the local cs of lvcs is also utm, so use it directly
00155         lvcs_->global_to_local(vec[0], vec[1], vec[2], vpgl_lvcs::utm, x, y, z);
00156         return;
00157       }
00158     }
00159     //find the UTM values
00160     vpgl_utm utm;
00161     utm.transform(utm_zone_, vec[0], vec[1], vec[2], lat, lon, elev);
00162   }
00163   else {
00164     lat = vec[0];
00165     lon = vec[1];
00166     elev = vec[2];
00167   }
00168 
00169   if (lvcs_)
00170     lvcs_->global_to_local(lon, lat, elev, vpgl_lvcs::wgs84, x, y, z);
00171 }
00172 
00173 void vpgl_geo_camera::translate(double tx, double ty, double z)
00174 {
00175   // use the scale values
00176   if (scale_tag_) {
00177     trans_matrix_[0][3] += tx*trans_matrix_[0][0];
00178     trans_matrix_[1][3] += ty*trans_matrix_[1][1]; // multiplying by -1.0 to get sy
00179   }
00180   else {
00181     vcl_cout << "Warning! Translation offset will only be computed correctly for lidar pixel spacing = 1 meter\n";
00182     trans_matrix_[0][3] += tx;
00183     trans_matrix_[1][3] -= ty;
00184   }
00185 }
00186 
00187 //: returns the corresponding geographical coordinates for a given pixel position (i,j)
00188 //  The output global coord is wgs84
00189 void vpgl_geo_camera::img_to_global(const double i, const double j,
00190                                     double& lon, double& lat) const
00191 {
00192   vnl_vector<double> v(4), res(4);
00193   if (scale_tag_) {
00194     v[0] = trans_matrix_[0][3] + i*trans_matrix_[0][0];
00195     v[1] = trans_matrix_[1][3] + j*trans_matrix_[1][1];
00196   }
00197   else {
00198     v[0] = trans_matrix_[0][3] + i;
00199     v[1] = trans_matrix_[1][3] - j;
00200   }
00201   v[2] = 0;
00202   v[3] = 1;
00203   if (is_utm) {
00204     vpgl_utm utm; double dummy;
00205     utm.transform(utm_zone_, v[0], v[1], v[2], lat, lon, dummy);
00206   }
00207   else {
00208     //lon = v[0]; lat = v[1]; elev = v[2];
00209     lon = v[0]; lat = v[1];
00210   }
00211 }
00212 
00213 //: returns the corresponding pixel position for given geographical coordinates
00214 //  The input global coord is wgs84
00215 void vpgl_geo_camera::global_to_img(const double lon, const double lat, const double gz,
00216                                     double& u, double& v) const
00217 {
00218   vnl_vector<double> vec(4), res(4);
00219   double x1=lon, y1=lat, z1=gz;
00220   if (is_utm) {
00221     vpgl_utm utm;
00222     int utm_zone;
00223     utm.transform(lat, lon, x1, y1, utm_zone);
00224     //vcl_cout << "utm returned x1: " << x1 << " y1: " << y1 << vcl_endl;
00225     //z1 = 0;
00226   }
00227   vec[0] = x1;
00228   vec[1] = y1;
00229   vec[2] = z1;
00230   vec[3] = 1;
00231 
00232   // do we really need this, const does not allow this
00233   vnl_matrix<double> tm(trans_matrix_);
00234   tm[2][2] = 1;
00235 
00236   if (scale_tag_) {
00237     u = (vec[0] - trans_matrix_[0][3])/trans_matrix_[0][0];
00238     v = (vec[1] - trans_matrix_[1][3])/trans_matrix_[1][1];
00239   }
00240   else {
00241     vnl_matrix<double> trans_matrix_inv = vnl_inverse(tm);
00242     res = trans_matrix_inv*vec;
00243     u = res[0];
00244     v = res[1];
00245   }
00246 }
00247 
00248 //: returns the corresponding geographical coordinates for a given pixel position (i,j)
00249 //  The output global coord is UTM: x east, y north
00250 void vpgl_geo_camera::img_to_global_utm(const double i, const double j, double& x, double& y) const
00251 {
00252   vnl_vector<double> v(4), res(4);
00253   if (scale_tag_) {
00254     v[0] = trans_matrix_[0][3] + i*trans_matrix_[0][0];
00255     v[1] = trans_matrix_[1][3] + j*trans_matrix_[1][1];
00256   }
00257   else {
00258     v[0] = trans_matrix_[0][3] + i;
00259     v[1] = trans_matrix_[1][3] - j;
00260   }
00261   v[2] = 0;
00262   v[3] = 1;
00263   if (is_utm) {
00264     x = v[0];
00265     y = v[1];
00266   }
00267   else {  // the trans matrix was using lat,lon coord, transform output to utm
00268     vpgl_utm utm; int dummy_zone;
00269     utm.transform(v[0], v[1], x, y, dummy_zone);
00270   }
00271 }
00272 
00273 //: returns the corresponding pixel position for given geographical coordinates
00274 //  The input global coord is UTM: x east, for y north
00275 void vpgl_geo_camera::global_utm_to_img(const double x, const double y, int zone, double elev, double& u, double& v) const
00276 {
00277   vnl_vector<double> vec(4), res(4);
00278   if (is_utm) {
00279     vec[0] = x;
00280     vec[1] = y;
00281     vec[2] = elev;
00282   }
00283   else {
00284     vpgl_utm utm;
00285     double lat, lon, z;
00286     utm.transform(zone, x, y, elev, lat, lon, z);
00287     vec[0] = lat;
00288     vec[1] = lon;
00289     vec[2] = z;
00290   }
00291   vec[3] = 1;
00292 
00293   // do we really need this, const does not allow this
00294   vnl_matrix<double> tm(trans_matrix_);
00295   tm[2][2] = 1;
00296 
00297   if (scale_tag_) {
00298     u = (vec[0] - trans_matrix_[0][3])/trans_matrix_[0][0];
00299     v = (vec[1] - trans_matrix_[1][3])/trans_matrix_[1][1];
00300   }
00301   else {
00302     vnl_matrix<double> trans_matrix_inv = vnl_inverse(tm);
00303     res = trans_matrix_inv*vec;
00304     u = res[0];
00305     v = res[1];
00306   }
00307 }
00308 
00309 //: returns the corresponding utm location for the given local position
00310 void vpgl_geo_camera::local_to_utm(const double x, const double y, const double z, double& e, double& n, int& utm_zone)
00311 {
00312   double lat, lon, gz;
00313   lvcs_->local_to_global(x,y,z,vpgl_lvcs::wgs84,lon, lat, gz);
00314 
00315   vpgl_utm utm;
00316   utm.transform(lat, lon, e, n, utm_zone);
00317 }
00318 
00319 bool vpgl_geo_camera::img_four_corners_in_utm(const unsigned ni, const unsigned nj, double elev, double& e1, double& n1, double& e2, double& n2)
00320 {
00321   if (!is_utm) {
00322     vcl_cerr << "In vpgl_geo_camera::img_four_corners_in_utm() -- UTM hasn't been set!\n";
00323     return false;
00324   }
00325   double lon,lat;
00326   this->img_to_global(0, 0, lon, lat);
00327   vpgl_utm utm;int utm_zone;
00328   utm.transform(lat, lon, e1, n1, utm_zone);
00329   this->img_to_global(ni, nj, lon, lat);
00330   utm.transform(lat, lon, e2, n2, utm_zone);
00331   return true;
00332 }
00333 
00334 
00335 bool vpgl_geo_camera::operator==(vpgl_geo_camera const& rhs) const
00336 {
00337   return this->trans_matrix_ == rhs.trans_matrix_ &&
00338          *(this->lvcs_) == *(rhs.lvcs_);
00339 }
00340 
00341 //: Write vpgl_perspective_camera to stream
00342 vcl_ostream&  operator<<(vcl_ostream& s,
00343                          vpgl_geo_camera const& p)
00344 {
00345   s << p.trans_matrix_ << '\n'<< *(p.lvcs_) << '\n';
00346   if (p.is_utm) {
00347     s << "geocam is using UTM with zone: " << p.utm_zone_ << '\n';
00348   }
00349 
00350   return s ;
00351 }
00352 
00353 //: Read vpgl_perspective_camera from stream
00354 vcl_istream&  operator>>(vcl_istream& s,
00355                          vpgl_geo_camera& p)
00356 {
00357   vnl_matrix_fixed<double,4,4> tr_matrix;
00358   s >> tr_matrix;
00359   vpgl_lvcs_sptr lvcs = new vpgl_lvcs();
00360   s >> (*lvcs);
00361   p = vpgl_geo_camera(tr_matrix.as_ref(), lvcs);
00362   return s ;
00363 }
00364 
00365 bool vpgl_geo_camera::comp_trans_matrix(double sx1, double sy1, double sz1,
00366                                         vcl_vector<vcl_vector<double> > tiepoints,
00367                                         vnl_matrix<double>& trans_matrix,
00368                                         bool scale_tag)
00369 {
00370   // use tiepoints and scale values to create a transformation matrix
00371   // for now use the first tiepoint if there are more than one
00372   assert (tiepoints.size() > 0);
00373   assert (tiepoints[0].size() == 6);
00374   double I = tiepoints[0][0];
00375   double J = tiepoints[0][1];
00376   double K = tiepoints[0][2];
00377   double X = tiepoints[0][3];
00378   double Y = tiepoints[0][4];
00379   double Z = tiepoints[0][5];
00380 
00381   // Define a transformation matrix as follows:
00382   //
00383   //      |-                         -|
00384   //      |   Sx    0.0   0.0   Tx    |
00385   //      |                           |      Tx = X - I*Sx
00386   //      |   0.0  -Sy    0.0   Ty    |      Ty = Y + J*Sy
00387   //      |                           |      Tz = Z - K*Sz
00388   //      |   0.0   0.0   Sz    Tz    |
00389   //      |                           |
00390   //      |   0.0   0.0   0.0   1.0   |
00391   //      |-                         -|
00392   double sx = 1.0, sy = 1.0, sz = 1.0;
00393   if (scale_tag) {
00394     sx = sx1; sy = sy1; sz = sz1;
00395   }
00396   double Tx = X - I*sx;
00397   double Ty = Y + J*sy;
00398   double Tz = Z - K*sz;
00399 
00400   vnl_matrix<double> m(4,4);
00401   m.fill(0.0);
00402   m[0][0] = sx;
00403   m[1][1] = -1*sy;
00404   m[2][2] = sz;
00405   m[3][3] = 1.0;
00406   m[0][3] = Tx;
00407   m[1][3] = Ty;
00408   m[2][3] = Tz;
00409   trans_matrix = m;
00410   vcl_cout << trans_matrix << vcl_endl;
00411   return true;
00412 }
00413 
00414 void vpgl_geo_camera::img_to_wgs(unsigned i, unsigned j, unsigned k, double& lon, double& lat, double& elev)
00415 {
00416   assert(!"Not yet implemented");
00417 }