USGS

Isis 3.0 Object Programmers' Reference

Home

Camera.cpp

Go to the documentation of this file.
00001 
00023 #include <cfloat>
00024 #include <cmath>
00025 #include "Camera.h"
00026 #include "Projection.h"
00027 #include "Constants.h"
00028 #include "CameraDetectorMap.h"
00029 #include "CameraFocalPlaneMap.h"
00030 #include "CameraDistortionMap.h"
00031 #include "CameraGroundMap.h"
00032 #include "CameraSkyMap.h"
00033 #include "ProjectionFactory.h"
00034 #include "NaifStatus.h"
00035 
00036 using namespace std;
00037 namespace Isis {
00043   Camera::Camera (Isis::Pvl &lab) : Isis::Sensor (lab) {
00044     // Get the image size which can be different than the alpha cube size
00045     Isis::PvlGroup &dims = lab.FindObject("IsisCube")
00046                             .FindObject("Core")
00047                             .FindGroup("Dimensions");
00048     p_lines = dims["Lines"];
00049     p_samples = dims["Samples"];
00050     p_bands = dims["Bands"];
00051 
00052     SetGeometricTilingHint();
00053 
00054     // Get the AlphaCube information
00055     p_alphaCube = new Isis::AlphaCube(lab);
00056 
00057     // Get the projection group if it exists
00058     if (lab.FindObject("IsisCube").HasGroup("Mapping")) {
00059       p_projection = ProjectionFactory::CreateFromCube(lab);
00060     }
00061     else {
00062       p_projection = NULL;
00063     }
00064     p_ignoreProjection = false;
00065 
00066     // Initialize stuff
00067     p_focalLength = 0.0;
00068     p_pixelPitch = 1.0;
00069     p_referenceBand = 0;
00070     p_childBand = 1;
00071 
00072     p_distortionMap = NULL;
00073     p_focalPlaneMap = NULL;
00074     p_detectorMap = NULL;
00075     p_groundMap = NULL;
00076     p_skyMap = NULL;
00077 
00078     // See if we have a reference band
00079     Isis::PvlGroup &inst = lab.FindObject("IsisCube").FindGroup("Instrument");
00080     if (inst.HasKeyword("ReferenceBand")) {
00081       p_referenceBand = inst["ReferenceBand"];
00082     }
00083 
00084     p_groundRangeComputed = false;
00085     p_raDecRangeComputed = false;
00086     p_pointComputed = false;
00087   }
00088 
00090   Camera::~Camera () {
00091     if (p_projection) {
00092       delete p_projection;
00093       p_projection = NULL;
00094     }
00095 
00096     if(p_alphaCube) {
00097       delete p_alphaCube;
00098       p_alphaCube = NULL;
00099     }
00100 
00101     if(p_distortionMap) {
00102       delete p_distortionMap;
00103       p_distortionMap = NULL;
00104     }
00105 
00106     if(p_focalPlaneMap) {
00107       delete p_focalPlaneMap;
00108       p_focalPlaneMap = NULL;
00109     }
00110 
00111     if(p_detectorMap) {
00112       delete p_detectorMap;
00113       p_detectorMap = NULL;
00114     }
00115 
00116     if(p_groundMap) {
00117       delete p_groundMap;
00118       p_groundMap = NULL;
00119     }
00120 
00121     if(p_skyMap) {
00122       delete p_skyMap;
00123       p_skyMap = NULL;
00124     }
00125   }
00126 
00137   bool Camera::SetImage (const double sample, const double line) {
00138     p_childSample = sample;
00139     p_childLine = line;
00140     p_pointComputed = true;
00141 
00142     // Case of no map projection
00143     if (p_projection == NULL || p_ignoreProjection) {
00144       // Convert to parent coordinate (remove crop, pad, shrink, enlarge)
00145       double parentSample = p_alphaCube->AlphaSample(sample);
00146       double parentLine = p_alphaCube->AlphaLine(line);
00147       // Convert from parent to detector
00148       if (p_detectorMap->SetParent(parentSample,parentLine)) {
00149         double detectorSample = p_detectorMap->DetectorSample();
00150         double detectorLine   = p_detectorMap->DetectorLine();
00151         // Now Convert from detector to distorted focal plane
00152         if (p_focalPlaneMap->SetDetector(detectorSample,detectorLine)) {
00153           double focalPlaneX = p_focalPlaneMap->FocalPlaneX();
00154           double focalPlaneY = p_focalPlaneMap->FocalPlaneY();
00155           // Remove optical distortion
00156           if (p_distortionMap->SetFocalPlane(focalPlaneX,focalPlaneY)) {
00157             // Map to the ground
00158             double x = p_distortionMap->UndistortedFocalPlaneX();
00159             double y = p_distortionMap->UndistortedFocalPlaneY();
00160             double z = p_distortionMap->UndistortedFocalPlaneZ();
00161             p_hasIntersection =  p_groundMap->SetFocalPlane(x,y,z);
00162             return p_hasIntersection;
00163           }
00164         }
00165       }
00166     }
00167 
00168     // The projection is a sky map
00169     else if (p_projection->IsSky()) {
00170       if (p_projection->SetWorld(sample,line)) {
00171         if (SetRightAscensionDeclination(p_projection->Longitude(),
00172                                          p_projection->UniversalLatitude())) {
00173           p_childSample = sample;
00174           p_childLine = line;
00175 
00176           return HasSurfaceIntersection();
00177         }
00178       }
00179     }
00180 
00181     // We have map projected camera model
00182     else {
00183       if (p_projection->SetWorld(sample,line)) {
00184         if (SetUniversalGround(p_projection->UniversalLatitude(),
00185                                p_projection->UniversalLongitude())) {
00186           p_childSample = sample;
00187           p_childLine = line;
00188 
00189           p_hasIntersection = true;
00190           return p_hasIntersection;
00191         }
00192       }
00193     }
00194 
00195     // failure
00196     p_hasIntersection = false;
00197     return p_hasIntersection;
00198   }
00199 
00209   bool Camera::SetUniversalGround (const double latitude, const double longitude) {
00210     // Convert lat/lon to undistorted focal plane x/y
00211     if (p_groundMap->SetGround(latitude,longitude)) {
00212       return RawFocalPlanetoImage();
00213     }
00214 
00215     p_hasIntersection = false;
00216     return p_hasIntersection;
00217   }
00218 
00219 
00220 
00228   bool Camera::RawFocalPlanetoImage () {
00229     double ux = p_groundMap->FocalPlaneX();
00230     double uy = p_groundMap->FocalPlaneY();
00231     // Convert undistorted x/y to distorted x/y
00232     if (p_distortionMap->SetUndistortedFocalPlane(ux,uy)) {
00233       double focalPlaneX = p_distortionMap->FocalPlaneX();
00234       double focalPlaneY = p_distortionMap->FocalPlaneY();
00235        // Convert distorted x/y to detector position
00236        if (p_focalPlaneMap->SetFocalPlane(focalPlaneX,focalPlaneY)) {
00237          double detectorSample = p_focalPlaneMap->DetectorSample();
00238          double detectorLine = p_focalPlaneMap->DetectorLine();
00239          // Convert detector to parent position
00240          if (p_detectorMap->SetDetector(detectorSample,detectorLine)) {
00241            double parentSample = p_detectorMap->ParentSample();
00242            double parentLine = p_detectorMap->ParentLine();
00243            p_pointComputed = true;
00244 
00245            if (p_projection == NULL || p_ignoreProjection) {
00246              p_childSample = p_alphaCube->BetaSample(parentSample);
00247              p_childLine = p_alphaCube->BetaLine(parentLine);
00248              p_hasIntersection = true;
00249              return p_hasIntersection;
00250            }
00251            else if (p_projection->IsSky()) {
00252              if (p_projection->SetGround(Declination(),RightAscension())) {
00253                p_childSample = p_projection->WorldX();
00254                p_childLine = p_projection->WorldY();
00255                p_hasIntersection = true;
00256                return p_hasIntersection;
00257              }
00258            }
00259            else {
00260              if (p_projection->SetUniversalGround(UniversalLatitude(), UniversalLongitude())) {
00261                p_childSample = p_projection->WorldX();
00262                p_childLine = p_projection->WorldY();
00263                p_hasIntersection = true;
00264                return p_hasIntersection;
00265              }
00266            }
00267          }
00268        }
00269     }
00270 
00271     p_hasIntersection = false;
00272     return p_hasIntersection;
00273   }
00274 
00275 
00276 
00289   bool Camera::SetUniversalGround (const double latitude, const double longitude,
00290                                    const double radius) {
00291     // Convert lat/lon to undistorted focal plane x/y
00292     if (p_groundMap->SetGround(latitude,longitude,radius)) {
00293       return RawFocalPlanetoImage();  // sets p_hasIntersection
00294     }
00295 
00296     p_hasIntersection = false;
00297     return p_hasIntersection;
00298   }
00299 
00305   double Camera::DetectorResolution () {
00306     if (HasSurfaceIntersection()) {
00307       double sB[3];
00308       InstrumentPosition(sB);
00309       double pB[3];
00310       Coordinate(pB);
00311       double a = sB[0] - pB[0];
00312       double b = sB[1] - pB[1];
00313       double c = sB[2] - pB[2];
00314       double dist = sqrt(a*a + b*b + c*c) * 1000.0;
00315       return dist / (p_focalLength / p_pixelPitch);
00316     }
00317     return -1.0;
00318   }
00319 
00325   double Camera::SampleResolution () {
00326     return DetectorResolution() * p_detectorMap->SampleScaleFactor();
00327   }
00328 
00334   double Camera::LineResolution () {
00335     return DetectorResolution() * p_detectorMap->LineScaleFactor();
00336   }
00337 
00343   double Camera::PixelResolution () {
00344     double lineRes = LineResolution();
00345     double sampRes = SampleResolution();
00346     if (lineRes < 0.0) return -1.0;
00347     if (sampRes < 0.0) return -1.0;
00348     return (lineRes + sampRes) / 2.0;
00349   }
00350 
00356   double Camera::LowestImageResolution () {
00357     GroundRangeResolution();
00358     return p_maxres;
00359   }
00360 
00366   double Camera::HighestImageResolution () {
00367     GroundRangeResolution();
00368     return p_minres;
00369   }
00370 
00374   void Camera::GroundRangeResolution () {
00375     // Have we already done this
00376     if (p_groundRangeComputed) return;
00377     p_groundRangeComputed = true;
00378 
00379     bool computed = p_pointComputed;
00380     double originalSample = Sample();
00381     double originalLine = Line();
00382     int originalBand = Band();
00383 
00384     // Initializations
00385     p_minlat    = DBL_MAX;
00386     p_minlon    = DBL_MAX;
00387     p_minlon180 = DBL_MAX;
00388     p_maxlat    = -DBL_MAX;
00389     p_maxlon    = -DBL_MAX;
00390     p_maxlon180 = -DBL_MAX;
00391     p_minres    = DBL_MAX;
00392     p_maxres    = -DBL_MAX;
00393 
00394     // See if we have band dependence and loop for the appropriate number of bands
00395     int eband = p_bands;
00396     if (IsBandIndependent()) eband = 1;
00397     for (int band=1; band<=eband; band++) {
00398       SetBand(band);
00399 
00400       // Loop for each line testing the left and right sides of the image
00401       for (int line=1; line<=p_lines+1; line++) {
00402         // Look for the first good lat/lon on the left edge of the image
00403         // If it is the first or last line then test the whole line
00404         int samp;
00405         for (samp=1; samp<=p_samples+1; samp++) {
00406 
00407           if (SetImage((double)samp-0.5,(double)line-0.5)) {
00408             double lat = UniversalLatitude();
00409             double lon = UniversalLongitude();
00410             if (lat < p_minlat) p_minlat = lat;
00411             if (lat > p_maxlat) p_maxlat = lat;
00412             if (lon < p_minlon) p_minlon = lon;
00413             if (lon > p_maxlon) p_maxlon = lon;
00414 
00415             if (lon > 180.0) lon -= 360.0;
00416             if (lon < p_minlon180) p_minlon180 = lon;
00417             if (lon > p_maxlon180) p_maxlon180 = lon;
00418 
00419             double res = PixelResolution();
00420             if (res > 0.0) {
00421               if (res < p_minres) p_minres = res;
00422               if (res > p_maxres) p_maxres = res;
00423             }
00424             if ((line != 1) && (line != p_lines+1)) break;
00425           }
00426         }
00427 
00428         //We've already checked the first and last lines.
00429         if(line == 1) continue;
00430         if(line == p_lines+1) continue;
00431 
00432         // Look for the first good lat/lon on the right edge of the image
00433         if (samp < p_samples+1) {
00434           for (samp=p_samples+1; samp>=1; samp--) {
00435             if (SetImage((double)samp-0.5,(double)line-0.5)) {
00436               double lat = UniversalLatitude();
00437               double lon = UniversalLongitude();
00438               if (lat < p_minlat) p_minlat = lat;
00439               if (lat > p_maxlat) p_maxlat = lat;
00440               if (lon < p_minlon) p_minlon = lon;
00441               if (lon > p_maxlon) p_maxlon = lon;
00442 
00443               if (lon > 180.0) lon -= 360.0;
00444               if (lon < p_minlon180) p_minlon180 = lon;
00445               if (lon > p_maxlon180) p_maxlon180 = lon;
00446 
00447               double res = PixelResolution();
00448               if (res > 0.0) {
00449                 if (res < p_minres) p_minres = res;
00450                 if (res > p_maxres) p_maxres = res;
00451               }
00452               break;
00453             }
00454           }
00455         }
00456       }
00457 
00458       // Test at the sub-spacecraft point to see if we have a
00459       // better resolution
00460       double lat,lon;
00461       SubSpacecraftPoint(lat,lon);
00462       if (SetUniversalGround(lat,lon)) {
00463         if (Sample() >= 0.5 && Line() >= 0.5 &&
00464             Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
00465           double res = PixelResolution();
00466           if (res > 0.0) {
00467             if (res < p_minres) p_minres = res;
00468             if (res > p_maxres) p_maxres = res;
00469           }
00470         }
00471       }
00472 
00473       // Special test for ground range to see if either pole is in the image
00474       if (SetUniversalGround(90.0,0.0)) {
00475         if (Sample() >= 0.5 && Line() >= 0.5 &&
00476             Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
00477           p_maxlat = 90.0;
00478           p_minlon = 0.0;
00479           p_maxlon = 360.0;
00480           p_minlon180 = -180.0;
00481           p_maxlon180 = 180.0;
00482         }
00483       }
00484 
00485       if (SetUniversalGround(-90.0,0.0)) {
00486         if (Sample() >= 0.5 && Line() >= 0.5 &&
00487             Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
00488           p_minlat = -90.0;
00489           p_minlon = 0.0;
00490           p_maxlon = 360.0;
00491           p_minlon180 = -180.0;
00492           p_maxlon180 = 180.0;
00493         }
00494       }
00495 
00496       // Another special test for ground range as we could have the
00497       // 0-360 seam running right through the image so
00498       // test it as well (the increment may not be fine enough !!!)
00499       for (double lat=p_minlat; lat<=p_maxlat; lat+=(p_maxlat-p_minlat)/10.0) {
00500         if (SetUniversalGround(lat,0.0)) {
00501           if (Sample() >= 0.5 && Line() >= 0.5 &&
00502               Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
00503             p_minlon = 0.0;
00504             p_maxlon = 360.0;
00505             break;
00506           }
00507         }
00508       }
00509 
00510       // Another special test for ground range as we could have the
00511       // -180-180 seam running right through the image so
00512       // test it as well (the increment may not be fine enough !!!)
00513       for (double lat=p_minlat; lat<=p_maxlat; lat+=(p_maxlat-p_minlat)/10.0) {
00514         if (SetUniversalGround(lat,180.0)) {
00515           if (Sample() >= 0.5 && Line() >= 0.5 &&
00516               Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
00517             p_minlon180 = -180.0;
00518             p_maxlon180 = 180.0;
00519             break;
00520           }
00521         }
00522       }
00523     }
00524 
00525     SetBand(originalBand);
00526 
00527     if(computed) {
00528       SetImage(originalSample, originalLine);
00529     }
00530     else {
00531       p_pointComputed = false;
00532     }
00533 
00534     // Checks for invalide lat/lon ranges
00535     if ( p_minlon == DBL_MAX  ||  p_minlat == DBL_MAX  ||  p_maxlon == -DBL_MAX  ||  p_maxlat == -DBL_MAX ) {
00536       string message = "Camera missed planet or SPICE data off.";
00537       throw iException::Message( iException::Camera, message, _FILEINFO_ );
00538     }
00539   }
00540 
00549   bool Camera::IntersectsLongitudeDomain (Isis::Pvl &pvl) {
00550     double minlat, minlon, maxlat, maxlon;
00551     return GroundRange(minlat,maxlat,minlon,maxlon,pvl);
00552   }
00553 
00570   bool Camera::GroundRange (double &minlat, double &maxlat,
00571                                 double &minlon, double &maxlon,
00572                                 Isis::Pvl &pvl) {
00573     // Compute the ground range and resolution
00574     GroundRangeResolution();
00575 
00576     // Get the default radii
00577     double radii[3];
00578     Radii(radii);
00579     double a = radii[0] * 1000.0;
00580     double b = radii[2] * 1000.0;
00581 
00582     // See if the PVL overrides the radii
00583     Isis::PvlGroup map = pvl.FindGroup("Mapping",Isis::Pvl::Traverse);
00584     if (map.HasKeyword("EquatorialRadius")) a = map["EquatorialRadius"];
00585     if (map.HasKeyword("PolarRadius")) b = map["PolarRadius"];
00586 
00587     // Convert to planetographic if necessary
00588     minlat = p_minlat;
00589     maxlat = p_maxlat;
00590     if (map.HasKeyword("LatitudeType")) {
00591       Isis::iString latType = (string) map["LatitudeType"];
00592       if (latType.UpCase() == "PLANETOGRAPHIC") {
00593         if (abs(minlat) < 90.0) { // So tan doesn't fail
00594           minlat *= Isis::PI / 180.0;
00595           minlat = atan (tan(minlat) * (a / b) * (a / b));
00596           minlat *= 180.0 / Isis::PI;
00597         }
00598 
00599         if (abs(maxlat) < 90.0) { // So tan doesn't fail
00600           maxlat *= Isis::PI / 180.0;
00601           maxlat = atan (tan(maxlat) * (a / b) * (a / b));
00602           maxlat *= 180.0 / Isis::PI;
00603         }
00604       }
00605     }
00606 
00607     // Assume 0 to 360 domain but change it if necessary
00608     minlon = p_minlon;
00609     maxlon = p_maxlon;
00610     bool domain360 = true;
00611     if (map.HasKeyword("LongitudeDomain")) {
00612       Isis::iString lonDomain = (string) map["LongitudeDomain"];
00613       if (lonDomain.UpCase() == "180") {
00614         minlon = p_minlon180;
00615         maxlon = p_maxlon180;
00616         domain360 = false;
00617       }
00618     }
00619 
00620     // Convert to the proper longitude direction
00621     if (map.HasKeyword("LongitudeDirection")) {
00622       Isis::iString lonDirection = (string) map["LongitudeDirection"];
00623       if (lonDirection.UpCase() == "POSITIVEWEST") {
00624         double swap = minlon;
00625         minlon = -maxlon;
00626         maxlon = -swap;
00627       }
00628     }
00629 
00630     // Convert to the proper longitude domain
00631     if (domain360) {
00632       while (minlon < 0.0) {
00633         minlon += 360.0;
00634         maxlon += 360.0;
00635       }
00636       while (minlon > 360.0) {
00637         minlon -= 360.0;
00638         maxlon -= 360.0;
00639       }
00640     }
00641     else {
00642       while (minlon < -180.0) {
00643         minlon += 360.0;
00644         maxlon += 360.0;
00645       }
00646       while (minlon > 180.0) {
00647         minlon -= 360.0;
00648         maxlon -= 360.0;
00649       }
00650     }
00651     // Now return if it crosses the longitude domain boundary
00652     if ((maxlon - minlon) > 359.0) return true;
00653     return false;
00654   }
00655 
00661   void Camera::BasicMapping (Isis::Pvl &pvl) {
00662     Isis::PvlGroup map("Mapping");
00663     map += Isis::PvlKeyword("TargetName",Target());
00664     map += Isis::PvlKeyword("EquatorialRadius",p_radii[0]*1000.0,"<meters>");
00665     map += Isis::PvlKeyword("PolarRadius",p_radii[2]*1000.0,"<meters>");
00666     map += Isis::PvlKeyword("LatitudeType","Planetocentric");
00667     map += Isis::PvlKeyword("LongitudeDirection","PositiveEast");
00668     map += Isis::PvlKeyword("LongitudeDomain","360");
00669 
00670     GroundRangeResolution();
00671     map += Isis::PvlKeyword("MinimumLatitude",p_minlat);
00672     map += Isis::PvlKeyword("MaximumLatitude",p_maxlat);
00673     map += Isis::PvlKeyword("MinimumLongitude",p_minlon);
00674     map += Isis::PvlKeyword("MaximumLongitude",p_maxlon);
00675     map += Isis::PvlKeyword("PixelResolution",p_minres);
00676 
00677     map += Isis::PvlKeyword("ProjectionName","Sinusoidal");
00678     pvl.AddGroup(map);
00679   }
00680 
00682   void Camera::SetFocalLength () {
00683     int code = NaifIkCode();
00684     string key = "INS" + Isis::iString(code) + "_FOCAL_LENGTH";
00685     SetFocalLength(Isis::Spice::GetDouble(key));
00686   }
00687 
00689   void Camera::SetPixelPitch () {
00690     int code = NaifIkCode();
00691     string key = "INS" + Isis::iString(code) + "_PIXEL_PITCH";
00692     SetPixelPitch(Isis::Spice::GetDouble(key));
00693   }
00694 
00705   bool Camera::SetRightAscensionDeclination(const double ra, const double dec) {
00706     if (p_skyMap->SetSky(ra,dec)) {
00707       double ux = p_skyMap->FocalPlaneX();
00708       double uy = p_skyMap->FocalPlaneY();
00709       if (p_distortionMap->SetUndistortedFocalPlane(ux,uy)) {
00710         double dx = p_distortionMap->FocalPlaneX();
00711         double dy = p_distortionMap->FocalPlaneY();
00712         if (p_focalPlaneMap->SetFocalPlane(dx,dy)) {
00713           double detectorSamp = p_focalPlaneMap->DetectorSample();
00714           double detectorLine = p_focalPlaneMap->DetectorLine();
00715           if (p_detectorMap->SetDetector(detectorSamp,detectorLine)) {
00716             double parentSample = p_detectorMap->ParentSample();
00717             double parentLine = p_detectorMap->ParentLine();
00718             p_pointComputed = true;
00719 
00720             if (p_projection == NULL || p_ignoreProjection) {
00721               p_childSample = p_alphaCube->BetaSample(parentSample);
00722               p_childLine = p_alphaCube->BetaLine(parentLine);
00723               return true;
00724             }
00725             else if (p_projection->IsSky()) {
00726               if (p_projection->SetGround(dec,ra)) {
00727                 p_childSample = p_projection->WorldX();
00728                 p_childLine = p_projection->WorldY();
00729                 return true;
00730               }
00731             }
00732             else if (p_hasIntersection) {
00733               if (p_projection->SetUniversalGround(UniversalLatitude(),
00734                                                    UniversalLongitude())) {
00735                 p_childSample = p_projection->WorldX();
00736                 p_childLine = p_projection->WorldY();
00737                 return true;
00738               }
00739             }
00740           }
00741         }
00742       }
00743     }
00744 
00745     return false;
00746   }
00747 
00762   bool Camera::RaDecRange (double &minra, double &maxra,
00763                            double &mindec, double &maxdec) {
00764     if(p_projection != NULL && !p_projection->IsSky()) {
00765       iString msg = "Camera::RaDecRange can not calculate a right ascension, declination range";
00766       msg += " for projected images which are not projected to sky";
00767       throw iException::Message(iException::Programmer, msg, _FILEINFO_);
00768     }
00769 
00770     bool computed = p_pointComputed;
00771     double originalSample = Sample();
00772     double originalLine = Line();
00773     int originalBand = Band();
00774 
00775     // Have we already done this
00776     if (!p_raDecRangeComputed) {
00777       p_raDecRangeComputed = true;
00778 
00779       // Initializations
00780       p_mindec    = DBL_MAX;
00781       p_minra     = DBL_MAX;
00782       p_minra180  = DBL_MAX;
00783       p_maxdec    = -DBL_MAX;
00784       p_maxra     = -DBL_MAX;
00785       p_maxra180  = -DBL_MAX;
00786 
00787       // See if we have band dependence and loop for the appropriate number of bands
00788       int eband = p_bands;
00789       if (IsBandIndependent()) eband = 1;
00790       for (int band=1; band<=eband; band++) {
00791         this->SetBand(band);
00792 
00793         for (int line=1; line<=p_lines; line++) {
00794           // Test left, top, and bottom sides
00795           int samp;
00796           for (samp=1; samp<=p_samples; samp++) {
00797             SetImage((double)samp,(double)line);
00798             double ra = RightAscension();
00799             double dec = Declination();
00800             if (ra < p_minra) p_minra = ra;
00801             if (ra > p_maxra) p_maxra = ra;
00802             if (dec < p_mindec) p_mindec = dec;
00803             if (dec > p_maxdec) p_maxdec = dec;
00804 
00805             if (ra > 180.0) ra -= 360.0;
00806             if (ra < p_minra180) p_minra180 = ra;
00807             if (ra > p_maxra180) p_maxra180 = ra;
00808 
00809             if ((line != 1) && (line != p_lines)) break;
00810           }
00811 
00812           // Test right side
00813           if (samp < p_samples) {
00814             for (samp=p_samples; samp>=1; samp--) {
00815               SetImage((double)samp,(double)line);
00816               double ra = RightAscension();
00817               double dec = Declination();
00818               if (ra < p_minra) p_minra = ra;
00819               if (ra > p_maxra) p_maxra = ra;
00820               if (dec < p_mindec) p_mindec = dec;
00821               if (dec > p_maxdec) p_maxdec = dec;
00822 
00823               if (ra > 180.0) ra -= 360.0;
00824               if (ra < p_minra180) p_minra180 = ra;
00825               if (ra > p_maxra180) p_maxra180 = ra;
00826 
00827               break;
00828             }
00829           }
00830         }
00831 
00832         // Special test for ground range to see if either pole is in the image
00833         if (SetRightAscensionDeclination(0.0,90.0)) {
00834           if ((Line() >= 0.5) && (Line() <= p_lines) &&
00835               (Sample() >= 0.5) && (Sample() <= p_samples)) {
00836             p_maxdec = 90.0;
00837             p_minra = 0.0;
00838             p_maxra = 360.0;
00839             p_minra180 = -180.0;
00840             p_maxra180 = 180.0;
00841           }
00842         }
00843 
00844         if (SetRightAscensionDeclination(0.0,-90.0)) {
00845           if ((Line() >= 0.5) && (Line() <= p_lines) &&
00846               (Sample() >= 0.5) && (Sample() <= p_samples)) {
00847             p_mindec = -90.0;
00848             p_minra = 0.0;
00849             p_maxra = 360.0;
00850             p_minra180 = -180.0;
00851             p_maxra180 = 180.0;
00852           }
00853         }
00854 
00855         // Another special test for ground range as we could have the
00856         // 0-360 seam running right through the image so
00857         // test it as well (the increment may not be fine enough !!!)
00858         for (double dec=p_mindec; dec<=p_maxdec; dec+=(p_maxdec-p_mindec)/10.0) {
00859           if (SetRightAscensionDeclination(0.0,dec)) {
00860             if ((Line() >= 0.5) && (Line() <= p_lines) &&
00861                 (Sample() >= 0.5) && (Sample() <= p_samples)) {
00862               p_minra = 0.0;
00863               p_maxra = 360.0;
00864               break;
00865             }
00866           }
00867         }
00868 
00869         // Another special test for ground range as we could have the
00870         // 0-360 seam running right through the image so
00871         // test it as well (the increment may not be fine enough !!!)
00872         for (double dec=p_mindec; dec<=p_maxdec; dec+=(p_maxdec-p_mindec)/10.0) {
00873           if (SetRightAscensionDeclination(180.0,dec)) {
00874             if ((Line() >= 0.5) && (Line() <= p_lines) &&
00875                 (Sample() >= 0.5) && (Sample() <= p_samples)) {
00876               p_minra180 = -180.0;
00877               p_maxra180 = 180.0;
00878               break;
00879             }
00880           }
00881         }
00882       }
00883     }
00884 
00885     minra = p_minra;
00886     maxra = p_maxra;
00887     mindec = p_mindec;
00888     maxdec = p_maxdec;
00889 
00890     SetBand(originalBand);
00891 
00892     if(computed) {
00893       SetImage(originalSample, originalLine);
00894     }
00895     else {
00896       p_pointComputed = false;
00897     }
00898 
00899     return true;
00900   }
00901 
00902 
00909   double Camera::RaDecResolution () {
00910     if(p_projection != NULL && !p_projection->IsSky()) {
00911       iString msg = "Camera::RaDecResolution can not calculate a right ascension, declination resolution";
00912       msg += " for projected images which are not projected to sky";
00913       throw iException::Message(iException::Programmer, msg, _FILEINFO_);
00914     }
00915 
00916     bool computed = p_pointComputed;
00917     double originalSample = Sample();
00918     double originalLine = Line();
00919     int originalBand = Band();
00920 
00921     SetImage(1.0,1.0);
00922     double ra1 = RightAscension();
00923     double dec1 = Declination();
00924 
00925     SetImage(1.0,(double)p_lines);
00926     double ra2 = RightAscension();
00927     double dec2 = Declination();
00928 
00929     double dist = (ra1 - ra2) * (ra1 - ra2) + (dec1 - dec2) * (dec1 - dec2);
00930     dist = sqrt(dist);
00931     double lineRes = dist / (p_lines - 1);
00932 
00933     SetImage((double)p_samples,1.0);
00934     ra2 = RightAscension();
00935     dec2 = Declination();
00936 
00937     dist = (ra1 - ra2) * (ra1 - ra2) + (dec1 - dec2) * (dec1 - dec2);
00938     dist = sqrt(dist);
00939     double sampRes = dist / (p_samples - 1);
00940 
00941     SetBand(originalBand);
00942 
00943     if(computed) {
00944       SetImage(originalSample, originalLine);
00945     }
00946     else {
00947       p_pointComputed = false;
00948     }
00949 
00950     return (sampRes < lineRes) ? sampRes : lineRes;
00951   }
00952 
00958   double Camera::NorthAzimuth () {
00959     return ComputeAzimuth(p_radii[2],90.0,0.0);
00960   }
00961 
00969   double Camera::SunAzimuth () {
00970     double lat,lon;
00971     SubSolarPoint (lat,lon);
00972     return ComputeAzimuth(LocalRadius()/1000.0,lat,lon);
00973   }
00974 
00982   double Camera::SpacecraftAzimuth () {
00983     double lat,lon;
00984     SubSpacecraftPoint (lat,lon);
00985     return ComputeAzimuth(LocalRadius()/1000.0,lat,lon);
00986   }
00987 
01002   double Camera::ComputeAzimuth(const double radius,
01003                                 const double lat, const double lon) {
01004     if (!HasSurfaceIntersection()) return -1.0;
01005 
01006     bool computed = p_pointComputed;
01007     double originalSample = Sample();
01008     double originalLine = Line();
01009 
01010     NaifStatus::CheckErrors();
01011 
01012     // Convert the point to x/y/z in body-fixed
01013     SpiceDouble pB[3];
01014     latrec_c(radius,lon*Isis::PI/180.0,lat*Isis::PI/180.0,pB);
01015 
01016     // Get the origin point
01017     SpiceDouble oB[3];
01018     Coordinate(oB);
01019 
01020     // Get the difference unit vector
01021     SpiceDouble poB[3],upoB[3];
01022     vsub_c(pB,oB,poB);
01023     vhat_c(poB,upoB);
01024 
01025     // Scale to be within a pixel (km)
01026     double scale = (PixelResolution() / 1000.0) / 2.0;
01027     SpiceDouble supoB[3];
01028     vscl_c(scale,upoB,supoB);
01029 
01030     // Compute the new point in body fixed.  This point will be within
01031     // a pixel of the origin but in the same direction as the
01032     // requested lat/lon
01033     SpiceDouble nB[3];
01034     vadd_c(oB,supoB,nB);
01035 
01036     // However, it could be below the surface of the planet so bring it
01037     // back
01038     vhat_c(nB,nB);
01039     vscl_c(LocalRadius()/1000.0,nB,nB);
01040 
01041     // Get the origin image coordinate
01042     double osample = Sample();
01043     double oline = Line();
01044 
01045     // Convert the point to a lat/lon and find out its image coordinate
01046     double nrad,nlon,nlat;
01047     reclat_c(nB,&nrad,&nlon,&nlat);
01048     SetUniversalGround(nlat*180.0/Isis::PI,nlon*180.0/Isis::PI);
01049     double nsample = Sample();
01050     double nline = Line();
01051 
01052     // TODO:  Write PushState and PopState method to ensure the
01053     // internals of the class are set based on SetImage or SetGround
01054     SetImage(osample,oline);
01055 
01056     double deltaSample = nsample - osample;
01057     double deltaLine = nline - oline;
01058 
01059     // Compute the angle
01060     double azimuth = 0.0;
01061     if (deltaSample != 0.0 || deltaLine != 0.0) {
01062       azimuth = atan2(deltaLine,deltaSample);
01063       azimuth *= 180.0 / Isis::PI;
01064     }
01065     if (azimuth < 0.0) azimuth += 360.0;
01066     if (azimuth > 360.0) azimuth -= 360.0;
01067 
01068     NaifStatus::CheckErrors();
01069 
01070     if(computed) {
01071       SetImage(originalSample, originalLine);
01072     }
01073     else {
01074       p_pointComputed = false;
01075     }
01076 
01077     return azimuth;
01078   }
01079 
01085   double Camera::OffNadirAngle () {
01086     NaifStatus::CheckErrors();
01087 
01088     // Get the xyz coordinates for the spacecraft and point we are interested in
01089     double coord[3],spCoord[3];
01090     Coordinate(coord);
01091     InstrumentPosition(spCoord);
01092 
01093     // Get the angle between the 2 points and convert to degrees
01094     double a = vsep_c(coord,spCoord) * 180.0 / Isis::PI;
01095     double b = 180.0 - EmissionAngle();
01096 
01097     // The three angles in a triangle must add up to 180 degrees
01098     double c = 180.0 - (a + b);
01099 
01100     NaifStatus::CheckErrors();
01101 
01102     return c;
01103   }
01104 
01120   double Camera::GroundAzimuth(double glat, double glon, 
01121                                double slat, double slon) {
01122     double a = (90.0 - slat) * Isis::PI / 180.0;
01123     double b = (90.0 - glat) * Isis::PI / 180.0;
01124     double c = (glon - slon) * Isis::PI / 180.0;
01125     double absum = 0.5 * (a + b);
01126     double cosabsum = cos(absum);
01127     double sinabsum = sin(absum);
01128     double abdif = 0.5 * (a - b);
01129     double cosabdif = cos(abdif);
01130     double sinabdif = sin(abdif);
01131     double cotc = 1.0 / (tan(0.5 * c));
01132     double tanabsum = cotc * cosabdif / cosabsum;
01133     double tanabdif = cotc * sinabdif / sinabsum;
01134     double ABsum = atan(tanabsum);
01135     double ABdif = atan(tanabdif);
01136     double A = ABsum + ABdif;
01137     double B = ABsum - ABdif;
01138     double sinc = sin(c) * sin(a) / sin(A);
01139     c = asin(sinc);
01140     double azimuthA = A * 180.0 / Isis::PI + 90.0;
01141     double azimuthB = 90.0 - B * 180.0 / Isis::PI;
01142     double azimuth = 0.0;
01143     if ((glat > slat && glon > slon) ||
01144         (glat < slat && glon > slon)) {
01145       if (azimuthA < azimuthB) {
01146         azimuth = 270.0 - azimuthA;
01147       } else {
01148         azimuth = 270.0 - azimuthB;
01149       }
01150     } else if ((glat < slat && glon < slon) ||
01151         (glat > slat && glon < slon)) {
01152       if (azimuthA < azimuthB) {
01153         azimuth = 90.0 - azimuthA;
01154       } else {
01155         azimuth = 90.0 - azimuthB;
01156       }
01157     }
01158     return azimuth;
01159   }
01160 
01174   double Camera::Distance(double lat1, double lon1, 
01175                           double lat2, double lon2, double radius) {
01176     // Convert lat/lon values to radians
01177     double latRad1 = lat1 * Isis::PI / 180.0; 
01178     double latRad2 = lat2 * Isis::PI / 180.0; 
01179     double lonRad1 = lon1 * Isis::PI / 180.0; 
01180     double lonRad2 = lon2 * Isis::PI / 180.0; 
01181 
01182     double deltaLat = latRad2 - latRad1;
01183     double deltaLon = lonRad2 - lonRad1;
01184     double a = (sin(deltaLat/2) * sin(deltaLat/2)) + cos(latRad1) * 
01185                 cos(latRad2) * (sin(deltaLon/2) * sin(deltaLon/2));
01186     double c = 2 * atan(sqrt(a) / sqrt(1-a));
01187     double dist = radius * c;
01188     return dist;
01189   }
01190 
01191 
01198   void Camera::SetDistortionMap (CameraDistortionMap *map) { 
01199     if(p_distortionMap) {
01200       delete p_distortionMap; 
01201     }
01202 
01203     p_distortionMap = map;
01204   };
01205 
01212   void Camera::SetFocalPlaneMap (CameraFocalPlaneMap *map) { 
01213     if(p_focalPlaneMap) {
01214       delete p_focalPlaneMap; 
01215     }
01216 
01217     p_focalPlaneMap = map; 
01218   };
01219 
01226   void Camera::SetDetectorMap (CameraDetectorMap *map) { 
01227     if(p_detectorMap) {
01228       delete p_detectorMap; 
01229     }
01230 
01231     p_detectorMap = map;
01232   };
01233 
01240   void Camera::SetGroundMap (CameraGroundMap *map) { 
01241     if(p_groundMap) {
01242       delete p_groundMap; 
01243     }
01244 
01245     p_groundMap = map; 
01246   };
01247 
01253   void Camera::SetSkyMap (CameraSkyMap *map) { 
01254     if(p_skyMap) {
01255       delete p_skyMap; 
01256     }
01257 
01258     p_skyMap = map; 
01259   };
01260 
01269   void Camera::LoadCache(int cacheSize) {
01270     // We want to stay in unprojected space for this process
01271     bool projIgnored = p_ignoreProjection;
01272     p_ignoreProjection = true;
01273 
01274     double etStart = 0.0, etEnd = 0.0;
01275 
01276     for(int band = 1; band <= Bands(); band++) {
01277       SetBand(band);
01278       SetImage(0.5, 0.5);
01279       double etStartTmp = EphemerisTime();
01280       SetImage(p_alphaCube->BetaSamples() + 0.5, p_alphaCube->BetaLines() + 0.5);
01281       double etEndTmp = EphemerisTime();
01282 
01283       if(band == 1) {
01284         etStart = min(etStartTmp, etEndTmp);
01285         etEnd   = max(etStartTmp, etEndTmp);
01286       }
01287 
01288       etStart = min(etStart, min(etStartTmp, etEndTmp));
01289       etEnd   = max(etEnd,   max(etStartTmp, etEndTmp));
01290     }
01291 
01292     if(cacheSize <= 0) {
01293       // BetaLines() + 1 so we get at least 2 points for interpolation
01294       cacheSize = p_alphaCube->BetaLines() + 1;
01295 
01296       if(etStart == etEnd) {
01297         cacheSize = 1;
01298       }
01299     }
01300 
01301     if(etStart == -DBL_MAX || etEnd == -DBL_MAX) {
01302       string msg = "Unable to find time range for the spice kernels";
01303       throw iException::Message(iException::Programmer, msg, _FILEINFO_);
01304     }
01305 
01306     // Set a position in the image so that the PixelResolution can be calculated
01307     SetImage(p_alphaCube->BetaSamples()/2, p_alphaCube->BetaLines()/2);
01308     double tol = PixelResolution()/100.;  //meters/pix/100.
01309 
01310     if (tol < 0.) {
01311       // Alternative calculation of ground resolution of a pixel/100
01312       double altitudeMeters;
01313       if (IsSky()) {  // Use the unit sphere as the target
01314         altitudeMeters = 1.0;
01315       }
01316       else {
01317         altitudeMeters = SpacecraftAltitude()*1000.;
01318       }
01319       tol = PixelPitch()*altitudeMeters/FocalLength()/100.;
01320     }
01321 
01322     p_ignoreProjection = projIgnored;
01323 
01324     Spice::CreateCache(etStart, etEnd, cacheSize, tol);
01325 
01326     SetEphemerisTime(etStart);
01327 
01328     // Reset to band 1
01329     SetBand(1);
01330   }
01331 
01348   void Camera::SetGeometricTilingHint(int startSize, int endSize) {
01349     // verify the start size is a multiple of 2 greater than 2
01350     int powerOf2 = 2;
01351 
01352     // No hint if 2's are passed in
01353     if(startSize == 2 && endSize == 2) {
01354       p_geometricTilingStartSize = 2;
01355       p_geometricTilingEndSize = 2;
01356       return;
01357     }
01358 
01359     if(endSize > startSize) {
01360       iString message = "Camera::SetGeometricTilingHint End size must be smaller than the start size";
01361       throw iException::Message(iException::Programmer, message, _FILEINFO_);
01362     }
01363 
01364     if(startSize < 4) {
01365       iString message = "Camera::SetGeometricTilingHint Start size must be at least 4";
01366       throw iException::Message(iException::Programmer, message, _FILEINFO_);
01367     }
01368 
01369     bool foundEnd = false;
01370     while(powerOf2 > 0 && startSize != powerOf2) {
01371       if(powerOf2 == endSize) foundEnd = true;
01372       powerOf2 *= 2;
01373     }
01374     
01375     // Didnt find a solution, the integer became negative first, must not be
01376     //   a power of 2
01377     if(powerOf2 < 0) {
01378       iString message = "Camera::SetGeometricTilingHint Start size must be a power of 2";
01379       throw iException::Message(iException::Programmer, message, _FILEINFO_);
01380     }
01381 
01382     if(!foundEnd) {
01383       iString message = "Camera::SetGeometricTilingHint End size must be a power of 2 less than the start size, but greater than 2";
01384       throw iException::Message(iException::Programmer, message, _FILEINFO_);
01385     }
01386 
01387     p_geometricTilingStartSize = startSize;
01388     p_geometricTilingEndSize = endSize;
01389   }
01390 
01398   void Camera::GetGeometricTilingHint(int &startSize, int &endSize) {
01399     startSize = p_geometricTilingStartSize;
01400     endSize = p_geometricTilingEndSize;
01401   }
01402 
01403   
01412   bool Camera::InCube() {
01413     if(Sample() < 0.5 || Line() < 0.5) {
01414       return false;
01415     }
01416 
01417     if(Sample() > Samples() + 0.5 || Line() > Lines() + 0.5) {
01418       return false;
01419     }
01420 
01421     return true;
01422   }
01423 } // end namespace isis