USGS

Isis 3.0 Object Programmers' Reference

Home

TransverseMercator.cpp
Go to the documentation of this file.
1 
23 #include "TransverseMercator.h"
24 
25 #include <cmath>
26 #include <cfloat>
27 
28 #include "Constants.h"
29 #include "IException.h"
30 #include "TProjection.h"
31 #include "Pvl.h"
32 #include "PvlGroup.h"
33 #include "PvlKeyword.h"
34 
35 using namespace std;
36 namespace Isis {
55  TransverseMercator::TransverseMercator(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  // Compute and write the default center longitude if allowed and
62  // necessary
63  if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLongitude"))) {
64  double lon = (m_minimumLongitude + m_maximumLongitude) / 2.0;
65  mapGroup += PvlKeyword("CenterLongitude", toString(lon));
66  }
67 
68  // Compute and write the default center latitude if allowed and
69  // necessary
70  if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLatitude"))) {
71  double lat = (m_minimumLatitude + m_maximumLatitude) / 2.0;
72  mapGroup += PvlKeyword("CenterLatitude", toString(lat));
73  }
74 
75  // Get the center longitude & latitude
76  m_centerLongitude = mapGroup["CenterLongitude"];
77  m_centerLatitude = mapGroup["CenterLatitude"];
78 
79  // make sure the center latitude value is valid
80  if (fabs(m_centerLatitude) >= 90.0) {
81  string msg = "Invalid Center Latitude Value. Must be between -90 and 90";
83  }
84 
85  // make sure the center longitude value is valid
86  if (fabs(m_centerLongitude) > 360.0) {
87  string msg = "Invalid Center Longitude Value. Must be between -360 and 360";
89  }
90 
91  // convert latitude to planetographic if it is planetocentric
92  if (IsPlanetocentric()) {
94  }
95 
96  // convert to radians and adjust for longitude direction
98  m_centerLatitude *= PI / 180.0;
99  m_centerLongitude *= PI / 180.0;
100 
101  // Compute other necessary variables. See Snyder, page 61
103  m_esp = m_eccsq;
104  m_e0 = 1.0 - 0.25 * m_eccsq * (1.0 + m_eccsq / 16.0 * (3.0 + 1.25 * m_eccsq));
105  m_e1 = 0.375 * m_eccsq * (1.0 + 0.25 * m_eccsq * (1.0 + 0.468750 * m_eccsq));
106  m_e2 = 0.058593750 * m_eccsq * m_eccsq * (1.0 + 0.750 * m_eccsq);
107  m_e3 = m_eccsq * m_eccsq * m_eccsq * (35.0 / 3072.0);
109  m_e2 * sin(4.0 * m_centerLatitude) - m_e3 * sin(6.0 * m_centerLatitude));
110 
111  // Set flag for sphere or ellipsiod
112  m_sph = true; // Sphere
113  if (Eccentricity() >= .00001) {
114  m_sph = false; // Ellipsoid
115  m_esp = m_eccsq / (1.0 - m_eccsq);
116  }
117 
118  // Get the scale factor
119  if ((allowDefaults) && (!mapGroup.hasKeyword("ScaleFactor"))) {
120  mapGroup += PvlKeyword("ScaleFactor", toString(1.0));
121  }
122  m_scalefactor = mapGroup["ScaleFactor"];
123  }
124  catch(IException &e) {
125  string message = "Invalid label group [Mapping]";
126  throw IException(e, IException::Io, message, _FILEINFO_);
127  }
128  }
129 
132  }
133 
143  if (!Projection::operator==(proj)) return false;
144  // dont do the below it is a recusive plunge
145  // if (Projection::operator!=(proj)) return false;
146  TransverseMercator *trans = (TransverseMercator *) &proj;
147  if ((trans->m_centerLongitude != m_centerLongitude) ||
148  (trans->m_centerLatitude != m_centerLatitude)) return false;
149  return true;
150  }
151 
157  QString TransverseMercator::Name() const {
158  return "TransverseMercator";
159  }
160 
167  QString TransverseMercator::Version() const {
168  return "1.0";
169  }
170 
183  bool TransverseMercator::SetGround(const double lat, const double lon) {
184  // Get longitude & fix direction
185  m_longitude = lon;
187 
188  double cLonDeg = m_centerLongitude * 180.0 / PI;
189  double deltaLon = m_longitude - cLonDeg;
190  while(deltaLon < -360.0) deltaLon += 360.0;
191  while(deltaLon > 360.0) deltaLon -= 360.0;
192  double deltaLonRads = deltaLon * PI / 180.0;
193 
194  // Now convert latitude to radians & clean up ... it must be planetographic
195  m_latitude = lat;
196  double latRadians = m_latitude * PI / 180.0;
197  if (IsPlanetocentric()) {
198  latRadians = ToPlanetographic(m_latitude) * PI / 180.0;
199  }
200 
201  // distance along the meridian fromthe Equator to the latitude phi
202  // see equation (3-21) on pages 61, 17.
203  double M = m_equatorialRadius * (m_e0 * latRadians
204  - m_e1 * sin(2.0 * latRadians)
205  + m_e2 * sin(4.0 * latRadians)
206  - m_e3 * sin(6.0 * latRadians));
207 
208  // Declare variables
209  const double epsilon = 1.0e-10;
210 
211  // Sphere Conversion
212  double x, y;
213  if (m_sph) {
214  double cosphi = cos(latRadians);
215  double b = cosphi * sin(deltaLonRads);
216 
217  // Point projects to infinity
218  if (fabs(fabs(b) - 1.0) <= epsilon) {
219  m_good = false;
220  return m_good;
221  }
222  x = 0.5 * m_equatorialRadius * m_scalefactor * log((1.0 + b) / (1.0 - b));
223 
224  // If arcosine argument is too close to 1, con=0.0 because arcosine(1)=0
225  double con = cosphi * cos(deltaLonRads) / sqrt(1.0 - b * b);
226  if (fabs(con) > 1.0) {
227  con = 0.0;
228  }
229  else {
230  con = acos(con);
231  }
232  if (m_latitude < 0.0) con = -con;
234  }
235 
236  // Ellipsoid Conversion
237  else {
238  if (fabs(HALFPI - fabs(latRadians)) < epsilon) {
239  x = 0.0;
240  y = m_scalefactor * (M - m_ml0);
241  }
242  else {
243  // Define Snyder's variables for ellipsoidal projections, page61
244  double sinphi = sin(latRadians);
245  double cosphi = cos(latRadians);
246  double A = cosphi * deltaLonRads; // see equation (8-15), page 61
247  double Asquared = A * A;
248  double C = m_esp * cosphi * cosphi; // see equation (8-14), page 61
249  double tanphi = tan(latRadians);
250  double T = tanphi * tanphi; // see equation (8-13), page 61
251  double N = m_equatorialRadius / sqrt(1.0 - m_eccsq * sinphi * sinphi);
252  // see equation (4-20), page 61
253 
254  x = m_scalefactor * N * A
255  * (1.0 + Asquared / 6.0 * (1.0 - T + C + Asquared / 20.0
256  *(5.0 - 18.0*T + T*T + 72.0*C - 58.0*m_esp)));
257  y = m_scalefactor
258  * (M - m_ml0 + N*tanphi*(Asquared * (0.5 + Asquared / 24.0 *
259  (5.0 - T + 9.0*C + 4.0*C*C + Asquared / 30.0
260  *(61.0 - 58.0*T + T*T + 600.0*C - 330.0*m_esp)))));
261  }
262  }
263 
264  SetComputedXY(x, y);
265  m_good = true;
266  return m_good;
267  }
268 
282  bool TransverseMercator::SetCoordinate(const double x, const double y) {
283  // Save the coordinate
284  SetXY(x, y);
285 
286  // Declare & Initialize variables
287  double f, g, h, temp, con, phi, dphi, sinphi, cosphi, tanphi;
288  double c, cs, t, ts, n, rp, d, ds;
289  const double epsilon = 1.0e-10;
290 
291  // Sphere Conversion
292  if (m_sph) {
293  f = exp(GetX() / (m_equatorialRadius * m_scalefactor));
294  g = 0.5 * (f - 1.0 / f);
296  h = cos(temp);
297  con = sqrt((1.0 - h * h) / (1.0 + g * g));
298  if (con > 1.0) con = 1.0;
299  if (con < -1.0) con = -1.0;
300  m_latitude = asin(con);
301  if (temp < 0.0) m_latitude = -m_latitude;
303  if (g != 0.0 || h != 0.0) {
304  m_longitude = atan2(g, h) + m_centerLongitude;
305  }
306  }
307 
308  // Ellipsoid Conversion
309  else if (!m_sph) {
310  con = (m_ml0 + GetY() / m_scalefactor) / m_equatorialRadius;
311  phi = con;
312  for (int i = 1; i < 7; i++) {
313  dphi = ((con + m_e1 * sin(2.0 * phi) - m_e2 * sin(4.0 * phi)
314  + m_e3 * sin(6.0 * phi)) / m_e0) - phi;
315  phi += dphi;
316  if (fabs(dphi) <= epsilon) break;
317  }
318 
319  // Didn't converge
320  if (fabs(dphi) > epsilon) {
321  m_good = false;
322  return m_good;
323  }
324  if (fabs(phi) >= HALFPI) {
325  if (GetY() >= 0.0) m_latitude = fabs(HALFPI);
326  if (GetY() < 0.0) m_latitude = - fabs(HALFPI);
328  }
329  else {
330  sinphi = sin(phi);
331  cosphi = cos(phi);
332  tanphi = tan(phi);
333  c = m_esp * cosphi * cosphi;
334  cs = c * c;
335  t = tanphi * tanphi;
336  ts = t * t;
337  con = 1.0 - m_eccsq * sinphi * sinphi;
338  n = m_equatorialRadius / sqrt(con);
339  rp = n * (1.0 - m_eccsq) / con;
340  d = GetX() / (n * m_scalefactor);
341  ds = d * d;
342  m_latitude = phi - (n * tanphi * ds / rp) * (0.5 - ds /
343  24.0 * (5.0 + 3.0 * t + 10.0 * c - 4.0 * cs - 9.0 *
344  m_esp - ds / 30.0 * (61.0 + 90.0 * t + 298.0 * c +
345  45.0 * ts - 252.0 * m_esp - 3.0 * cs)));
346 
347 
348  // Latitude cannot be greater than + or - halfpi radians (or 90 degrees)
349  if (fabs(m_latitude) > HALFPI) {
350  m_good = false;
351  return m_good;
352  }
354  + (d * (1.0 - ds / 6.0 *
355  (1.0 + 2.0 * t + c - ds / 20.0 * (5.0 - 2.0 * c +
356  28.0 * t - 3.0 * cs + 8.0 * m_esp + 24.0 * ts))) / cosphi);
357  }
358  }
359 
360  // Convert to Degrees
361  m_latitude *= 180.0 / PI;
362  m_longitude *= 180.0 / PI;
363 
364  // Cleanup the longitude
366  // These need to be done for circular type projections
369 
370  // Cleanup the latitude
372 
373  m_good = true;
374  return m_good;
375  }
376 
400  bool TransverseMercator::XYRange(double &minX, double &maxX,
401  double &minY, double &maxY) {
402  // Check the corners of the lat/lon range
407 
408  // convert center latitude to degrees & test
409  double clat = m_centerLatitude * 180.0 / PI;
410 
411  if (clat > m_minimumLatitude &&
412  clat < m_maximumLatitude) {
415  }
416 
417  // convert center longitude to degrees & test
418  double clon = m_centerLongitude * 180.0 / PI;
419  if (clon > m_minimumLongitude &&
420  clon < m_maximumLongitude) {
423  }
424 
425  // Make sure everything is ordered
426  if (m_minimumX >= m_maximumX) return false;
427  if (m_minimumY >= m_maximumY) return false;
428 
429  // Return X/Y min/maxs
430  minX = m_minimumX;
431  maxX = m_maximumX;
432  minY = m_minimumY;
433  maxY = m_maximumY;
434  return true;
435  }
436 
437 
444  PvlGroup mapping = TProjection::Mapping();
445 
446  mapping += m_mappingGrp["CenterLatitude"];
447  mapping += m_mappingGrp["CenterLongitude"];
448  mapping += m_mappingGrp["ScaleFactor"];
449 
450  return mapping;
451  }
452 
460 
461  mapping += m_mappingGrp["CenterLatitude"];
462 
463  return mapping;
464  }
465 
473 
474  mapping += m_mappingGrp["CenterLongitude"];
475 
476  return mapping;
477  }
478 } // end namespace isis
479 
495  bool allowDefaults) {
496  return new Isis::TransverseMercator(lab, allowDefaults);
497 }
498 
499