USGS

Isis 3.0 Object Programmers' Reference

Home

Camera.cpp
Go to the documentation of this file.
1 
23 #include "Camera.h"
24 
25 #include <cfloat>
26 #include <cmath>
27 #include <iomanip>
28 
29 #include <QDebug>
30 #include <QList>
31 #include <QPair>
32 #include <QTime>
33 #include <QVector>
34 
35 #include "Angle.h"
36 #include "Constants.h"
37 #include "CameraDetectorMap.h"
38 #include "CameraFocalPlaneMap.h"
39 #include "CameraDistortionMap.h"
40 #include "CameraGroundMap.h"
41 #include "CameraSkyMap.h"
42 #include "DemShape.h"
43 #include "IException.h"
44 #include "IString.h"
45 #include "iTime.h"
46 #include "Latitude.h"
47 #include "Longitude.h"
48 #include "NaifStatus.h"
49 #include "Projection.h"
50 #include "ProjectionFactory.h"
51 #include "RingPlaneProjection.h"
52 #include "ShapeModel.h"
53 #include "SurfacePoint.h"
54 #include "Target.h"
55 #include "TProjection.h"
56 
57 using namespace std;
58 
59 namespace Isis {
65  Camera::Camera(Cube &cube) : Sensor(cube) {
66  // Get the image size which can be different than the alpha cube size
67  p_lines = cube.lineCount();
68  p_samples = cube.sampleCount();
69  p_bands = cube.bandCount();
70 
72 
73  // Get the AlphaCube information
74  p_alphaCube = new AlphaCube(cube);
75 
76  // Get the projection group if it exists
77  Pvl &lab = *cube.label();
78  if (lab.findObject("IsisCube").hasGroup("Mapping")) {
80  }
81  else {
82  p_projection = NULL;
83  }
84  p_ignoreProjection = false;
85 
86  // Initialize stuff
87  p_focalLength = 0.0;
88  p_pixelPitch = 1.0;
89  p_referenceBand = 0;
90  p_childBand = 1;
91 
92  p_distortionMap = NULL;
93  p_focalPlaneMap = NULL;
94  p_detectorMap = NULL;
95  p_groundMap = NULL;
96  p_skyMap = NULL;
97 
98  // See if we have a reference band
99  PvlGroup &inst = lab.findObject("IsisCube").findGroup("Instrument");
100  if (inst.hasKeyword("ReferenceBand")) {
101  p_referenceBand = inst["ReferenceBand"];
102  }
103 
104  p_groundRangeComputed = false;
105  p_raDecRangeComputed = false;
106  p_ringRangeComputed = false;
107  p_pointComputed = false;
108  }
109 
112  if (p_projection) {
113  delete p_projection;
114  p_projection = NULL;
115  }
116 
117  if (p_alphaCube) {
118  delete p_alphaCube;
119  p_alphaCube = NULL;
120  }
121 
122  if (p_distortionMap) {
123  delete p_distortionMap;
124  p_distortionMap = NULL;
125  }
126 
127  if (p_focalPlaneMap) {
128  delete p_focalPlaneMap;
129  p_focalPlaneMap = NULL;
130  }
131 
132  if (p_detectorMap) {
133  delete p_detectorMap;
134  p_detectorMap = NULL;
135  }
136 
137  if (p_groundMap) {
138  delete p_groundMap;
139  p_groundMap = NULL;
140  }
141 
142  if (p_skyMap) {
143  delete p_skyMap;
144  p_skyMap = NULL;
145  }
146  }
147 
157  bool Camera::SetImage(const double sample, const double line) {
158  p_childSample = sample;
159  p_childLine = line;
160  p_pointComputed = true;
161 
162  // get shape
163  // TODO: we need to validate this pointer (somewhere)
164  ShapeModel *shape = target()->shape();
165 
166  // Case of no map projection
167  if (p_projection == NULL || p_ignoreProjection) {
168  // Convert to parent coordinate (remove crop, pad, shrink, enlarge)
169  double parentSample = p_alphaCube->AlphaSample(sample);
170  double parentLine = p_alphaCube->AlphaLine(line);
171  //cout << "\n\n\n\n\n\nCube: " << sample << " " << line << endl;
172  //cout << "alpha cube: " << parentSample << " " << parentLine << endl; //debug
173  //cout.precision(15);
174  //cout << "Time: " << Time().Et() << endl;
175  bool success = false;
176  success = p_detectorMap->SetParent(parentSample, parentLine);
177  // Convert from parent to detector
178  if (success) {
179  double detectorSample = p_detectorMap->DetectorSample();
180  double detectorLine = p_detectorMap->DetectorLine();
181  // cout << "detector: " << detectorSample << " " << detectorLine << endl; //debug
182  success = p_focalPlaneMap->SetDetector(detectorSample, detectorLine);
183  // Now Convert from detector to distorted focal plane
184  if (success) {
185  double focalPlaneX = p_focalPlaneMap->FocalPlaneX();
186  double focalPlaneY = p_focalPlaneMap->FocalPlaneY();
187  // cout << "focal plane: " << focalPlaneX << " " << focalPlaneY << endl; //debug
188  success = p_distortionMap->SetFocalPlane(focalPlaneX, focalPlaneY);
189  // Remove optical distortion
190  if (success) {
191  // Map to the ground
195  return p_groundMap->SetFocalPlane(x, y, z);
196  }
197  }
198  }
199  }
200 
201  // The projection is a sky map
202  else if (p_projection->IsSky()) {
203  TProjection *tproj = (TProjection *) p_projection;
204  if (tproj->SetWorld(sample, line)) {
206  tproj->UniversalLatitude())) {
207  p_childSample = sample;
208  p_childLine = line;
209 
210  return HasSurfaceIntersection();
211  }
212  }
213  }
214 
215  // We have map projected camera model
216  else {
217  Latitude lat;
218  Longitude lon;
219  Distance rad;
220  if (shape->name() != "Plane") { // this is the normal behavior
221  if (p_projection->SetWorld(sample, line)) {
222  TProjection *tproj = (TProjection *) p_projection;
223  lat = Latitude(tproj->UniversalLatitude(), Angle::Degrees);
225  rad = Distance(LocalRadius(lat, lon));
226  if (!rad.isValid()) {
227  shape->setHasIntersection(false);
228  return false;
229  }
230  SurfacePoint surfPt(lat, lon, rad);
231  if (SetGround(surfPt)) {
232  p_childSample = sample;
233  p_childLine = line;
234 
235  shape->setHasIntersection(true);
236  return true;
237  }
238  }
239  }
240  else { // shape is ring plane
241  if (p_projection->SetWorld(sample, line)) {
243  lat = Latitude(0.0, Angle::Degrees);
246 
247  if (!rad.isValid()) {
248  shape->setHasIntersection(false);
249  return false;
250  }
251  SurfacePoint surfPt(lat, lon, rad);
252  if (SetGround(surfPt)) {
253  p_childSample = sample;
254  p_childLine = line;
255 
256  shape->setHasIntersection(true);
257  return true;
258  }
259  }
260  }
261  }
262 
263  // failure
264  shape->clearSurfacePoint();
265  return false;
266  }
267 
277  bool Camera::SetUniversalGround(const double latitude, const double longitude) {
278  // Convert lat/lon or rad/az (i.e. ring rad / ring lon) to undistorted focal plane x/y
280  Longitude(longitude, Angle::Degrees))) {
281  return RawFocalPlanetoImage();
282  }
283 
285  return false;
286  }
287 
288 
298  bool Camera::SetGround(Latitude latitude, Longitude longitude) {
299  ShapeModel *shape = target()->shape();
300  Distance localRadius;
301 
302  if (shape->name() != "Plane") { // this is the normal behavior
303  localRadius = LocalRadius(latitude, longitude);
304  }
305  else {
306  localRadius = Distance(latitude.degrees(),Distance::Kilometers);
307  latitude = Latitude(0.,Angle::Degrees);
308  }
309 
310  if (!localRadius.isValid()) {
312  return false;
313  }
314 
315  return SetGround(SurfacePoint(latitude, longitude, localRadius));
316  }
317 
318 
319 
329  bool Camera::SetGround(const SurfacePoint & surfacePt) {
330  ShapeModel *shape = target()->shape();
331  if (!surfacePt.Valid()) {
332  shape->clearSurfacePoint();
333  return false;
334  }
335 
336  // Convert lat/lon to undistorted focal plane x/y
337  if (p_groundMap->SetGround(surfacePt)) {
338  return RawFocalPlanetoImage();
339  }
340 
341  shape->clearSurfacePoint();
342  return false;
343  }
344 
345 
354  double ux = p_groundMap->FocalPlaneX();
355  double uy = p_groundMap->FocalPlaneY();
356 
357  // get shape
358  // TODO: we need to validate this pointer (somewhere)
359  ShapeModel *shape = target()->shape();
360 
361  //cout << "undistorted focal plane: " << ux << " " << uy << endl; //debug
362  //cout.precision(15);
363  //cout << "Backward Time: " << Time().Et() << endl;
364  // Convert undistorted x/y to distorted x/y
365  bool success = p_distortionMap->SetUndistortedFocalPlane(ux, uy);
366  if (success) {
367  double focalPlaneX = p_distortionMap->FocalPlaneX();
368  double focalPlaneY = p_distortionMap->FocalPlaneY();
369  //cout << "focal plane: " << focalPlaneX << " " << focalPlaneY << endl; //debug
370  // Convert distorted x/y to detector position
371  success = p_focalPlaneMap->SetFocalPlane(focalPlaneX, focalPlaneY);
372  if (success) {
373  double detectorSample = p_focalPlaneMap->DetectorSample();
374  double detectorLine = p_focalPlaneMap->DetectorLine();
375  //cout << "detector: " << detectorSample << " " << detectorLine << endl;
376  // Convert detector to parent position
377  success = p_detectorMap->SetDetector(detectorSample, detectorLine);
378  if (success) {
379  double parentSample = p_detectorMap->ParentSample();
380  double parentLine = p_detectorMap->ParentLine();
381  //cout << "cube: " << parentSample << " " << parentLine << endl; //debug
382  if (p_projection == NULL || p_ignoreProjection) {
383  p_childSample = p_alphaCube->BetaSample(parentSample);
384  p_childLine = p_alphaCube->BetaLine(parentLine);
385  p_pointComputed = true;
386  shape->setHasIntersection(true);
387  return true;
388  }
389  else if (p_projection->IsSky()) {
390  if (p_projection->SetGround(Declination(), RightAscension())) {
393  p_pointComputed = true;
394  shape->setHasIntersection(true);
395  return true;
396  }
397  }
402  p_pointComputed = true;
403  shape->setHasIntersection(true);
404  return true;
405  }
406  }
407  else { // ring plane
408  // UniversalLongitude should return azimuth (ring longitude) in this case TODO: when we make the change
409  // to real azimuths this value may need to be adjusted or code changed in the shapemodel or
410  // surfacepoint class.
414  p_pointComputed = true;
415  shape->setHasIntersection(true);
416  return true;
417  }
418  }
419  }
420  }
421  }
422 
423  shape->clearSurfacePoint();
424  return false;
425  }
426 
427 
428 
439  bool Camera::SetUniversalGround(const double latitude, const double longitude,
440  const double radius) {
441  // Convert lat/lon to undistorted focal plane x/y
443  Longitude(longitude, Angle::Degrees),
444  Distance(radius, Distance::Meters)))) {
445  return RawFocalPlanetoImage(); // sets p_hasIntersection
446  }
447 
449  return false;
450  }
451 
458  if (HasSurfaceIntersection()) {
459  double sB[3];
460  instrumentPosition(sB);
461  double pB[3];
462  Coordinate(pB);
463  double a = sB[0] - pB[0];
464  double b = sB[1] - pB[1];
465  double c = sB[2] - pB[2];
466  double dist = sqrt(a * a + b * b + c * c) * 1000.0;
467  return dist / (p_focalLength / p_pixelPitch);
468  }
469  return -1.0;
470  }
471 
479  }
480 
488  }
489 
496  double lineRes = LineResolution();
497  double sampRes = SampleResolution();
498  if (lineRes < 0.0) return -1.0;
499  if (sampRes < 0.0) return -1.0;
500  return (lineRes + sampRes) / 2.0;
501  }
502 
510  return p_maxres;
511  }
512 
520  return p_minres;
521  }
522 
527  // Software adjustment is needed if we get here -- call RingRangeResolution instead
528  if (target()->shape()->name() == "Plane") {
529  IString msg = "Images with plane targets should use Camera method RingRangeResolution ";
530  msg += "instead of GroundRangeResolution";
532  }
533 
534  // Have we already done this
535  if (p_groundRangeComputed) return;
536  p_groundRangeComputed = true;
537 
538  bool computed = p_pointComputed;
539  double originalSample = Sample();
540  double originalLine = Line();
541  int originalBand = Band();
542 
543  // Initializations
544  p_minlat = DBL_MAX;
545  p_minlon = DBL_MAX;
546  p_minlon180 = DBL_MAX;
547  p_maxlat = -DBL_MAX;
548  p_maxlon = -DBL_MAX;
549  p_maxlon180 = -DBL_MAX;
550  p_minres = DBL_MAX;
551  p_maxres = -DBL_MAX;
552 
553  // See if we have band dependence and loop for the appropriate number of bands
554  int eband = p_bands;
555  if (IsBandIndependent()) eband = 1;
556  for (int band = 1; band <= eband; band++) {
557  SetBand(band);
558 
559  // Loop for each line testing the left and right sides of the image
560  for (int line = 1; line <= p_lines + 1; line++) {
561  // Look for the first good lat/lon on the left edge of the image
562  // If it is the first or last line then test the whole line
563  int samp;
564  for (samp = 1; samp <= p_samples + 1; samp++) {
565 
566  if (SetImage((double)samp - 0.5, (double)line - 0.5)) {
567  double lat = UniversalLatitude();
568  double lon = UniversalLongitude();
569  if (lat < p_minlat) p_minlat = lat;
570  if (lat > p_maxlat) p_maxlat = lat;
571  if (lon < p_minlon) p_minlon = lon;
572  if (lon > p_maxlon) p_maxlon = lon;
573 
574  if (lon > 180.0) lon -= 360.0;
575  if (lon < p_minlon180) p_minlon180 = lon;
576  if (lon > p_maxlon180) p_maxlon180 = lon;
577 
578  double res = PixelResolution();
579  if (res > 0.0) {
580  if (res < p_minres) p_minres = res;
581  if (res > p_maxres) p_maxres = res;
582  }
583  if ((line != 1) && (line != p_lines + 1)) break;
584  }
585  } // end loop through samples
586 
587  //We've already checked the first and last lines.
588  if (line == 1) continue;
589  if (line == p_lines + 1) continue;
590 
591  // Look for the first good lat/lon on the right edge of the image
592  if (samp < p_samples + 1) {
593  for (samp = p_samples + 1; samp >= 1; samp--) {
594  if (SetImage((double)samp - 0.5, (double)line - 0.5)) {
595  double lat = UniversalLatitude();
596  double lon = UniversalLongitude();
597  if (lat < p_minlat) p_minlat = lat;
598  if (lat > p_maxlat) p_maxlat = lat;
599  if (lon < p_minlon) p_minlon = lon;
600  if (lon > p_maxlon) p_maxlon = lon;
601 
602  if (lon > 180.0) lon -= 360.0;
603  if (lon < p_minlon180) p_minlon180 = lon;
604  if (lon > p_maxlon180) p_maxlon180 = lon;
605 
606  double res = PixelResolution();
607  if (res > 0.0) {
608  if (res < p_minres) p_minres = res;
609  if (res > p_maxres) p_maxres = res;
610  }
611  break;
612  }
613  }
614  }
615  } // end loop through lines
616 
617  // Test at the sub-spacecraft point to see if we have a
618  // better resolution
619  double lat, lon;
620 
621  subSpacecraftPoint(lat, lon);
622  Latitude latitude(lat, Angle::Degrees);
623  Longitude longitude(lon, Angle::Degrees);
624  // get the local radius for the subspacecraft point
625  Distance radius(LocalRadius(latitude, longitude));
626  SurfacePoint testPoint;
627 
628  if (radius.isValid()) {
629 
630  testPoint = SurfacePoint(latitude, longitude, radius);
631 
632  if (SetGround(testPoint)) {
633  if (Sample() >= 0.5 && Line() >= 0.5 &&
634  Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
635  double res = PixelResolution();
636  if (res > 0.0) {
637  if (res < p_minres) p_minres = res;
638  if (res > p_maxres) p_maxres = res;
639  }
640  }
641  }
642  } // end valid local (subspacecraft) radius
643 
644  // Special test for ground range to see if either pole is in the image
645  latitude = Latitude(90, Angle::Degrees);
646  longitude = Longitude(0.0, Angle::Degrees);
647  // get radius for north pole
648  radius = LocalRadius(latitude, longitude);
649 
650  if (radius.isValid()) {
651 
652  testPoint = SurfacePoint(latitude, longitude, radius);
653 
654  if (SetGround(testPoint)) {
655  if (Sample() >= 0.5 && Line() >= 0.5 &&
656  Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
657  p_maxlat = 90.0;
658  p_minlon = 0.0;
659  p_maxlon = 360.0;
660  p_minlon180 = -180.0;
661  p_maxlon180 = 180.0;
662  }
663  }
664  } // end valid north polar radius
665 
666  latitude = Latitude(-90, Angle::Degrees);
667  // get radius for south pole
668  radius = LocalRadius(latitude, longitude);
669 
670  if (radius.isValid()) {
671 
672  testPoint = SurfacePoint(latitude, longitude, radius);
673  if (SetGround(testPoint)) {
674  if (Sample() >= 0.5 && Line() >= 0.5 &&
675  Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
676  p_minlat = -90.0;
677  p_minlon = 0.0;
678  p_maxlon = 360.0;
679  p_minlon180 = -180.0;
680  p_maxlon180 = 180.0;
681  }
682  }
683  } // end valid south polar radius
684 
685  // Another special test for ground range as we could have the
686  // 0-360 seam running right through the image so
687  // test it as well (the increment may not be fine enough !!!)
690  lat += Angle((p_maxlat - p_minlat) / 10.0, Angle::Degrees)) {
691  if (SetGround(lat, Longitude(0.0, Angle::Degrees))) {
692  if (Sample() >= 0.5 && Line() >= 0.5 &&
693  Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
694  p_minlon = 0.0;
695  p_maxlon = 360.0;
696  break;
697  }
698  } // end if set ground (lat, 0)
699 
700  // Another special test for ground range as we could have the
701  // -180-180 seam running right through the image so
702  // test it as well (the increment may not be fine enough !!!)
703  if (SetGround(lat, Longitude(180.0, Angle::Degrees))) {
704  if (Sample() >= 0.5 && Line() >= 0.5 &&
705  Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
706  p_minlon180 = -180.0;
707  p_maxlon180 = 180.0;
708  break;
709  }
710  } // end if set ground (lat, 180)
711  } // end for loop (latitudes from min to max)
712  } // end for loop through bands
713 
714  SetBand(originalBand);
715 
716  if(computed) {
717  SetImage(originalSample, originalLine);
718  }
719  else {
720  p_pointComputed = false;
721  }
722 
723  if (p_minlon == DBL_MAX || p_minlat == DBL_MAX || p_maxlon == -DBL_MAX || p_maxlat == -DBL_MAX) {
724  string message = "Camera missed planet or SPICE data off.";
725  throw IException(IException::Unknown, message, _FILEINFO_);
726  }
727 
728 
729  // Checks for invalid lat/lon ranges
730 // if(p_minlon == DBL_MAX || p_minlat == DBL_MAX || p_maxlon == -DBL_MAX || p_maxlat == -DBL_MAX) {
731 // string message = "Camera missed planet or SPICE data off.";
732 // throw IException(IException::Unknown, message, _FILEINFO_);
733 // }
734  }
735 
736 
742  // TODO Add test to make sure we have a ring plane image **
743 
744  // Have we already done this
745  if (p_ringRangeComputed) return;
746 
747  p_ringRangeComputed = true;
748 
749  bool computed = p_pointComputed;
750  double originalSample = Sample();
751  double originalLine = Line();
752  int originalBand = Band();
753 
754  // Initializations
755  p_minRingRadius = DBL_MAX;
756  p_minRingLongitude = DBL_MAX;
757  p_minRingLongitude180 = DBL_MAX;
758  p_maxRingRadius = -DBL_MAX;
759  p_maxRingLongitude = -DBL_MAX;
760  p_maxRingLongitude180 = -DBL_MAX;
761  p_minres = DBL_MAX;
762  p_maxres = -DBL_MAX;
763 
764  // See if we have band dependence and loop for the appropriate number of bands
765  int eband = p_bands;
766  if (IsBandIndependent())
767  eband = 1;
768 
769  for (int band = 1; band <= eband; band++) {
770  SetBand(band);
771 
772  // Loop for each line testing the left and right sides of the image
773  for (int line = 1; line <= p_lines + 1; line++) {
774 
775  // Look for the first good radius/azimuth on the left edge of the image
776  // If it is the first or last line then test the whole line
777  int samp;
778  for (samp = 1; samp <= p_samples + 1; samp++) {
779 
780  if (SetImage((double)samp - 0.5, (double)line - 0.5)) {
781  double radius = LocalRadius().meters();
782  double azimuth = UniversalLongitude();
783  if (radius < p_minRingRadius) p_minRingRadius = radius;
784  if (radius > p_maxRingRadius) p_maxRingRadius = radius;
785  if (azimuth < p_minRingLongitude) p_minRingLongitude = azimuth;
786  if (azimuth > p_maxRingLongitude) p_maxRingLongitude = azimuth;
787 
788  if (azimuth > 180.0) azimuth -= 360.0;
789  if (azimuth < p_minRingLongitude180) p_minRingLongitude180 = azimuth;
790  if (azimuth > p_maxRingLongitude180) p_maxRingLongitude180 = azimuth;
791 
792  double res = PixelResolution();
793  if (res > 0.0) {
794  if (res < p_minres) p_minres = res;
795  if (res > p_maxres) p_maxres = res;
796  }
797  if ((line != 1) && (line != p_lines + 1)) break;
798  }
799  }
800  //We've already checked the first and last lines.
801  if (line == 1) continue;
802  if (line == p_lines + 1) continue;
803 
804  // Look for the first good rad/azimuth on the right edge of the image
805  if (samp < p_samples + 1) {
806  for(samp = p_samples + 1; samp >= 1; samp--) {
807  if (SetImage((double)samp - 0.5, (double)line - 0.5)) {
808  double radius = LocalRadius().meters();
809  double azimuth = UniversalLongitude();
810  if (radius < p_minRingRadius) p_minRingRadius = radius;
811  if (radius > p_maxRingRadius) p_maxRingRadius = radius;
812  if (azimuth < p_minRingLongitude) p_minRingLongitude = azimuth;
813  if (azimuth > p_maxRingLongitude) p_maxRingLongitude = azimuth;
814 
815  if (azimuth > 180.0) azimuth -= 360.0;
816  if (azimuth < p_minRingLongitude180) p_minRingLongitude180 = azimuth;
817  if (azimuth > p_maxRingLongitude180) p_maxRingLongitude180 = azimuth;
818 
819  double res = PixelResolution();
820  if (res > 0.0) {
821  if (res < p_minres) p_minres = res;
822  if (res > p_maxres) p_maxres = res;
823  }
824  break;
825  }
826  }
827  }
828  }
829 
830  // Test at the sub-spacecraft point to see if we have a
831  // better resolution
832  // TODO: is there something to this analogous for ring images?
833 // double rad, lon;
834 
835 // subSpacecraftPoint(lat, lon);
836 // Latitude latitude(lat, Angle::Degrees);
837 // Longitude longitude(lon, Angle::Degrees);
838 // Distance radius(LocalRadius(latitude, longitude));
839 // SurfacePoint testPoint;
840 
841 // if (radius.isValid()) {
842 
843 // testPoint = SurfacePoint(latitude, longitude, radius);
844 
845 // if(SetGround(testPoint)) {
846 // if(Sample() >= 0.5 && Line() >= 0.5 &&
847 // Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
848 // double res = PixelResolution();
849 // if(res > 0.0) {
850 // if(res < p_minres) p_minres = res;
851 // if(res > p_maxres) p_maxres = res;
852 // }
853 // }
854 // }
855 // }
856 
857  // Another special test for ring range as we could have the
858  // 0-360 seam running right through the image so
859  // test it as well (the increment may not be fine enough !!!)
862  radius += Distance((p_maxRingRadius - p_minRingRadius) / 10.0, Distance::Meters)) {
864  if (Sample() >= 0.5 && Line() >= 0.5 &&
865  Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
866  p_minRingLongitude = 0.0;
867  p_maxRingLongitude = 360.0;
868  break;
869  }
870  }
871 
872  // for (Latitude lat = Latitude(p_minlat, Angle::Degrees);
873  // lat <= Latitude(p_maxlat, Angle::Degrees);
874  // lat += Angle((p_maxlat - p_minlat) / 10.0, Angle::Degrees)) {
875  // if (SetGround(lat, Longitude(0.0, Angle::Degrees))) {
876  // if (Sample() >= 0.5 && Line() >= 0.5 &&
877  // Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
878  // p_minlon = 0.0;
879  // p_maxlon = 360.0;
880  // break;
881  // }
882  // }
883 
884  // Another special test for ring range as we could have the
885  // -180-180 seam running right through the image so
886  // test it as well (the increment may not be fine enough !!!)
888  if (Sample() >= 0.5 && Line() >= 0.5 &&
889  Sample() <= p_samples + 0.5 && Line() <= p_lines + 0.5) {
890  p_minRingLongitude180 = -180.0;
891  p_maxRingLongitude180 = 180.0;
892  break;
893  }
894  }
895  }
896  } // end loop over bands
897 
898  SetBand(originalBand);
899 
900  if (computed) {
901  SetImage(originalSample, originalLine);
902  }
903  else {
904  p_pointComputed = false;
905  }
906 
907  // Checks for invalid radius/lon ranges
908  if (p_minRingRadius == DBL_MAX || p_minRingRadius == DBL_MAX || p_minRingLongitude == DBL_MAX || p_maxRingLongitude == -DBL_MAX) {
909  string message = "RingPlane ShapeModel - Camera missed plane or SPICE data off.";
910  throw IException(IException::Unknown, message, _FILEINFO_);
911  }
912  }
913 
914 
924  double minlat, minlon, maxlat, maxlon;
925  return GroundRange(minlat, maxlat, minlon, maxlon, pvl);
926  }
927 
940  bool Camera::GroundRange(double &minlat, double &maxlat,
941  double &minlon, double &maxlon,
942  Pvl &pvl) {
943  // Compute the ground range and resolution
945 
946  // Get the default radii
947  Distance localRadii[3];
948  radii(localRadii);
949  Distance &a = localRadii[0];
950  Distance &b = localRadii[2];
951 
952  // See if the PVL overrides the radii
953  PvlGroup map = pvl.findGroup("Mapping", Pvl::Traverse);
954 
955  if(map.hasKeyword("EquatorialRadius"))
956  a = Distance(toDouble(map["EquatorialRadius"][0]), Distance::Meters);
957 
958  if(map.hasKeyword("PolarRadius"))
959  b = Distance(toDouble(map["PolarRadius"][0]), Distance::Meters);
960 
961  // Convert to planetographic if necessary
962  minlat = p_minlat;
963  maxlat = p_maxlat;
964  if(map.hasKeyword("LatitudeType")) {
965  QString latType = (QString) map["LatitudeType"];
966  if (latType.toUpper() == "PLANETOGRAPHIC") {
967  if (abs(minlat) < 90.0) { // So tan doesn't fail
968  minlat *= PI / 180.0;
969  minlat = atan(tan(minlat) * (a / b) * (a / b));
970  minlat *= 180.0 / PI;
971  }
972 
973  if(abs(maxlat) < 90.0) { // So tan doesn't fail
974  maxlat *= PI / 180.0;
975  maxlat = atan(tan(maxlat) * (a / b) * (a / b));
976  maxlat *= 180.0 / PI;
977  }
978  }
979  }
980 
981  // Assume 0 to 360 domain but change it if necessary
982  minlon = p_minlon;
983  maxlon = p_maxlon;
984  bool domain360 = true;
985  if(map.hasKeyword("LongitudeDomain")) {
986  QString lonDomain = (QString) map["LongitudeDomain"];
987  if(lonDomain.toUpper() == "180") {
988  minlon = p_minlon180;
989  maxlon = p_maxlon180;
990  domain360 = false;
991  }
992  }
993 
994  // Convert to the proper longitude direction
995  if(map.hasKeyword("LongitudeDirection")) {
996  QString lonDirection = (QString) map["LongitudeDirection"];
997  if(lonDirection.toUpper() == "POSITIVEWEST") {
998  double swap = minlon;
999  minlon = -maxlon;
1000  maxlon = -swap;
1001  }
1002  }
1003 
1004  // Convert to the proper longitude domain
1005  if(domain360) {
1006  while(minlon < 0.0) {
1007  minlon += 360.0;
1008  maxlon += 360.0;
1009  }
1010  while(minlon > 360.0) {
1011  minlon -= 360.0;
1012  maxlon -= 360.0;
1013  }
1014  }
1015  else {
1016  while(minlon < -180.0) {
1017  minlon += 360.0;
1018  maxlon += 360.0;
1019  }
1020  while(minlon > 180.0) {
1021  minlon -= 360.0;
1022  maxlon -= 360.0;
1023  }
1024  }
1025 
1026  // Now return if it crosses the longitude domain boundary
1027  if((maxlon - minlon) > 359.0) return true;
1028  return false;
1029  }
1030 
1044  bool Camera::ringRange(double &minRingRadius, double &maxRingRadius,
1045  double &minRingLongitude, double &maxRingLongitude, Pvl &pvl) {
1046  // Compute the ring range and resolution
1048 
1049  // Get the mapping group
1050  PvlGroup map = pvl.findGroup("Mapping", Pvl::Traverse);
1051 
1052  // Get the ring radius range
1053  minRingRadius = p_minRingRadius;
1054  maxRingRadius = p_maxRingRadius;
1055 
1056  // Assume 0 to 360 domain but change it if necessary
1057  minRingLongitude = p_minRingLongitude;
1058  maxRingLongitude = p_maxRingLongitude;
1059  bool domain360 = true;
1060  if (map.hasKeyword("RingLongitudeDomain")) {
1061  QString ringLongitudeDomain = (QString) map["RingLongitudeDomain"];
1062  if (ringLongitudeDomain == "180") {
1063  minRingLongitude = p_minRingLongitude180;
1064  maxRingLongitude = p_maxRingLongitude180;
1065  domain360 = false;
1066  }
1067  }
1068 
1069  // Convert to the proper azimuth direction
1070  if (map.hasKeyword("RingLongitudeDirection")) {
1071  QString ringLongitudeDirection = (QString) map["RingLongitudeDirection"];
1072  if (ringLongitudeDirection.toUpper() == "Clockwise") {
1073  double swap = minRingLongitude;
1074  minRingLongitude = -maxRingLongitude;
1075  maxRingLongitude = -swap;
1076  }
1077  }
1078 
1079  // Convert to the proper azimuth domain
1080  if (domain360) {
1081  while (minRingLongitude < 0.0) {
1082  minRingLongitude += 360.0;
1083  maxRingLongitude += 360.0;
1084  }
1085  while (minRingLongitude > 360.0) {
1086  minRingLongitude -= 360.0;
1087  maxRingLongitude -= 360.0;
1088  }
1089  }
1090  else {
1091  while (minRingLongitude < -180.0) {
1092  minRingLongitude += 360.0;
1093  maxRingLongitude += 360.0;
1094  }
1095  while (minRingLongitude > 180.0) {
1096  minRingLongitude -= 360.0;
1097  maxRingLongitude -= 360.0;
1098  }
1099  }
1100 
1101  // Now return if it crosses the azimuth domain boundary
1102  if ((maxRingLongitude - minRingLongitude) > 359.0) {
1103  return true;
1104  }
1105  return false;
1106  }
1107 
1108 
1115  PvlGroup map("Mapping");
1116  map += PvlKeyword("TargetName", target()->name());
1117 
1118  std::vector<Distance> radii = target()->radii();
1119  map += PvlKeyword("EquatorialRadius", toString(radii[0].meters()), "meters");
1120  map += PvlKeyword("PolarRadius", toString(radii[2].meters()), "meters");
1121 
1122  map += PvlKeyword("LatitudeType", "Planetocentric");
1123  map += PvlKeyword("LongitudeDirection", "PositiveEast");
1124  map += PvlKeyword("LongitudeDomain", "360");
1125 
1127  map += PvlKeyword("MinimumLatitude", toString(p_minlat));
1128  map += PvlKeyword("MaximumLatitude", toString(p_maxlat));
1129  map += PvlKeyword("MinimumLongitude", toString(p_minlon));
1130  map += PvlKeyword("MaximumLongitude", toString(p_maxlon));
1131  map += PvlKeyword("PixelResolution", toString(p_minres));
1132 
1133  map += PvlKeyword("ProjectionName", "Sinusoidal");
1134  pvl.addGroup(map);
1135  }
1136 
1137 
1144  if (target()->shape()->name() != "Plane") { // If we get here and we don't have a plane, throw an error
1145  IString msg = "A ring plane projection has been requested on an image whose shape is not a ring plane. ";
1146  msg += "Rerun spiceinit with shape=RINGPLANE. ";
1147  throw IException(IException::User, msg, _FILEINFO_);
1148  }
1149 
1150  PvlGroup map("Mapping");
1151  map += PvlKeyword("TargetName", target()->name());
1152 
1153  map += PvlKeyword("RingLongitudeDirection", "CounterClockwise");
1154  map += PvlKeyword("RingLongitudeDomain", "360");
1155 
1157  map += PvlKeyword("MinimumRingRadius", toString(p_minRingRadius));
1158  map += PvlKeyword("MaximumRingRadius", toString(p_maxRingRadius));
1159  map += PvlKeyword("MinimumRingLongitude", toString(p_minRingLongitude));
1160  map += PvlKeyword("MaximumRingLongitude", toString(p_maxRingLongitude));
1161  map += PvlKeyword("PixelResolution", toString(p_minres));
1162 
1163  map += PvlKeyword("ProjectionName", "Planar");
1164  pvl.addGroup(map);
1165  }
1166 
1167 
1170  int code = naifIkCode();
1171  QString key = "INS" + toString(code) + "_FOCAL_LENGTH";
1173  }
1174 
1177  int code = naifIkCode();
1178  QString key = "INS" + toString(code) + "_PIXEL_PITCH";
1180  }
1181 
1191  bool Camera::SetRightAscensionDeclination(const double ra, const double dec) {
1192  if (p_skyMap->SetSky(ra, dec)) {
1193  double ux = p_skyMap->FocalPlaneX();
1194  double uy = p_skyMap->FocalPlaneY();
1196  double dx = p_distortionMap->FocalPlaneX();
1197  double dy = p_distortionMap->FocalPlaneY();
1198  if (p_focalPlaneMap->SetFocalPlane(dx, dy)) {
1199  double detectorSamp = p_focalPlaneMap->DetectorSample();
1200  double detectorLine = p_focalPlaneMap->DetectorLine();
1201  if (p_detectorMap->SetDetector(detectorSamp, detectorLine)) {
1202  double parentSample = p_detectorMap->ParentSample();
1203  double parentLine = p_detectorMap->ParentLine();
1204 
1205  if (p_projection == NULL || p_ignoreProjection) {
1206  p_childSample = p_alphaCube->BetaSample(parentSample);
1207  p_childLine = p_alphaCube->BetaLine(parentLine);
1208  p_pointComputed = true;
1209  return true;
1210  }
1211  else if (p_projection->IsSky()) {
1212  if (p_projection->SetGround(dec, ra)) {
1215  p_pointComputed = true;
1216  return true;
1217  }
1218  }
1219  else if (target()->shape()->hasIntersection()) {
1221  UniversalLongitude())) {
1224  p_pointComputed = true;
1225  return true;
1226  }
1227  }
1228  }
1229  }
1230  }
1231  }
1232 
1233  return false;
1234  }
1235 
1236 
1244  void Camera::GetLocalNormal(double normal[3]) {
1245 
1246  ShapeModel *shapeModel = target()->shape();
1247  if ( !shapeModel->hasIntersection()) {
1248  // if the shape is not intersected, then clearly there is no normal
1249  normal[0] = normal[1] = normal[2] = 0.0;
1250  return;
1251  }
1252 
1253  QVector<double *> cornerNeighborPoints(4);
1254  bool computed = p_pointComputed;
1255  if (QString::compare(shapeModel->name(), "DemShape", Qt::CaseInsensitive) == 0) {
1256  // If the shape model is not DEM, we don't use the neighbor points.
1257  // So, to avoid the potetially expesive SetImage() calls used to find the neighbors,
1258  // just pass in empty vector to shape model's calculateLocalNormal() method
1259  shapeModel->calculateLocalNormal(cornerNeighborPoints);
1260  }
1261  else { // attempt to find local normal for DEM shapes using 4 surrounding points on the image
1262 
1263  // As documented in the doxygen above, the goal of this method is to
1264  // calculate a normal vector to the surface using the 4 corner surrounding points.
1265  double samp = Sample();
1266  double line = Line();
1267 
1268  // order of points in vector is top, bottom, left, right
1269  QList< QPair< double, double > > surroundingPoints;
1270  surroundingPoints.append(qMakePair(samp, line - 0.5));
1271  surroundingPoints.append(qMakePair(samp, line + 0.5));
1272  surroundingPoints.append(qMakePair(samp - 0.5, line));
1273  surroundingPoints.append(qMakePair(samp + 0.5, line));
1274 
1275  // save input state to be restored on return
1276  double originalSample = samp;
1277  double originalLine = line;
1278 
1279  // now we have all four points in the image, so find the same points on the surface
1280  for (int i = 0; i < cornerNeighborPoints.size(); i++) {
1281  cornerNeighborPoints[i] = new double[3];
1282  }
1283 
1284  Latitude lat;
1285  Longitude lon;
1286  Distance radius;
1287 
1288  // if this is a dsk, we only need to use the existing intercept point (plate) normal then return
1289  for (int i = 0; i < cornerNeighborPoints.size(); i++) {
1290  // If a surrounding point fails, set it to the original point
1291  if (!(SetImage(surroundingPoints[i].first, surroundingPoints[i].second))) {
1292  surroundingPoints[i].first = samp;
1293  surroundingPoints[i].second = line;
1294 
1295  // If the original point fails too, we can't get a normal. Clean up and return.
1296  if (!(SetImage(surroundingPoints[i].first, surroundingPoints[i].second))) {
1297  normal[0] = 0.;
1298  normal[1] = 0.;
1299  normal[2] = 0.;
1300 
1301  // restore input state
1302  if (computed) {
1303  SetImage(originalSample, originalLine);
1304  }
1305  else {
1306  p_pointComputed = false;
1307  }
1308 
1309  // free memory
1310  for (int i = 0; i < cornerNeighborPoints.size(); i++) {
1311  delete [] cornerNeighborPoints[i];
1312  }
1313 
1314  return;
1315  }
1316  }
1317 
1318  SurfacePoint surfacePoint = GetSurfacePoint();
1319  lat = surfacePoint.GetLatitude();
1320  lon = surfacePoint.GetLongitude();
1321  radius = LocalRadius(lat, lon);
1322 
1323  latrec_c(radius.kilometers(), lon.radians(), lat.radians(), cornerNeighborPoints[i]);
1324  }
1325 
1326  // if the first 2 surrounding points match or the last 2 surrounding points match,
1327  // we can't get a normal. Clean up and return.
1328  if ((surroundingPoints[0].first == surroundingPoints[1].first &&
1329  surroundingPoints[0].second == surroundingPoints[1].second) ||
1330  (surroundingPoints[2].first == surroundingPoints[3].first &&
1331  surroundingPoints[2].second == surroundingPoints[3].second)) {
1332  normal[0] = 0.;
1333  normal[1] = 0.;
1334  normal[2] = 0.;
1335 
1336  // restore input state
1337  if (!computed) {
1338  SetImage(originalSample, originalLine);
1339  }
1340  else {
1341  p_pointComputed = false;
1342  }
1343 
1344  // free memory
1345  for (int i = 0; i < cornerNeighborPoints.size(); i++)
1346  delete [] cornerNeighborPoints[i];
1347 
1348  return;
1349  }
1350 
1351  // Restore input state to original point before calculating normal
1352  SetImage(originalSample, originalLine);
1353  shapeModel->calculateLocalNormal(cornerNeighborPoints);
1354 
1355  // free memory
1356  for (int i = 0; i < cornerNeighborPoints.size(); i++)
1357  delete [] cornerNeighborPoints[i];
1358 
1359  }
1360 
1361  // restore input state if calculation failed and clean up.
1362  if (!shapeModel->hasNormal()) {
1363  p_pointComputed = false;
1364  return;
1365  }
1366 
1367  // restore failed computed state
1368  if (!computed) {
1369  p_pointComputed = false;
1370  }
1371 
1372  // Set the method normal values
1373  std::vector<double> localNormal(3);
1374  localNormal = shapeModel->normal();
1375  memcpy(normal, (double *) &localNormal[0], sizeof(double) * 3);
1376  }
1377 
1378 
1391  void Camera::LocalPhotometricAngles(Angle & phase, Angle & incidence,
1392  Angle & emission, bool &success) {
1393 
1394  // get local normal vector
1395  double normal[3];
1396  GetLocalNormal(normal);
1397  success = true;
1398 
1399  // Check to make sure normal is valid
1400  SpiceDouble mag;
1401  unorm_c(normal,normal,&mag);
1402  if (mag == 0.) {
1403  success = false;
1404  return;
1405  }
1406 
1407  // get a normalized surface spacecraft vector
1408  SpiceDouble surfSpaceVect[3], unitizedSurfSpaceVect[3], dist;
1409  std::vector<double> sB = bodyRotation()->ReferenceVector(
1411 
1412  SpiceDouble pB[3];
1413  SurfacePoint surfacePoint = GetSurfacePoint();
1414  pB[0] = surfacePoint.GetX().kilometers();
1415  pB[1] = surfacePoint.GetY().kilometers();
1416  pB[2] = surfacePoint.GetZ().kilometers();
1417 
1418  vsub_c((SpiceDouble *) &sB[0], pB, surfSpaceVect);
1419  unorm_c(surfSpaceVect, unitizedSurfSpaceVect, &dist);
1420 
1421  // get a normalized surface sun vector
1422  SpiceDouble surfaceSunVect[3];
1423  vsub_c(m_uB, pB, surfaceSunVect);
1424  SpiceDouble unitizedSurfSunVect[3];
1425  unorm_c(surfaceSunVect, unitizedSurfSunVect, &dist);
1426 
1427  // use normalized surface spacecraft and surface sun vectors to calculate
1428  // the phase angle (in radians)
1429  phase = Angle(vsep_c(unitizedSurfSpaceVect, unitizedSurfSunVect),
1430  Angle::Radians);
1431 
1432  // use normalized surface spacecraft and local normal vectors to calculate
1433  // the emission angle (in radians)
1434  emission = Angle(vsep_c(unitizedSurfSpaceVect, normal),
1435  Angle::Radians);
1436 
1437  // use normalized surface sun and normal vectors to calculate the incidence
1438  // angle (in radians)
1439  incidence = Angle(vsep_c(unitizedSurfSunVect, normal),
1440  Angle::Radians);
1441  }
1442 
1443 
1458  bool Camera::RaDecRange(double &minra, double &maxra,
1459  double &mindec, double &maxdec) {
1460  TProjection *tproj = (TProjection *) p_projection;
1461  if (p_projection != NULL && !tproj->IsSky()) {
1462  IString msg = "Camera::RaDecRange can not calculate a right ascension, declination range";
1463  msg += " for projected images which are not projected to sky";
1465  }
1466 
1467  bool computed = p_pointComputed;
1468  double originalSample = Sample();
1469  double originalLine = Line();
1470  int originalBand = Band();
1471 
1472  // Have we already done this
1473  if (!p_raDecRangeComputed) {
1474  p_raDecRangeComputed = true;
1475 
1476  // Initializations
1477  p_mindec = DBL_MAX;
1478  p_minra = DBL_MAX;
1479  p_minra180 = DBL_MAX;
1480  p_maxdec = -DBL_MAX;
1481  p_maxra = -DBL_MAX;
1482  p_maxra180 = -DBL_MAX;
1483 
1484  // See if we have band dependence and loop for the appropriate number of bands
1485  int eband = p_bands;
1486  if (IsBandIndependent()) eband = 1;
1487  for (int band = 1; band <= eband; band++) {
1488  this->SetBand(band);
1489 
1490  for (int line = 1; line <= p_lines; line++) {
1491  // Test left, top, and bottom sides
1492  int samp;
1493  for (samp = 1; samp <= p_samples; samp++) {
1494  SetImage((double)samp, (double)line);
1495  double ra = RightAscension();
1496  double dec = Declination();
1497  if (ra < p_minra) p_minra = ra;
1498  if (ra > p_maxra) p_maxra = ra;
1499  if (dec < p_mindec) p_mindec = dec;
1500  if (dec > p_maxdec) p_maxdec = dec;
1501 
1502  if (ra > 180.0) ra -= 360.0;
1503  if (ra < p_minra180) p_minra180 = ra;
1504  if (ra > p_maxra180) p_maxra180 = ra;
1505 
1506  if ((line != 1) && (line != p_lines)) break;
1507  }
1508 
1509  // Test right side
1510  if (samp < p_samples) {
1511  for (samp = p_samples; samp >= 1; samp--) {
1512  SetImage((double)samp, (double)line);
1513  double ra = RightAscension();
1514  double dec = Declination();
1515  if (ra < p_minra) p_minra = ra;
1516  if (ra > p_maxra) p_maxra = ra;
1517  if (dec < p_mindec) p_mindec = dec;
1518  if (dec > p_maxdec) p_maxdec = dec;
1519 
1520  if (ra > 180.0) ra -= 360.0;
1521  if (ra < p_minra180) p_minra180 = ra;
1522  if (ra > p_maxra180) p_maxra180 = ra;
1523 
1524  break;
1525  }
1526  }
1527  }
1528 
1529  // Special test for ground range to see if either pole is in the image
1530  if (SetRightAscensionDeclination(0.0, 90.0)) {
1531  if ((Line() >= 0.5) && (Line() <= p_lines) &&
1532  (Sample() >= 0.5) && (Sample() <= p_samples)) {
1533  p_maxdec = 90.0;
1534  p_minra = 0.0;
1535  p_maxra = 360.0;
1536  p_minra180 = -180.0;
1537  p_maxra180 = 180.0;
1538  }
1539  }
1540 
1541  if (SetRightAscensionDeclination(0.0, -90.0)) {
1542  if ((Line() >= 0.5) && (Line() <= p_lines) &&
1543  (Sample() >= 0.5) && (Sample() <= p_samples)) {
1544  p_mindec = -90.0;
1545  p_minra = 0.0;
1546  p_maxra = 360.0;
1547  p_minra180 = -180.0;
1548  p_maxra180 = 180.0;
1549  }
1550  }
1551 
1552  // Another special test for ground range as we could have the
1553  // 0-360 seam running right through the image so
1554  // test it as well (the increment may not be fine enough !!!)
1555  for (double dec = p_mindec; dec <= p_maxdec; dec += (p_maxdec - p_mindec) / 10.0) {
1556  if (SetRightAscensionDeclination(0.0, dec)) {
1557  if ((Line() >= 0.5) && (Line() <= p_lines) &&
1558  (Sample() >= 0.5) && (Sample() <= p_samples)) {
1559  p_minra = 0.0;
1560  p_maxra = 360.0;
1561  break;
1562  }
1563  }
1564  }
1565 
1566  // Another special test for ground range as we could have the
1567  // 0-360 seam running right through the image so
1568  // test it as well (the increment may not be fine enough !!!)
1569  for (double dec = p_mindec; dec <= p_maxdec; dec += (p_maxdec - p_mindec) / 10.0) {
1570  if (SetRightAscensionDeclination(180.0, dec)) {
1571  if ((Line() >= 0.5) && (Line() <= p_lines) &&
1572  (Sample() >= 0.5) && (Sample() <= p_samples)) {
1573  p_minra180 = -180.0;
1574  p_maxra180 = 180.0;
1575  break;
1576  }
1577  }
1578  }
1579  }
1580  }
1581 
1582  minra = p_minra;
1583  maxra = p_maxra;
1584  mindec = p_mindec;
1585  maxdec = p_maxdec;
1586 
1587  SetBand(originalBand);
1588 
1589  if (computed) {
1590  SetImage(originalSample, originalLine);
1591  }
1592  else {
1593  p_pointComputed = false;
1594  }
1595 
1596  return true;
1597  }
1598 
1599 
1606  TProjection *tproj = (TProjection *) p_projection;
1607  if (p_projection != NULL && !tproj->IsSky()) {
1608  IString msg = "Camera::RaDecResolution can not calculate a right ascension, declination resolution";
1609  msg += " for projected images which are not projected to sky";
1611  }
1612 
1613  bool computed = p_pointComputed;
1614  double originalSample = Sample();
1615  double originalLine = Line();
1616  int originalBand = Band();
1617 
1618  SetImage(1.0, 1.0);
1619  double ra1 = RightAscension();
1620  double dec1 = Declination();
1621 
1622  SetImage(1.0, (double)p_lines);
1623  double ra2 = RightAscension();
1624  double dec2 = Declination();
1625 
1626  double dist = (ra1 - ra2) * (ra1 - ra2) + (dec1 - dec2) * (dec1 - dec2);
1627  dist = sqrt(dist);
1628  double lineRes = dist / (p_lines - 1);
1629 
1630  SetImage((double)p_samples, 1.0);
1631  ra2 = RightAscension();
1632  dec2 = Declination();
1633 
1634  dist = (ra1 - ra2) * (ra1 - ra2) + (dec1 - dec2) * (dec1 - dec2);
1635  dist = sqrt(dist);
1636  double sampRes = dist / (p_samples - 1);
1637 
1638  SetBand(originalBand);
1639 
1640  if (computed) {
1641  SetImage(originalSample, originalLine);
1642  }
1643  else {
1644  p_pointComputed = false;
1645  }
1646 
1647  return (sampRes < lineRes) ? sampRes : lineRes;
1648  }
1649 
1656  if (target()->shape()->name() == "Plane") {
1657  QString msg = "North Azimuth is not available for plane target shapes.";
1659  }
1660  // Get the latitude of your current location using the shape model
1661  // specified in the image Kernels
1662  double lat = UniversalLatitude();
1663  // We are in northern hemisphere
1664  if (lat >= 0.0) {
1665  return ComputeAzimuth(LocalRadius(90.0, 0.0), 90.0, 0.0);
1666  }
1667  // We are in southern hemisphere
1668  else {
1669  double azimuth = ComputeAzimuth(LocalRadius(-90.0, 0.0),
1670  -90.0, 0.0) + 180.0;
1671  if (azimuth > 360.0) azimuth = azimuth - 360.0;
1672  return azimuth;
1673  }
1674  }
1675 
1684  double lat, lon;
1685  subSolarPoint(lat, lon);
1686  return ComputeAzimuth(LocalRadius(lat, lon), lat, lon);
1687  }
1688 
1697  double lat, lon;
1698  subSpacecraftPoint(lat, lon);
1699  return ComputeAzimuth(LocalRadius(lat, lon), lat, lon);
1700  }
1701 
1803  const double lat, const double lon) {
1804  // Make sure we are on the planet, if not, north azimuth is meaningless
1805  if (!HasSurfaceIntersection()) return Isis::Null;
1806 
1807  // Need to save the "state" of the camera so we can restore it when the
1808  // method is done
1809  bool computed = p_pointComputed;
1810 
1812 
1813  // Get the azimuth's origin point (the current position) and its radius
1814  SpiceDouble azimuthOrigin[3];
1815  Coordinate(azimuthOrigin);
1816  Distance originRadius = LocalRadius();
1817  if (!originRadius.isValid()) {
1818  return Isis::Null;
1819  }
1820 
1821  // Convert the point of interest to rectangular coordinates (x,y,z) in the
1822  // body-fixed coordinate system and use the azimuth's origin radius to avoid the
1823  // situation where the DEM does not cover the entire planet
1824  SpiceDouble pointOfInterestFromBodyCenter[3];
1825  latrec_c(originRadius.kilometers(), lon * PI / 180.0,
1826  lat * PI / 180.0, pointOfInterestFromBodyCenter);
1827 
1828  // Get the difference vector with its tail at the azimuth origin and its
1829  // head at the point of interest by subtracting vectors the vectors that
1830  // originate at the body center
1831  //
1832  // pointOfInterest = pointOfInterestFromBodyCenter - azimuthOriginFromBodyCenter
1833  //
1834  SpiceDouble pointOfInterest[3];
1835  vsub_c(pointOfInterestFromBodyCenter, azimuthOrigin, pointOfInterest);
1836 
1837  // Get the component of the difference vector pointOfInterestFromAzimuthOrigin that is
1838  // perpendicular to the origin point (i.e. project perpendicularly onto the reference plane).
1839  // This will result in a point of interest vector that is in the plane tangent to the surface at
1840  // the origin point
1841  SpiceDouble pointOfInterestProj[3];
1842  vperp_c(pointOfInterest, azimuthOrigin, pointOfInterestProj);
1843 
1844  // Unitize the tangent vector to a 1 km length vector
1845  SpiceDouble pointOfInterestProjUnit[3];
1846  vhat_c(pointOfInterestProj, pointOfInterestProjUnit);
1847 
1848  // Scale the vector to within a pixel of the azimuth's origin point.
1849  // Get pixel scale in km/pixel and divide by 2 to insure that we stay within
1850  // a pixel of the origin point
1851  double scale = (PixelResolution() / 1000.0) / 2.0;
1852  SpiceDouble pointOfInterestProjUnitScaled[3];
1853  vscl_c(scale, pointOfInterestProjUnit, pointOfInterestProjUnitScaled);
1854 
1855  // Compute the adjusted point of interest vector from the body center. This point
1856  // will be within a pixel of the origin and in the same direction as the requested
1857  // raw point of interest vector
1858  SpiceDouble adjustedPointOfInterestFromBodyCenter[3];
1859  vadd_c(azimuthOrigin, pointOfInterestProjUnitScaled, adjustedPointOfInterestFromBodyCenter);
1860 
1861  // Get the origin image coordinate
1862  double azimuthOriginSample = Sample();
1863  double azimuthOriginLine = Line();
1864 
1865  // Convert the point to a lat/lon and find out its image coordinate
1866  double adjustedPointOfInterestRad, adjustedPointOfInterestLon, adjustedPointOfInterestLat;
1867  reclat_c(adjustedPointOfInterestFromBodyCenter,
1868  &adjustedPointOfInterestRad,
1869  &adjustedPointOfInterestLon,
1870  &adjustedPointOfInterestLat);
1871  adjustedPointOfInterestLat = adjustedPointOfInterestLat * 180.0 / PI;
1872  adjustedPointOfInterestLon = adjustedPointOfInterestLon * 180.0 / PI;
1873  if (adjustedPointOfInterestLon < 0) adjustedPointOfInterestLon += 360.0;
1874 
1875  // Use the radius of the azimuth's origin point
1876  // (rather than that of the adjusted point of interest location)
1877  // to avoid the effects of topography on the calculation
1878  bool success = SetUniversalGround(adjustedPointOfInterestLat,
1879  adjustedPointOfInterestLon,
1880  originRadius.meters());
1881  if (!success) {
1882  // if the adjusted lat/lon values for the point of origin fail to be set, we can not compute
1883  // an azimuth. reset to the original sample/line and return null.
1884  SetImage(azimuthOriginSample, azimuthOriginLine);
1885  return Isis::Null;
1886  }
1887 
1888  double adjustedPointOfInterestSample = Sample();
1889  double adjustedPointOfInterestLine = Line();
1890 
1891  // TODO: Write PushState and PopState method to ensure the
1892  // internals of the class are set based on SetImage or SetGround
1893 
1894  // We now have the information needed to calculate an arctangent
1895  //
1896  //
1897  // point of interest
1898  // |\ |
1899  // | \ |
1900  // | \ | tan(A) = (delta line) / (delta sample)
1901  // delta line | \ | A = arctan( (delta line) / (delta sample) )
1902  // | \ |
1903  // | \ |
1904  // | \ |
1905  // ___________|_____A_\|_______________
1906  // delta sample |origin point
1907  // |
1908  // |
1909  // |
1910  // |
1911  // |
1912  //
1913  // in this example, the azimuth is the angle indicated by the A plus 180 degrees, since we begin
1914  // the angle rotation at the positive x-axis
1915  // This quadrant issue (i.e.need to add 180 degrees) is handled by the atan2 program
1916  //
1917  double deltaSample = adjustedPointOfInterestSample - azimuthOriginSample;
1918  double deltaLine = adjustedPointOfInterestLine - azimuthOriginLine;
1919 
1920  // Compute the angle; the azimuth is the arctangent of the line difference divided by the
1921  // sample difference; the atan2 function is used because it determines which quadrant we
1922  // are in based on the sign of the 2 arguments; the arctangent is measured in a positive
1923  // clockwise direction because the lines in the image increase downward; the arctangent
1924  // uses the 3 o'clock axis (positive sample direction) as its reference line (line of
1925  // zero degrees); a good place to read about the atan2 function is at
1926  // http://en.wikipedia.org/wiki/Atan2
1927  double azimuth = 0.0;
1928  if (deltaSample != 0.0 || deltaLine != 0.0) {
1929  azimuth = atan2(deltaLine, deltaSample);
1930  azimuth *= 180.0 / PI;
1931  }
1932 
1933  // Azimuth is limited to the range of 0 to 360
1934  if (azimuth < 0.0) azimuth += 360.0;
1935  if (azimuth > 360.0) azimuth -= 360.0;
1936 
1938 
1939  // computed is true if the sample/line or lat/lon were reset in this method
1940  // to find the location of the point of interest
1941  // If so, reset "state" of camera to the original sample/line
1942  if (computed) {
1943  SetImage(azimuthOriginSample, azimuthOriginLine);
1944  }
1945  else {
1946  p_pointComputed = false;
1947  }
1948 
1949  return azimuth;
1950  }
1951 
1959 
1960  // Get the xyz coordinates for the spacecraft and point we are interested in
1961  double coord[3], spCoord[3];
1962  Coordinate(coord);
1963  instrumentPosition(spCoord);
1964 
1965  // Get the angle between the 2 points and convert to degrees
1966  double a = vsep_c(coord, spCoord) * 180.0 / PI;
1967  double b = 180.0 - EmissionAngle();
1968 
1969  // The three angles in a triangle must add up to 180 degrees
1970  double c = 180.0 - (a + b);
1971 
1973 
1974  return c;
1975  }
1976 
1998  double Camera::GroundAzimuth(double glat, double glon,
1999  double slat, double slon) {
2000  double a;
2001  double b;
2002  if (glat >= 0.0) {
2003  a = (90.0 - slat) * PI / 180.0;
2004  b = (90.0 - glat) * PI / 180.0;
2005  }
2006  else {
2007  a = (90.0 + slat) * PI / 180.0;
2008  b = (90.0 + glat) * PI / 180.0;
2009  }
2010 
2011  double cslon = slon;
2012  double cglon = glon;
2013  if (cslon > cglon) {
2014  if ((cslon-cglon) > 180.0) {
2015  while ((cslon-cglon) > 180.0) cslon = cslon - 360.0;
2016  }
2017  }
2018  if (cglon > cslon) {
2019  if ((cglon-cslon) > 180.0) {
2020  while ((cglon-cslon) > 180.0) cglon = cglon - 360.0;
2021  }
2022  }
2023 
2024  // Which quadrant are we in?
2025  int quad;
2026  if (slat > glat) {
2027  if (cslon > cglon) {
2028  quad = 1;
2029  }
2030  else if (cslon < cglon) {
2031  quad = 2;
2032  }
2033  else {
2034  quad = 1;
2035  }
2036  }
2037  else if (slat < glat) {
2038  if (cslon > cglon) {
2039  quad = 4;
2040  }
2041  else if (cslon < cglon) {
2042  quad = 3;
2043  }
2044  else {
2045  quad = 4;
2046  }
2047  }
2048  else {
2049  if (cslon > cglon) {
2050  quad = 1;
2051  }
2052  else if (cslon < cglon) {
2053  quad = 2;
2054  }
2055  else {
2056  return 0.0;
2057  }
2058  }
2059 
2060  double C = (cglon - cslon) * PI / 180.0;
2061  if (C < 0) C = -C;
2062  double c = acos(cos(a)*cos(b) + sin(a)*sin(b)*cos(C));
2063  double azimuth = 0.0;
2064  if (sin(b) == 0.0 || sin(c) == 0.0) {
2065  return azimuth;
2066  }
2067  double A = acos((cos(a) - cos(b)*cos(c))/(sin(b)*sin(c))) * 180.0 / PI;
2068  //double B = acos((cos(b) - cos(c)*cos(a))/(sin(c)*sin(a))) * 180.0 / PI;
2069  if (glat >= 0.0) {
2070  if (quad == 1 || quad == 4) {
2071  azimuth = A;
2072  }
2073  else if (quad == 2 || quad == 3) {
2074  azimuth = 360.0 - A;
2075  }
2076  }
2077  else {
2078  if (quad == 1 || quad == 4) {
2079  azimuth = 180.0 - A;
2080  }
2081  else if (quad == 2 || quad == 3) {
2082  azimuth = 180.0 + A;
2083  }
2084  }
2085 
2086  return azimuth;
2087  }
2088 
2089 
2097  if (p_distortionMap) {
2098  delete p_distortionMap;
2099  }
2100 
2101  p_distortionMap = map;
2102  }
2103 
2111  if (p_focalPlaneMap) {
2112  delete p_focalPlaneMap;
2113  }
2114 
2115  p_focalPlaneMap = map;
2116  }
2117 
2125  if (p_detectorMap) {
2126  delete p_detectorMap;
2127  }
2128 
2129  p_detectorMap = map;
2130  }
2131 
2139  if (p_groundMap) {
2140  delete p_groundMap;
2141  }
2142 
2143  p_groundMap = map;
2144  }
2145 
2152  if (p_skyMap) {
2153  delete p_skyMap;
2154  }
2155 
2156  p_skyMap = map;
2157  }
2158 
2170  // We want to stay in unprojected space for this process
2171  bool projIgnored = p_ignoreProjection;
2172  p_ignoreProjection = true;
2173 
2174  // get the cache variables
2175  pair<double,double> ephemerisTimes = StartEndEphemerisTimes();
2176  int cacheSize = CacheSize(ephemerisTimes.first, ephemerisTimes.second);
2177 
2178  // Set a position in the image so that the PixelResolution can be calculated
2180  double tol = PixelResolution() / 100.; //meters/pix/100.
2181 
2182  if (tol < 0.0) {
2183  // Alternative calculation of ground resolution of a pixel/100
2184  double altitudeMeters;
2185  if (target()->isSky()) { // Use the unit sphere as the target
2186  altitudeMeters = 1.0;
2187  }
2188  else {
2189  altitudeMeters = SpacecraftAltitude() * 1000.;
2190  }
2191  tol = PixelPitch() * altitudeMeters / FocalLength() / 100.;
2192  }
2193 
2194  p_ignoreProjection = projIgnored;
2195 
2196  Spice::createCache(ephemerisTimes.first, ephemerisTimes.second,
2197  cacheSize, tol);
2198 
2199  setTime(ephemerisTimes.first);
2200 
2201  // Reset to band 1
2202  SetBand(1);
2203 
2204  return;
2205  }
2206 
2207 
2226  pair<double, double> Camera::StartEndEphemerisTimes() {
2227  pair<double,double> ephemerisTimes;
2228  double startTime = -DBL_MAX;
2229  double endTime = -DBL_MAX;
2230 
2231  for (int band = 1; band <= Bands(); band++) {
2232  SetBand(band);
2233  SetImage(0.5, 0.5);
2234  double etStart = time().Et();
2235  SetImage(p_alphaCube->BetaSamples() + 0.5,
2236  p_alphaCube->BetaLines() + 0.5);
2237  double etEnd = time().Et();
2238  if (band == 1) {
2239  startTime = min(etStart, etEnd);
2240  endTime = max(etStart, etEnd);
2241  }
2242  startTime = min(startTime, min(etStart, etEnd));
2243  endTime = max(endTime, max(etStart, etEnd));
2244  }
2245  if (startTime == -DBL_MAX || endTime == -DBL_MAX) {
2246  string msg = "Unable to find time range for the spice kernels";
2248  }
2249  ephemerisTimes.first = startTime;
2250  ephemerisTimes.second = endTime;
2251  return ephemerisTimes;
2252  }
2253 
2268  int Camera::CacheSize(double startTime, double endTime) {
2269  int cacheSize;
2270  // BetaLines() + 1 so we get at least 2 points for interpolation
2271  cacheSize = p_alphaCube->BetaLines() + 1;
2272  if (startTime == endTime) {
2273  cacheSize = 1;
2274  }
2275  return cacheSize;
2276  }
2277 
2278 
2295  void Camera::SetGeometricTilingHint(int startSize, int endSize) {
2296  // verify the start size is a multiple of 2 greater than 2
2297  int powerOf2 = 2;
2298 
2299  // No hint if 2's are passed in
2300  if (startSize == 2 && endSize == 2) {
2303  return;
2304  }
2305 
2306  if (endSize > startSize) {
2307  IString message = "Camera::SetGeometricTilingHint End size must be smaller than the start size";
2308  throw IException(IException::Programmer, message, _FILEINFO_);
2309  }
2310 
2311  if (startSize < 4) {
2312  IString message = "Camera::SetGeometricTilingHint Start size must be at least 4";
2313  throw IException(IException::Programmer, message, _FILEINFO_);
2314  }
2315 
2316  bool foundEnd = false;
2317  while (powerOf2 > 0 && startSize != powerOf2) {
2318  if (powerOf2 == endSize) foundEnd = true;
2319  powerOf2 *= 2;
2320  }
2321 
2322  // Didnt find a solution, the integer became negative first, must not be
2323  // a power of 2
2324  if (powerOf2 < 0) {
2325  IString message = "Camera::SetGeometricTilingHint Start size must be a power of 2";
2326  throw IException(IException::Programmer, message, _FILEINFO_);
2327  }
2328 
2329  if (!foundEnd) {
2330  IString message = "Camera::SetGeometricTilingHint End size must be a power of 2 less than the start size, but greater than 2";
2331  throw IException(IException::Programmer, message, _FILEINFO_);
2332  }
2333 
2334  p_geometricTilingStartSize = startSize;
2335  p_geometricTilingEndSize = endSize;
2336  }
2337 
2345  void Camera::GetGeometricTilingHint(int &startSize, int &endSize) {
2346  startSize = p_geometricTilingStartSize;
2347  endSize = p_geometricTilingEndSize;
2348  }
2349 
2350 
2360  if (Sample() < 0.5 || Line() < 0.5) {
2361  return false;
2362  }
2363 
2364  if (Sample() > Samples() + 0.5 || Line() > Lines() + 0.5) {
2365  return false;
2366  }
2367 
2368  return true;
2369  }
2370 
2378  return p_projection != 0;
2379  }
2380 
2388  return true;
2389  }
2390 
2397  return p_referenceBand;
2398  }
2399 
2407  return p_referenceBand != 0;
2408  }
2409 
2415  void Camera::SetBand(const int band) {
2416  p_childBand = band;
2417  }
2418 
2424  double Camera::Sample() {
2425  return p_childSample;
2426  }
2427 
2434  return p_childBand;
2435  }
2436 
2442  double Camera::Line() {
2443  return p_childLine;
2444  }
2451  return PixelResolution();
2452  }
2458  double Camera::FocalLength() const {
2459  return p_focalLength;
2460  }
2461 
2467  double Camera::PixelPitch() const {
2468  return p_pixelPitch;
2469  }
2470 
2478 
2479  QList<QPointF> offsets;
2480  offsets.append(QPointF(-PixelPitch() * DetectorMap()->SampleScaleFactor() / 2.0,
2481  -PixelPitch() * DetectorMap()->LineScaleFactor() / 2.0));
2482  offsets.append(QPointF(PixelPitch() * DetectorMap()->SampleScaleFactor() / 2.0,
2483  -PixelPitch() * DetectorMap()->LineScaleFactor() / 2.0));
2484  offsets.append(QPointF(PixelPitch() * DetectorMap()->SampleScaleFactor() / 2.0,
2485  PixelPitch() * DetectorMap()->LineScaleFactor() / 2.0));
2486  offsets.append(QPointF(-PixelPitch() * DetectorMap()->SampleScaleFactor() / 2.0,
2487  PixelPitch() * DetectorMap()->LineScaleFactor() / 2.0));
2488 
2489  return offsets;
2490  }
2491 
2492 
2498  int Camera::Samples() const {
2499  return p_samples;
2500  }
2501 
2507  int Camera::Lines() const {
2508  return p_lines;
2509  }
2510 
2516  int Camera::Bands() const {
2517  return p_bands;
2518  }
2519 
2525  int Camera::ParentLines() const {
2526  return p_alphaCube->AlphaLines();
2527  }
2528 
2535  return p_alphaCube->AlphaSamples();
2536  }
2543  return p_distortionMap;
2544  }
2545 
2552  return p_focalPlaneMap;
2553  }
2554 
2561  return p_detectorMap;
2562  }
2563 
2570  return p_groundMap;
2571  }
2572 
2579  return p_skyMap;
2580  }
2581 
2587  void Camera::IgnoreProjection(bool ignore) {
2588  p_ignoreProjection = ignore;
2589  }
2625  int Camera::SpkTargetId() const {
2626  return (naifSpkCode());
2627  }
2628 
2667  int Camera::SpkCenterId() const {
2668  return (naifBodyCode());
2669  }
2670 
2676  void Camera::SetFocalLength(double v) {
2677  p_focalLength = v;
2678  }
2679 
2685  void Camera::SetPixelPitch(double v) {
2686  p_pixelPitch = v;
2687  }
2688 // end namespace isis
2689 }