USGS

Isis 3.0 Object Programmers' Reference

Home

LambertConformal.cpp
Go to the documentation of this file.
1 
23 #include "LambertConformal.h"
24 
25 #include <cmath>
26 #include <cfloat>
27 
28 #include "Constants.h"
29 #include "IException.h"
30 #include "IString.h"
31 #include "Projection.h"
32 #include "Pvl.h"
33 #include "PvlGroup.h"
34 #include "PvlKeyword.h"
35 
36 using namespace std;
37 namespace Isis {
57  LambertConformal::LambertConformal(Pvl &label, bool allowDefaults) :
58  TProjection::TProjection(label) {
59  try {
60  // Try to read the mapping group
61  PvlGroup &mapGroup = label.findGroup("Mapping", Pvl::Traverse);
62 
63  // Compute and write the default center longitude if allowed and
64  // necessary
65  if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLongitude"))) {
66  double lon = (m_minimumLongitude + m_maximumLongitude) / 2.0;
67  mapGroup += PvlKeyword("CenterLongitude", toString(lon));
68  }
69 
70  // Compute and write the default center latitude if allowed and
71  // necessary
72  if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLatitude"))) {
73  double lat = (m_minimumLatitude + m_maximumLatitude) / 2.0;
74  mapGroup += PvlKeyword("CenterLatitude", toString(lat));
75  }
76 
77  // Get the center longitude & latitude
78  m_centerLongitude = mapGroup["CenterLongitude"];
79  m_centerLatitude = mapGroup["CenterLatitude"];
80  if (IsPlanetocentric()) {
82  }
83 
84  // Test to make sure center longitude is valid
85  if (fabs(m_centerLongitude) > 360.0) {
86  IString message = "Central Longitude [" + IString(m_centerLongitude);
87  message += "] must be between -360 and 360";
88  throw IException(IException::Unknown, message, _FILEINFO_);
89  }
90 
91  // convert to radians, adjust for longitude direction
92  m_centerLongitude *= PI / 180.0;
94 
95  // Get the standard parallels & convert them to ographic
96  m_par1 = mapGroup["FirstStandardParallel"];
97  if (IsPlanetocentric()) {
99  }
100  m_par2 = mapGroup["SecondStandardParallel"];
101  if (IsPlanetocentric()) {
103  }
104 
105  // Test to make sure standard parallels are valid
106  if (fabs(m_par1) > 90.0 || fabs(m_par2) > 90.0) {
107  QString message = "Standard Parallels must between -90 and 90";
108  throw IException(IException::Unknown, message, _FILEINFO_);
109  }
110  if (fabs(m_par1 + m_par2) < DBL_EPSILON) {
111  QString message = "Standard Parallels cannot be symmetric to the equator";
112  throw IException(IException::Unknown, message, _FILEINFO_);
113  }
114  // Removed because this test only works for northern hemisphere
115  // Just reorder the parallels so p1 is at the larger radius of the two
116  //if (m_par1 > m_par2) {
117  // QString message = "Standard Parallels must be ordered";
118  // throw IException::Message(IException::Projection,message,_FILEINFO_);
119  //}
120 
121  // Reorder the parallels so p1 is closer to the equator than p2
122  // Therefore p2 is nearest the apex of the cone
123  if (fabs(m_par1) > fabs(m_par2)) {
124  double tmp = m_par2;
125  m_par2 = m_par1;
126  m_par1 = tmp;
127  }
128 
129  // Test to make sure center latitude is valid
130  // The pole opposite the apex can not be used as the clat (i.e., origin of
131  // the projection) it projects to infinity
132  // Given: p2 is closer to the apex than p1, and p2 must be on the same
133  // side of the equator as the apex (due to the reording of p1 and p2 above)
134  // Test for cone pointed south "v"
135  if ((m_par2 < 0.0) && (fabs(90.0 - m_centerLatitude) < DBL_EPSILON)) {
136  IString message = "Center Latitude [" + IString(m_centerLatitude);
137  message += "] is not valid, it projects to infinity "
138  "for standard parallels [";
139  message += IString(m_par1) + "," + IString(m_par2) + "]";
140  throw IException(IException::Unknown, message, _FILEINFO_);
141  }
142  // Test for cone pointed north "^"
143  else if ((m_par2 > 0.0) && (fabs(-90.0 - m_centerLatitude) < DBL_EPSILON)) {
144  IString message = "Center Latitude [" + IString(m_centerLatitude);
145  message += "] is not valid, it projects to infinity "
146  "for standard parallels [";
147  message += IString(m_par1) + "," + IString(m_par2) + "]";
148  throw IException(IException::Unknown, message, _FILEINFO_);
149  }
150  // convert clat to radians
151  m_centerLatitude *= PI / 180.0;
152 
153  // Convert standard parallels to radians
154  m_par1 *= PI / 180.0;
155  m_par2 *= PI / 180.0;
156 
157  // Compute the Snyder's m and t values for the standard parallels and the
158  // center latitude
159  double sinpar1 = sin(m_par1);
160  double cospar1 = cos(m_par1);
161  double m1 = mCompute(sinpar1, cospar1);
162  double t1 = tCompute(m_par1, sinpar1);
163 
164  double sinpar2 = sin(m_par2);
165  double cospar2 = cos(m_par2);
166  double m2 = mCompute(sinpar2, cospar2);
167  double t2 = tCompute(m_par2, sinpar2);
168 
169  double sinclat = sin(m_centerLatitude);
170  double tclat = tCompute(m_centerLatitude, sinclat);
171 
172  // Calculate Snyder's n, f, and rho
173  if (fabs(m_par1 - m_par2) >= DBL_EPSILON) {
174  m_n = log(m1 / m2) / log(t1 / t2);
175  }
176  else {
177  m_n = sinpar1;
178  }
179  m_f = m1 / (m_n * pow(t1, m_n));
180  m_rho = m_equatorialRadius * m_f * pow(tclat, m_n);
181  }
182  catch(IException &e) {
183  QString message = "Invalid label group [Mapping]";
184  throw IException(e, IException::Io, message, _FILEINFO_);
185  }
186  }
187 
190  }
191 
201  if (!Projection::operator==(proj)) return false;
202  // dont do the below it is a recusive plunge
203  // if (Projection::operator!=(proj)) return false;
204  LambertConformal *lamb = (LambertConformal *) &proj;
205  if ((lamb->m_centerLongitude != m_centerLongitude) ||
206  (lamb->m_centerLatitude != m_centerLatitude)) return false;
207  return true;
208  }
209 
215  QString LambertConformal::Name() const {
216  return "LambertConformal";
217  }
218 
225  QString LambertConformal::Version() const {
226  return "1.0";
227  }
228 
236  if (m_par1 > m_par2) return m_par2 * 180.0 / PI;
237  else return m_par1 * 180.0 / PI;
238  }
239 
252  bool LambertConformal::SetGround(const double lat, const double lon) {
253  // Convert longitude to radians & clean up
254  m_longitude = lon;
255  double lonRadians = lon * PI / 180.0;
256  if (m_longitudeDirection == PositiveWest) lonRadians *= -1.0;
257 
258  // Now convert latitude to radians & clean up ... it must be planetographic
259  m_latitude = lat;
260  double latRadians = lat;
261  if (IsPlanetocentric()) latRadians = ToPlanetographic(latRadians);
262  latRadians *= PI / 180.0;
263 
264  // Check for special cases & calculate rh, theta, and snyder t
265  double rh;
266  if (fabs(fabs(latRadians) - HALFPI) < DBL_EPSILON) {
267  // Lat/Lon point cannot be projected
268  if (latRadians *m_n <= 0.0) {
269  m_good = false;
270  return m_good;
271  }
272  else rh = 0.0;
273  }
274  else {
275  double sinlat = sin(latRadians);
276  // Lat/Lon point cannot be projected
277  double t = tCompute(latRadians, sinlat);
278  rh = m_equatorialRadius * m_f * pow(t, m_n);
279  }
280  double theta = m_n * (lonRadians - m_centerLongitude);
281 
282  // Compute the coordinate
283  double x = rh * sin(theta);
284  double y = m_rho - rh * cos(theta);
285  SetComputedXY(x, y);
286 
287  m_good = true;
288  return m_good;
289  }
290 
304  bool LambertConformal::SetCoordinate(const double x, const double y) {
305  // Save the coordinate
306  SetXY(x, y);
307 
308  // Get the sign of Snyder's n
309  double sign;
310  if (m_n >= 0) sign = 1.0;
311  else sign = -1.0;
312 
313  double temp = m_rho - GetY();
314  double rh = sign * sqrt(GetX() * GetX() + temp * temp);
315 
316  double theta;
317  if (rh != 0) theta = atan2(sign * GetX(), sign * temp);
318  else theta = 0.0;
319 
320  // Compute latitude and longitude
321  if (rh != 0 || m_n > 0) {
322  double t = pow(rh / (m_equatorialRadius * m_f), 1.0 / m_n);
323  m_latitude = phi2Compute(t);
324  }
325  else m_latitude = -HALFPI;
326  m_longitude = theta / m_n + m_centerLongitude;
327 
328 
329  // Convert to degrees
330  m_latitude *= 180.0 / PI;
331  m_longitude *= 180.0 / PI;
332 
333  // Cleanup the longitude
335  // These need to be done for circular type projections
336  // m_longitude = To360Domain (m_longitude);
337  // if (m_longitudeDomain == 180) m_longitude = To180Domain(m_longitude);
338 
339  // Cleanup the latitude
341 
342  m_good = true;
343  return m_good;
344  }
345 
377  bool LambertConformal::XYRange(double &minX, double &maxX,
378  double &minY, double &maxY) {
379 
380  // Test the four corners
385 
386  // Decide which pole the apex of the cone is above
387  // Remember p1 is now closest to the equator and p2 is closest to one of the
388  // poles
389  bool north_hemi = true;
390  // Stuart Sides 2008-08-15
391  // This test was removed because the reordering of p1 and p2 in the
392  // constructor made it incorrect
393  //if (fabs(m_par1) > fabs(m_par2)) north_hemi = false;
394  if (m_par2 < 0.0) north_hemi = false;
395  if ((m_par1 == m_par2) && (m_par1 < 0.0)) north_hemi = false;
396 
397  double cLonDeg = m_centerLongitude * 180.0 / PI;
398 
399  // This is needed because the SetGround class applies the PositiveWest
400  // adjustment which was already done in the constructor.
401  if (m_longitudeDirection == PositiveWest) cLonDeg = cLonDeg * -1.0;
402 
403  double pole_north, min_lat_north, max_lat_north, londiff;
404  // North Pole
405  if (north_hemi) {
406  m_latitude = 90.0;
407  m_longitude = cLonDeg;
408 
409  //Unable to project at the pole
411  m_good = false;
412  return m_good;
413  }
414 
415  pole_north = YCoord();
417 
418  //Unable to project at the pole
420  m_good = false;
421  return m_good;
422  }
423 
424  min_lat_north = YCoord();
425  double y = min_lat_north + 2.0 * (pole_north - min_lat_north);
426 
427  //Unable to project opposite the center longitude
428  if (!SetCoordinate(XCoord(), y)) {
429  m_good = false;
430  return m_good;
431  }
432 
433  londiff = fabs(cLonDeg - m_longitude) / 2.0;
434  m_longitude = cLonDeg - londiff;
435  for (int i = 0; i < 3; i++) {
440  }
441  m_longitude += londiff;
442  }
443 
444  }
445  // South Pole
446  else {
447  m_latitude = -90.0;
448  m_longitude = cLonDeg;
449 
450  //Unable to project at the pole
452  m_good = false;
453  return m_good;
454  }
455 
456  pole_north = YCoord();
458 
459  //Unable to project at the pole
461  m_good = false;
462  return m_good;
463  }
464 
465  max_lat_north = YCoord();
466  double y = max_lat_north - 2.0 * (max_lat_north - pole_north);
467 
468  //Unable to project opposite the center longitude
469  if (!SetCoordinate(XCoord(), y)) {
470  m_good = false;
471  return m_good;
472  }
473 
474  londiff = fabs(cLonDeg - m_longitude) / 2.0;
475  m_longitude = cLonDeg - londiff;
476  for (int i = 0; i < 3; i++) {
481  }
482  m_longitude += londiff;
483  }
484  }
485 
486  // Make sure everything is ordered
487  if (m_minimumX >= m_maximumX) return false;
488  if (m_minimumY >= m_maximumY) return false;
489 
490  // Return X/Y min/maxs
491  minX = m_minimumX;
492  maxX = m_maximumX;
493  minY = m_minimumY;
494  maxY = m_maximumY;
495  return true;
496  }
497 
498 
505  PvlGroup mapping = TProjection::Mapping();
506 
507  mapping += m_mappingGrp["CenterLatitude"];
508  mapping += m_mappingGrp["CenterLongitude"];
509  mapping += m_mappingGrp["FirstStandardParallel"];
510  mapping += m_mappingGrp["SecondStandardParallel"];
511 
512  return mapping;
513  }
514 
522 
523  mapping += m_mappingGrp["CenterLatitude"];
524  mapping += m_mappingGrp["FirstStandardParallel"];
525  mapping += m_mappingGrp["SecondStandardParallel"];
526 
527  return mapping;
528  }
529 
537 
538  mapping += m_mappingGrp["CenterLongitude"];
539 
540  return mapping;
541  }
542 } // end namespace isis
543 
558  bool allowDefaults) {
559  return new Isis::LambertConformal(lab, allowDefaults);
560 }
561