USGS

Isis 3.0 Object Programmers' Reference

Home

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  lat = lat;
413  lon = m_minimumLongitude;
414  XYRangeCheck(lat, lon);
415 
416  lat = lat;
417  lon = m_maximumLongitude;
418  XYRangeCheck(lat, lon);
419  }
420 
421  // Walk left and right edges
422  for (lon = m_minimumLongitude; lon <= m_maximumLongitude; lon += 0.01) {
423  lat = m_minimumLatitude;
424  lon = lon;
425  XYRangeCheck(lat, lon);
426 
427  lat = m_maximumLatitude;
428  lon = lon;
429  XYRangeCheck(lat, lon);
430  }
431 
432  /*
433  * Walk the limb.
434  * When the pair is valid and within the correct range, reassign min/max x/y.
435  *
436  * , - ~ ~ ~ - ,
437  * , ' ' ,
438  * , _______ ,
439  * , | | , <----- Walking the limb would not affect an
440  * , | | , image that does not extend to the
441  * , |_______| , limits of the projection.
442  * , ________________________,_
443  * , | , |
444  * ,| , |
445  * |,___________________,_'___| <-- This corner would wrap around.
446  * ' - , _ _ _ , ' The image would rotate because the
447  * center lat was closer to the pole.
448  * This would allow for a more completely
449  * longitude range.
450  * _o__o__
451  * o| |o <------------This is the rotated view. If the image
452  * o |_______| o limits extend over the pole (due to the
453  * o * o offset of the center lat) it is possible
454  * o o to have a lon range that is larger than
455  * o o 180 degrees.
456  *
457  */
458 
459  for (double angle = 0.0; angle <= 360.0; angle += 0.01) {
460  double x = m_equatorialRadius * cos(angle * PI / 180.0);
461  double y = m_equatorialRadius * sin(angle * PI / 180.0);
462 
463  if (SetCoordinate(x, y)){
464 
465  adjustedLon = To360Domain(m_longitude);
466  if (adjustedLon > To360Domain(MinimumLongitude()) && correctedMinLon) {
467  adjustedLon -= 360;
468  }
469  if (m_latitude <= m_maximumLatitude &&
470  adjustedLon <= adjustedMaxLon &&
472  adjustedLon >= adjustedMinLon) {
473  m_minimumX = qMin(x, m_minimumX);
474  m_maximumX = qMax(x, m_maximumX);
475  m_minimumY = qMin(y, m_minimumY);
476  m_maximumY = qMax(y, m_maximumY);
477  XYRangeCheck(m_latitude, adjustedLon);
478  }
479  }
480  }
481  // Make sure everything is ordered
482  if (m_minimumX >= m_maximumX ||
484  return false;
485 
486  // Return X/Y min/maxs
487  minX = m_minimumX;
488  maxX = m_maximumX;
489  minY = m_minimumY;
490  maxY = m_maximumY;
491 
492  return true;
493  }
494 
495 
502  PvlGroup mapping = TProjection::Mapping();
503 
504  mapping += m_mappingGrp["CenterLatitude"];
505  mapping += m_mappingGrp["CenterLongitude"];
506 
507  return mapping;
508  }
509 
517  mapping += m_mappingGrp["CenterLatitude"];
518 
519  return mapping;
520  }
521 
529 
530  mapping += m_mappingGrp["CenterLongitude"];
531 
532  return mapping;
533  }
534 } // end namespace isis
535 
550  bool allowDefaults) {
551  return new Isis::Orthographic(lab, allowDefaults);
552 }
553