Isis 3 Programmer Reference
Orthographic.cpp
Go to the documentation of this file.
1 
23 #include "Orthographic.h"
24 
25 #include <cfloat>
26 #include <cmath>
27 #include <iomanip>
28 
29 #include <QDebug>
30 #include "Constants.h"
31 #include "IException.h"
32 #include "TProjection.h"
33 #include "Pvl.h"
34 #include "PvlGroup.h"
35 #include "PvlKeyword.h"
36 
37 using namespace std;
38 namespace Isis {
55  Orthographic::Orthographic(Pvl &label, bool allowDefaults) :
56  TProjection::TProjection(label) {
57  try {
58  // Try to read the mapping group
59  PvlGroup &mapGroup = label.findGroup("Mapping", Pvl::Traverse);
60 
61  /*
62  * Compute and write the default center longitude if allowed and
63  * necessary.
64  */
65  if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLongitude"))) {
66  double lon = (m_minimumLongitude + m_maximumLongitude) / 2.0;
67  mapGroup += PvlKeyword("CenterLongitude", toString(lon));
68  }
69 
70  /*
71  * Compute and write the default center latitude if allowed and
72  * necessary
73  */
74  if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLatitude"))) {
75  double lat = (m_minimumLatitude + m_maximumLatitude) / 2.0;
76  mapGroup += PvlKeyword("CenterLatitude", toString(lat));
77  }
78 
79  // Get the center longitude & latitude
80  m_centerLongitude = mapGroup["CenterLongitude"];
81  m_centerLatitude = mapGroup["CenterLatitude"];
82  if (IsPlanetocentric()) {
84  }
85 
86  //Restrict center longitude to avoid converting between domains.
87  if ((m_centerLongitude < -360.0) || (m_centerLongitude > 360.0)) {
88  QString msg = "The center longitude cannot exceed [-360, 360]. "
89  "[" + toString(m_centerLongitude) + "] is not valid";
91  }
92 
93  // convert to radians, adjust for longitude direction
94  m_centerLongitude *= PI / 180.0;
95  m_centerLatitude *= PI / 180.0;
97 
98  // Calculate sine & cosine of center latitude
100  m_cosph0 = cos(m_centerLatitude);
101 
102  /*
103  * This projection has a limited lat/lon range (can't do the world)
104  * So let's make sure that our lat/lon range is properly restricted!
105  * The equation: m_sinph0 * sinphi + m_cosph0 * cosphi * coslon tells us
106  * if we are inside of the projection.
107  *
108  * m_sinph0 = sin(center lat)
109  * sinphi = sin(lat)
110  * m_cosph0 = cos(center lat)
111  * cosphi = cos(lat)
112  * coslon = cos(lon - centerLon)
113  *
114  * Let's apply this equation at the extremes to minimize our lat/lon range
115  */
116  double sinphi, cosphi, coslon;
117 
118  /* Can we project at the minlat, center lon? If not, then we should move
119  * up the min lat to be inside the image.
120  */
121  sinphi = sin(m_minimumLatitude * PI / 180.0);
122  cosphi = cos(m_minimumLatitude * PI / 180.0);
123  coslon = 1.0; // at lon=centerLon: cos(lon-centerLon) = cos(0) = 1
124  if (m_sinph0 * sinphi + m_cosph0 * cosphi * coslon < 1E-10) {
125  /*solve for x: a * sin(x) + b * cos(x) * 1 = 0
126  * a * sin(x) + b * cos(x) = 0
127  * a * sin(x) = - b * cos(x)
128  * -(a * sin(x)) / b = cos(x)
129  * -(a / b) = cos(x) / sin(x)
130  * -(b / a) = sin(x) / cos(x)
131  * -(b / a) = tan(x)
132  * arctan(-(b / a)) = x
133  * arctan(-(m_cosph0 / m_sinph0)) = x
134  */
135  double newMin = atan2(- m_cosph0, m_sinph0) * 180.0 / PI;
136  if (newMin > m_minimumLatitude) {
137  m_minimumLatitude = newMin;
138  } // else something else is off (i.e. longitude range)
139  }
140 
141  //Restrict the longitude range to 360 degrees to simplify comparisons.
142  if ((m_maximumLongitude - m_minimumLongitude) > 360.0) {
143  QString msg = "The longitude range cannot exceed 360 degrees.";
145  }
146 
147  sinphi = sin(m_minimumLatitude * PI / 180.0);
148  cosphi = cos(m_minimumLatitude * PI / 180.0);
149 
150  /* Can we project at the maxlat, center lon? If not, then we should move
151  * down the max lat to be inside the image.
152  */
153  sinphi = sin(m_maximumLatitude * PI / 180.0);
154  cosphi = cos(m_maximumLatitude * PI / 180.0);
155  coslon = 1.0; // at lon=centerLon: cos(lon-centerLon) = cos(0) = 1
156  if (m_sinph0 * sinphi + m_cosph0 * cosphi * coslon < 1E-10) {
157  // see above equations for latitude
158  double newMax = atan2(- m_cosph0, m_sinph0) * 180.8 / PI;
159  if (newMax < m_maximumLatitude && newMax > m_minimumLatitude) {
160  m_maximumLatitude = newMax;
161  } // else something else is off (i.e. longitude range)
162  }
163  }
164  catch(IException &e) {
165  QString message = "Invalid label group [Mapping]";
166  throw IException(e, IException::Io, message, _FILEINFO_);
167  }
168  }
169 
172  }
173 
183  if (!Projection::operator==(proj)) return false;
184  // dont do the below it is a recusive plunge
185  Orthographic *ortho = (Orthographic *) &proj;
186  if ((ortho->m_centerLongitude != m_centerLongitude) ||
187  (ortho->m_centerLatitude != m_centerLatitude)) return false;
188  return true;
189  }
190 
196  QString Orthographic::Name() const {
197  return "Orthographic";
198  }
199 
206  QString Orthographic::Version() const {
207  return "1.0";
208  }
209 
220  //Snyder pg. 45
221  // no distortion at center of projection (centerLatitude, centerLongitude)
222  return m_centerLatitude * 180.0 / PI;// Change to true scale
223  }
224 
237  bool Orthographic::SetGround(const double lat, const double lon) {
238  // Convert longitude to radians & clean up
239  m_longitude = lon;
240  double lonRadians = lon * PI / 180.0;
241  if (m_longitudeDirection == PositiveWest) lonRadians *= -1.0;
242 
243  // Now convert latitude to radians & clean up ... it must be planetographic
244  m_latitude = lat;
245  double latRadians = lat;
246  if (IsPlanetocentric()) latRadians = ToPlanetographic(latRadians);
247  latRadians *= PI / 180.0;
248 
249  // Compute helper variables
250  double deltaLon = (lonRadians - m_centerLongitude);
251  double sinphi = sin(latRadians);
252  double cosphi = cos(latRadians);
253  double coslon = cos(deltaLon);
254 
255  // Lat/Lon cannot be projected
256  double g = m_sinph0 * sinphi + m_cosph0 * cosphi * coslon;
257  if ((g <= 0.0) && (fabs(g) > 1.0e-10)) {
258  m_good = false;
259  return m_good;
260  }
261 
262  // Compute the coordinates
263  double x = m_equatorialRadius * cosphi * sin(deltaLon);
264  double y = m_equatorialRadius * (m_cosph0 * sinphi - m_sinph0 * cosphi * coslon);
265 
266  SetComputedXY(x, y);
267  m_good = true;
268  return m_good;
269  }
270 
284  bool Orthographic::SetCoordinate(const double x, const double y) {
285  // Save the coordinate
286  SetXY(x, y);
287 
288  // Declare instance variables and calculate rho
289  double rho, con, z, sinz, cosz;
290  const double epsilon = 1.0e-10;
291  rho = sqrt(GetX() * GetX() + GetY() * GetY());
292 
293  /* Error calculating rho - should be less than equatorial radius.
294  *
295  * This if statement included "fabs(rho - m_equatorialRadius) < 1E-10 ||"
296  * to make the method more stable for limbs but this caused a false failure
297  * for some images when rho == m_equatorialRadius.
298  */
299  if (rho > m_equatorialRadius) {
300  m_good = false;
301  return m_good;
302  }
303 
304  // Calculate the latitude and longitude
306  if (fabs(rho) <= epsilon) {
308  }
309  else {
310  con = rho / m_equatorialRadius;
311  if (con > 1.0) con = 1.0;
312  if (con < -1.0) con = -1.0;
313  z = asin(con);
314  sinz = sin(z);
315  cosz = cos(z);
316  con = cosz * m_sinph0 + GetY() * sinz * m_cosph0 / rho;
317  if (con > 1.0) con = 1.0;
318  if (con < -1.0) con = -1.0;
319  m_latitude = asin(con);
320  con = fabs(m_centerLatitude) - HALFPI;
321  if (fabs(con) <= epsilon) {
322  if (m_centerLatitude >= 0.0) {
323  m_longitude += atan2(GetX(), -GetY());
324  }
325  else {
326  m_longitude += atan2(GetX(), GetY());
327  }
328  }
329  else {
330  con = cosz - m_sinph0 * sin(m_latitude);
331  if ((fabs(con) >= epsilon) || (fabs(GetX()) >= epsilon)) {
332  m_longitude += atan2(GetX() * sinz * m_cosph0, con * rho);
333  }
334  }
335  }
336 
337  // Convert to degrees
338  m_latitude *= 180.0 / PI;
339  m_longitude *= 180.0 / PI;
340 
341  // Cleanup the longitude
343 
344  /*
345  * When the longitude range is 0 to 360 and the seam is within the 180 displayable degrees,
346  * the longitude needs to be converted to its 360 lon domain counterpart. However, if the
347  * range is shifted out of the 0 to 360 range, the conversion is not necessary. For example,
348  * if the specified range is -180 to 180 and the clon is 0, the lon -90 is valid but will
349  * be converted to 270, which does not work with the comparison. The same idea applies if
350  * the range is 200 - 500 and the clon is 360. We want to display 270 to 450 (270 - 360 and
351  * 0 - 90). However, if 450 is converted to the 360 domain it becomes 90 which is no longer
352  * within the original 200 to 500 range.
353  */
354  // These need to be done for circular type projections
357 
358  // Cleanup the latitude
359  if (IsPlanetocentric())
361 
362  m_good = true;
363  return m_good;
364  }
365 
389  bool Orthographic::XYRange(double &minX, double &maxX,
390  double &minY, double &maxY) {
391  double lat, lon;
392 
393  //Restrict lon range to be between -360 and 360
394  double adjustedLon;
395  double adjustedMinLon = To360Domain(MinimumLongitude());
396  double adjustedMaxLon = To360Domain(MaximumLongitude());
397  bool correctedMinLon = false;
398 
399  if (adjustedMinLon >= adjustedMaxLon) {
400  adjustedMinLon -= 360;
401  correctedMinLon = true;
402  }
403 
404  // Check the corners of the lat/lon range
409 
410  // Walk top and bottom edges
411  for (lat = m_minimumLatitude; lat <= m_maximumLatitude; lat += 0.01) {
412  lon = m_minimumLongitude;
413  XYRangeCheck(lat, lon);
414 
415  lon = m_maximumLongitude;
416  XYRangeCheck(lat, lon);
417  }
418 
419  // Walk left and right edges
420  for (lon = m_minimumLongitude; lon <= m_maximumLongitude; lon += 0.01) {
421  lat = m_minimumLatitude;
422  XYRangeCheck(lat, lon);
423 
424  lat = m_maximumLatitude;
425  XYRangeCheck(lat, lon);
426  }
427 
428  /*
429  * Walk the limb.
430  * When the pair is valid and within the correct range, reassign min/max x/y.
431  *
432  * , - ~ ~ ~ - ,
433  * , ' ' ,
434  * , _______ ,
435  * , | | , <----- Walking the limb would not affect an
436  * , | | , image that does not extend to the
437  * , |_______| , limits of the projection.
438  * , ________________________,_
439  * , | , |
440  * ,| , |
441  * |,___________________,_'___| <-- This corner would wrap around.
442  * ' - , _ _ _ , ' The image would rotate because the
443  * center lat was closer to the pole.
444  * This would allow for a more completely
445  * longitude range.
446  * _o__o__
447  * o| |o <------------This is the rotated view. If the image
448  * o |_______| o limits extend over the pole (due to the
449  * o * o offset of the center lat) it is possible
450  * o o to have a lon range that is larger than
451  * o o 180 degrees.
452  *
453  */
454 
455  for (double angle = 0.0; angle <= 360.0; angle += 0.01) {
456  double x = m_equatorialRadius * cos(angle * PI / 180.0);
457  double y = m_equatorialRadius * sin(angle * PI / 180.0);
458 
459  if (SetCoordinate(x, y)){
460 
461  adjustedLon = To360Domain(m_longitude);
462  if (adjustedLon > To360Domain(MinimumLongitude()) && correctedMinLon) {
463  adjustedLon -= 360;
464  }
465  if (m_latitude <= m_maximumLatitude &&
466  adjustedLon <= adjustedMaxLon &&
468  adjustedLon >= adjustedMinLon) {
469  m_minimumX = qMin(x, m_minimumX);
470  m_maximumX = qMax(x, m_maximumX);
471  m_minimumY = qMin(y, m_minimumY);
472  m_maximumY = qMax(y, m_maximumY);
473  XYRangeCheck(m_latitude, adjustedLon);
474  }
475  }
476  }
477  // Make sure everything is ordered
478  if (m_minimumX >= m_maximumX ||
480  return false;
481 
482  // Return X/Y min/maxs
483  minX = m_minimumX;
484  maxX = m_maximumX;
485  minY = m_minimumY;
486  maxY = m_maximumY;
487 
488  return true;
489  }
490 
491 
498  PvlGroup mapping = TProjection::Mapping();
499 
500  mapping += m_mappingGrp["CenterLatitude"];
501  mapping += m_mappingGrp["CenterLongitude"];
502 
503  return mapping;
504  }
505 
513  mapping += m_mappingGrp["CenterLatitude"];
514 
515  return mapping;
516  }
517 
525 
526  mapping += m_mappingGrp["CenterLongitude"];
527 
528  return mapping;
529  }
530 } // end namespace isis
531 
546  bool allowDefaults) {
547  return new Isis::Orthographic(lab, allowDefaults);
548 }
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
PvlGroup MappingLongitudes()
This function returns the longitude keywords that this projection uses.
double MaximumLongitude() const
This returns the maximum longitude of the area of interest.
PvlGroupIterator findGroup(const QString &name, PvlGroupIterator beg, PvlGroupIterator end)
Find a group with the specified name, within these indexes.
Definition: PvlObject.h:141
const double PI
The mathematical constant PI.
Definition: Constants.h:56
Longitude values increase in the westerly direction.
Definition: TProjection.h:241
double m_centerLongitude
The center longitude for the map projection.
Definition: Orthographic.h:110
Base class for Map TProjections.
Definition: TProjection.h:182
Orthographic Map Projection.
Definition: Orthographic.h:91
double m_cosph0
Cosine of the center latitude.
Definition: Orthographic.h:113
const double HALFPI
The mathematical constant PI/2.
Definition: Constants.h:57
Namespace for the standard library.
Isis::Projection * OrthographicPlugin(Isis::Pvl &lab, bool allowDefaults)
This is the function that is called in order to instantiate an Orthographic object.
Search child objects.
Definition: PvlObject.h:170
double GetX() const
Calculates the unrotated form of current x value.
Definition: Projection.cpp:833
double m_minimumX
The data elements m_minimumX, m_minimumY, m_maximumX, and m_maximumY are convience data elements when...
Definition: Projection.h:333
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:226
A type of error that occurred when performing an actual I/O operation.
Definition: IException.h:171
void SetComputedXY(double x, double y)
This protected method is a helper for derived classes.
Definition: Projection.cpp:795
double m_latitude
This contains the currently set latitude value.
Definition: TProjection.h:332
virtual PvlGroup MappingLatitudes()
This function returns the latitude keywords that this projection uses.
double MinimumLongitude() const
This returns the minimum longitude of the area of interest.
bool XYRange(double &minX, double &maxX, double &minY, double &maxY)
This method is used to determine the x/y range which completely covers the area of interest specified...
double m_maximumY
See minimumX description.
Definition: Projection.h:344
double m_longitude
This contains the currently set longitude value.
Definition: TProjection.h:334
bool IsPlanetocentric() const
This indicates if the latitude type is planetocentric (as opposed to planetographic).
double m_maximumLongitude
Contains the maximum longitude for the entire ground range.
Definition: TProjection.h:376
double TrueScaleLatitude() const
Returns the center latitude, in degrees.
double m_minimumLatitude
Contains the minimum latitude for the entire ground range.
Definition: TProjection.h:370
double m_maximumLatitude
Contains the maximum latitude for the entire ground range.
Definition: TProjection.h:372
Base class for Map Projections.
Definition: Projection.h:171
double m_minimumY
See minimumX description.
Definition: Projection.h:343
PvlGroup MappingLatitudes()
This function returns the latitude keywords that this projection uses.
double m_equatorialRadius
Polar radius of the target.
Definition: TProjection.h:351
int m_longitudeDomain
This integer is either 180 or 360 and is read from the labels.
Definition: TProjection.h:347
static double To180Domain(const double lon)
This method converts a longitude into the -180 to 180 domain.
Contains multiple PvlContainers.
Definition: PvlGroup.h:57
QString Name() const
Returns the name of the map projection, "Orthographic".
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
A type of error that could only have occurred due to a mistake on the user&#39;s part (e...
Definition: IException.h:142
A single keyword-value pair.
Definition: PvlKeyword.h:98
bool operator==(const Projection &proj)
Compares two Projection objects to see if they are equal.
Container for cube-like labels.
Definition: Pvl.h:135
double ToPlanetographic(const double lat) const
This method converts a planetocentric latitude to a planetographic latitude.
const double E
Sets some basic constants for use in ISIS programming.
Definition: Constants.h:55
virtual PvlGroup Mapping()
This function returns the keywords that this projection uses.
bool m_good
Indicates if the contents of m_x, m_y, m_latitude, and m_longitude are valid.
Definition: Projection.h:316
QString Version() const
Returns the version of the map projection.
static double To360Domain(const double lon)
This method converts a longitude into the 0 to 360 domain.
PvlGroup Mapping()
This function returns the keywords that this projection uses.
LongitudeDirection m_longitudeDirection
An enumerated type indicating the LongitudeDirection read from the labels.
Definition: TProjection.h:340
Isis exception class.
Definition: IException.h:107
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
double m_centerLatitude
The center latitude for the map projection.
Definition: Orthographic.h:111
double GetY() const
Calculates the unrotated form of the current y value.
Definition: Projection.cpp:844
bool SetCoordinate(const double x, const double y)
This method is used to set the projection x/y.
double ToPlanetocentric(const double lat) const
This method converts a planetographic latitude to a planetocentric latitude.
double m_sinph0
Sine of the center latitude.
Definition: Orthographic.h:112
virtual PvlGroup MappingLongitudes()
This function returns the longitude keywords that this projection uses.
void SetXY(double x, double y)
This protected method is a helper for derived classes.
Definition: Projection.cpp:819
void XYRangeCheck(const double latitude, const double longitude)
This convience function is established to assist in the development of the XYRange virtual method...
bool SetGround(const double lat, const double lon)
This method is used to set the latitude/longitude (assumed to be of the correct LatitudeType, LongitudeDirection, and LongitudeDomain.
double m_minimumLongitude
Contains the minimum longitude for the entire ground range.
Definition: TProjection.h:374
double m_maximumX
See minimumX description.
Definition: Projection.h:342
~Orthographic()
Destroys the Orthographic object.
PvlGroup m_mappingGrp
Mapping group that created this projection.
Definition: Projection.h:345