Isis 3 Programmer Reference
TProjection.cpp
Go to the documentation of this file.
1 
22 #include "TProjection.h"
23 
24 #include <QObject>
25 
26 #include <cfloat>
27 #include <cmath>
28 #include <iomanip>
29 #include <sstream>
30 #include <vector>
31 
32 #include <SpiceUsr.h>
33 
34 #include "Constants.h"
35 #include "Displacement.h"
36 #include "FileName.h"
37 #include "IException.h"
38 #include "IString.h"
39 #include "Longitude.h"
40 #include "NaifStatus.h"
41 #include "Pvl.h"
42 #include "PvlGroup.h"
43 #include "PvlKeyword.h"
44 #include "SpecialPixel.h"
45 #include "Target.h"
46 #include "WorldMapper.h"
47 
48 using namespace std;
49 namespace Isis {
105  TProjection::TProjection(Pvl &label) : Projection::Projection(label) {
106  try {
107  // Mapping group is read by the parent
108  // Get the radii from the EquatorialRadius and PolarRadius keywords
109  if ((m_mappingGrp.hasKeyword("EquatorialRadius")) &&
110  (m_mappingGrp.hasKeyword("PolarRadius"))) {
111  m_equatorialRadius = m_mappingGrp["EquatorialRadius"];
112  m_polarRadius = m_mappingGrp["PolarRadius"];
113  }
114  // Get the radii
115  try {
116  PvlGroup radii = Target::radiiGroup(label, m_mappingGrp);
117  m_equatorialRadius = radii["EquatorialRadius"];
118  m_polarRadius = radii["PolarRadius"];
119  }
120  catch (IException &e) {
121  QString msg = "Projection failed. No target radii are available "
122  "through keywords [EquatorialRadius and PolarRadius] "
123  "or [TargetName].";
124  throw IException(e, IException::Unknown, msg, _FILEINFO_);
125  }
126 
127  // Check the radii for validity
128  if (m_equatorialRadius <= 0.0) {
129  QString msg = "Projection failed. Invalid value for keyword "
130  "[EquatorialRadius]. It must be greater than zero";
132  }
133  if (m_polarRadius <= 0.0) {
134  QString msg = "Projection failed. Invalid value for keyword "
135  "[PolarRadius]. It must be greater than zero";
137  }
138 
139  // Get the LatitudeType
140  if ((QString) m_mappingGrp["LatitudeType"] == "Planetographic") {
142  }
143  else if ((QString) m_mappingGrp["LatitudeType"] == "Planetocentric") {
145  }
146  else {
147  QString msg = "Projection failed. Invalid value for keyword "
148  "[LatitudeType] must be "
149  "[Planetographic or Planetocentric]";
151  }
152 
153  // Get the LongitudeDirection
154  if ((QString) m_mappingGrp["LongitudeDirection"] == "PositiveWest") {
156  }
157  else if ((QString) m_mappingGrp["LongitudeDirection"] == "PositiveEast") {
159  }
160  else {
161  QString msg = "Projection failed. Invalid value for keyword "
162  "[LongitudeDirection] must be "
163  "[PositiveWest or PositiveEast]";
165  }
166 
167  // Get the LongitudeDomain
168  if ((QString) m_mappingGrp["LongitudeDomain"] == "360") {
169  m_longitudeDomain = 360;
170  }
171  else if ((QString) m_mappingGrp["LongitudeDomain"] == "180") {
172  m_longitudeDomain = 180;
173  }
174  else {
175  QString msg = "Projection failed. Invalid value for keyword "
176  "[LongitudeDomain] must be [180 or 360]";
178  }
179 
180  // Get the ground range if it exists
181  m_groundRangeGood = false;
182  if ((m_mappingGrp.hasKeyword("MinimumLatitude")) &&
183  (m_mappingGrp.hasKeyword("MaximumLatitude")) &&
184  (m_mappingGrp.hasKeyword("MinimumLongitude")) &&
185  (m_mappingGrp.hasKeyword("MaximumLongitude"))) {
186  m_minimumLatitude = m_mappingGrp["MinimumLatitude"];
187  m_maximumLatitude = m_mappingGrp["MaximumLatitude"];
188  m_minimumLongitude = m_mappingGrp["MinimumLongitude"];
189  m_maximumLongitude = m_mappingGrp["MaximumLongitude"];
190 
191  if ((m_minimumLatitude < -90.0) || (m_minimumLatitude > 90.0)) {
192  QString msg = "Projection failed. "
193  "[MinimumLatitude] of [" + toString(m_minimumLatitude)
194  + "] is outside the range of [-90:90]";
196  }
197 
198  if ((m_maximumLatitude < -90.0) || (m_maximumLatitude > 90.0)) {
199  QString msg = "Projection failed. "
200  "[MaximumLatitude] of [" + toString(m_maximumLatitude)
201  + "] is outside the range of [-90:90]";
203  }
204 
206  QString msg = "Projection failed. "
207  "[MinimumLatitude,MaximumLatitude] of ["
208  + toString(m_minimumLatitude) + ","
209  + toString(m_maximumLatitude) + "] are not "
210  + "properly ordered";
212  }
213 
215  QString msg = "Projection failed. "
216  "[MinimumLongitude,MaximumLongitude] of ["
217  + toString(m_minimumLongitude) + ","
218  + toString(m_maximumLongitude) + "] are not "
219  + "properly ordered";
221  }
222 
223  m_groundRangeGood = true;
224  }
225  else {
226  // if no ground range is given, initialize the min/max lat/lon to 0
227  m_minimumLatitude = 0.0;
228  m_maximumLatitude = 0.0;
229  m_minimumLongitude = 0.0;
230  m_maximumLongitude = 0.0;
231  }
232 
233  // Initialize miscellaneous protected data elements
235  QString msg = "Projection failed. Invalid keyword value(s). "
236  "[EquatorialRadius] = " + toString(m_equatorialRadius)
237  + " must be greater than or equal to [PolarRadius] = "
240  }
241  else {
242  m_eccentricity = 1.0 -
246  }
247 
248  // initialize the rest of the x,y,lat,lon member variables
249  m_latitude = Null;
250  m_longitude = Null;
251 
252  // If we made it to this point, we have what we need for a triaxial projection
254  }
255  catch (IException &e) {
256  QString msg = "Projection failed. Invalid label group [Mapping]";
257  throw IException(e, IException::Unknown, msg, _FILEINFO_);
258  }
259  }
260 
263  }
264 
276  if (!Projection::operator==(proj)) return false;
277  TProjection *tproj = (TProjection *) &proj;
278  if (EquatorialRadius() != tproj->EquatorialRadius()) return false;
279  if (PolarRadius() != tproj->PolarRadius()) return false;
280  if (IsPlanetocentric() != tproj->IsPlanetocentric()) return false;
281  if (IsPositiveWest() != tproj->IsPositiveWest()) return false;
282  return true;
283  }
284 
285 
293  return m_equatorialRadius;
294  }
295 
302  double TProjection::PolarRadius() const {
303  return m_polarRadius;
304  }
305 
319  double TProjection::Eccentricity() const {
320  return m_eccentricity;
321  }
322 
341  double TProjection::LocalRadius(double latitude) const {
342  if (latitude == Null) {
344  "Unable to calculate local radius. The given latitude value ["
345  + toString(latitude) + "] is invalid.",
346  _FILEINFO_);
347  }
348  double a = m_equatorialRadius;
349  double c = m_polarRadius;
350  // to save calculations, if the target is spherical, return the eq. rad
351  if (a - c < DBL_EPSILON) {
352  return a;
353  }
354  else {
355  double lat = latitude * PI / 180.0;
356  return a * c / sqrt(pow(c * cos(lat), 2) + pow(a * sin(lat), 2));
357  }
358  }
359 
368  double TProjection::LocalRadius() const {
369  return LocalRadius(m_latitude);
370  }
371 
372 
384  return 0.0;
385  }
386 
397  return false;
398  }
399 
408  return m_latitudeType == Planetocentric;
409  }
410 
419  return m_latitudeType == Planetographic;
420  }
421 
433  double TProjection::ToPlanetocentric(const double lat) const {
435  }
436 
449  double TProjection::ToPlanetocentric(const double lat,
450  double eRadius, double pRadius) {
451  if (lat == Null || abs(lat) > 90.0) {
453  "Unable to convert to Planetocentric. The given latitude value ["
454  + toString(lat) + "] is invalid.",
455  _FILEINFO_);
456  }
457  double mylat = lat;
458  if (abs(mylat) < 90.0) { // So tan doesn't fail
459  mylat *= PI / 180.0;
460  mylat = atan(tan(mylat) * (pRadius / eRadius) *
461  (pRadius / eRadius));
462  mylat *= 180.0 / PI;
463  }
464  return mylat;
465  }
466 
478  double TProjection::ToPlanetographic(const double lat) const {
480  }
481 
495  double TProjection::ToPlanetographic(double lat,
496  double eRadius, double pRadius) {
497  //Account for double rounding error.
498  if (qFuzzyCompare(fabs(lat), 90.0)) {
499  lat = qRound(lat);
500  }
501  if (lat == Null || fabs(lat) > 90.0) {
503  "Unable to convert to Planetographic. The given latitude value ["
504  + toString(lat) + "] is invalid.",
505  _FILEINFO_);
506  }
507  double mylat = lat;
508  if (fabs(mylat) < 90.0) { // So tan doesn't fail
509  mylat *= PI / 180.0;
510  mylat = atan(tan(mylat) * (eRadius / pRadius) *
511  (eRadius / pRadius));
512  mylat *= 180.0 / PI;
513  }
514  return mylat;
515  }
516 
524  if (m_latitudeType == Planetographic) return "Planetographic";
525  return "Planetocentric";
526  }
527 
537  }
538 
548  }
549 
563  double TProjection::ToPositiveEast(const double lon, const int domain) {
564  if (lon == Null) {
566  "Unable to convert to PositiveEast. The given longitude value ["
567  + toString(lon) + "] is invalid.",
568  _FILEINFO_);
569  }
570  double mylon = lon;
571 
572  mylon *= -1;
573 
574  if (domain == 360) {
575  mylon = To360Domain(mylon);
576  }
577  else if (domain == 180) {
578  mylon = To180Domain(mylon);
579  }
580  else {
581  QString msg = "Unable to convert longitude. Domain [" + toString(domain)
582  + "] is not 180 or 360.";
584  }
585 
586  return mylon;
587  }
588 
602  double TProjection::ToPositiveWest(const double lon, const int domain) {
603  if (lon == Null) {
605  "Unable to convert to PositiveWest. The given longitude value ["
606  + toString(lon) + "] is invalid.",
607  _FILEINFO_);
608  }
609  double mylon = lon;
610 
611  mylon *= -1;
612 
613  if (domain == 360) {
614  mylon = To360Domain(mylon);
615  }
616  else if (domain == 180) {
617  mylon = To180Domain(mylon);
618  }
619  else {
620  QString msg = "Unable to convert longitude. Domain [" + toString(domain)
621  + "] is not 180 or 360.";
623  }
624 
625  return mylon;
626  }
627 
636  if (m_longitudeDirection == PositiveEast) return "PositiveEast";
637  return "PositiveWest";
638  }
639 
648  return m_longitudeDomain == 180;
649  }
650 
659  return m_longitudeDomain == 360;
660  }
661 
672  double TProjection::To180Domain(const double lon) {
673  if (lon == Null) {
675  "Unable to convert to 180 degree domain. The given longitude value ["
676  + toString(lon) + "] is invalid.",
677  _FILEINFO_);
678  }
680  }
681 
690  double TProjection::To360Domain(const double lon) {
691  if (lon == Null) {
693  "Unable to convert to 360 degree domain. The given longitude value ["
694  + toString(lon) + "] is invalid.",
695  _FILEINFO_);
696  }
697  double result = lon;
698 
699  if ( (lon < 0.0 || lon > 360.0) &&
700  !qFuzzyCompare(lon, 0.0) && !qFuzzyCompare(lon, 360.0)) {
702  }
703 
704  return result;
705  }
706 
714  if (m_longitudeDomain == 360) return "360";
715  return "180";
716  }
717 
726  return m_minimumLatitude;
727  }
728 
737  return m_maximumLatitude;
738  }
739 
748  return m_minimumLongitude;
749  }
750 
759  return m_maximumLongitude;
760  }
761 
775  bool TProjection::SetGround(const double lat, const double lon) {
776  if (lat == Null || lon == Null) {
777  m_good = false;
778  return m_good;
779  }
780  else {
781  m_latitude = lat;
782  m_longitude = lon;
783  m_good = true;
784  SetComputedXY(lon, lat);
785  }
786  return m_good;
787  }
788 
804  bool TProjection::SetCoordinate(const double x, const double y) {
805  if (x == Null || y == Null) {
806  m_good = false;
807  }
808  else {
809  m_good = true;
810  SetXY(x, y);
811  m_latitude = XCoord();
812  m_longitude = YCoord();
813  }
814  return m_good;
815  }
816 
817 
826  double TProjection::Latitude() const {
827  return m_latitude;
828  }
829 
838  double TProjection::Longitude() const {
839  return m_longitude;
840  }
841 
842 
854  bool TProjection::SetUniversalGround(const double lat, const double lon) {
855  if (lat == Null || lon == Null) {
856  m_good = false;
857  return m_good;
858  }
859  // Deal with the longitude first
860  m_longitude = lon;
862  if (m_longitudeDomain == 180) {
864  }
865  else {
866  // Do this because longitudeDirection could cause (-360,0)
868  }
869 
870  // Deal with the latitude
873  }
874  else {
875  m_latitude = lat;
876  }
877 
878  // Now the lat/lon are in user defined coordinates so set them
880  }
881 
882 
894  bool TProjection::SetUnboundUniversalGround(const double lat, const double lon) {
895  if (lat == Null || lon == Null) {
896  m_good = false;
897  return m_good;
898  }
899  // Deal with the longitude first
900  m_longitude = lon;
902 
903  // Deal with the latitude
906  }
907  else {
908  m_latitude = lat;
909  }
910 
911  // Now the lat/lon are in user defined coordinates so set them
913  }
914 
915 
924  double lat = m_latitude;
926  return lat;
927  }
928 
938  double lon = m_longitude;
939  if (m_longitudeDirection == PositiveWest) lon = -lon;
940  lon = To360Domain(lon);
941  return lon;
942  }
943 
944 
955  double TProjection::Scale() const {
956  if (m_mapper != NULL) {
957  double lat = TrueScaleLatitude() * PI / 180.0;
958  double a = m_polarRadius * cos(lat);
959  double b = m_equatorialRadius * sin(lat);
960  double localRadius = m_equatorialRadius * m_polarRadius /
961  sqrt(a * a + b * b);
962 
963  return localRadius / m_mapper->Resolution();
964  }
965  else {
966  return 1.0;
967  }
968  }
969 
970 
1008  bool TProjection::XYRange(double &minX, double &maxX,
1009  double &minY, double &maxY) {
1010  if (minX == Null || maxX == Null || minY == Null || maxY == Null) {
1011  return false;
1012  }
1013  if (m_groundRangeGood) {
1014  minX = m_minimumLongitude;
1015  maxX = m_maximumLongitude;
1016  minY = m_minimumLatitude;
1017  maxY = m_maximumLatitude;
1018  return true;
1019  }
1020  return false;
1021  }
1022 
1077  void TProjection::XYRangeCheck(const double latitude, const double longitude) {
1078 
1079  if (latitude == Null || longitude == Null) {
1080  m_good = false;
1081  return;
1082  }
1083 
1084  SetGround(latitude, longitude);
1085  if (!IsGood()) return;
1086 
1087  if (XCoord() < m_minimumX) m_minimumX = XCoord();
1088  if (XCoord() > m_maximumX) m_maximumX = XCoord();
1089  if (YCoord() < m_minimumY) m_minimumY = YCoord();
1090  if (YCoord() > m_maximumY) m_maximumY = YCoord();
1091  return;
1092  }
1093 
1094 
1112  bool TProjection::inLongitudeRange(double minLon,
1113  double maxLon,
1114  double longitude) {
1115  // get the min/max range closest to 0.0 lon
1116  double adjustedLon = To360Domain(longitude);
1117  double adjustedMinLon = To360Domain(minLon);
1118  double adjustedMaxLon = To360Domain(maxLon);
1119 
1120  if (adjustedMinLon > adjustedMaxLon) {
1121  if (adjustedLon > adjustedMinLon) {
1122  adjustedLon -= 360;
1123  }
1124  adjustedMinLon -= 360;
1125  }
1126 
1127  // if this range covers all longitudes, then the given longitude is clearly in range
1128  if (qFuzzyCompare(maxLon - minLon, 360.0)) {
1129  return true;
1130  }
1131  else if (adjustedMinLon <= adjustedLon && adjustedLon <= adjustedMaxLon) {
1132  return true;
1133  }
1134  else {
1135  return false;
1136  }
1137  }
1138 
1139 
1155  bool TProjection::inLongitudeRange(double longitude) {
1156  return inLongitudeRange(MinimumLongitude(), MaximumLongitude(), longitude);
1157  }
1158 
1159 
1175  bool TProjection::inLatitudeRange(double latitude) {
1176  if (MaximumLatitude() - MinimumLatitude() == 180) {
1177  return true;
1178  }
1179  else if (MinimumLatitude() <= latitude && latitude <= MaximumLatitude()) {
1180  return true;
1181  }
1182  else {
1183  return false;
1184  }
1185  }
1186 
1187 
1210  bool TProjection::xyRangeOblique(double &minX, double &maxX,
1211  double &minY, double &maxY) {
1212  if (minX == Null || maxX == Null || minY == Null || maxY == Null) {
1213  return false;
1214  }
1215  //For oblique, we'll have to walk all 4 sides to find out min/max x/y values
1216  if (!HasGroundRange()) return false; // Don't have min/max lat/lon,
1217  //can't continue
1218 
1219  m_specialLatCases.clear();
1220  m_specialLonCases.clear();
1221 
1222  // First, search longitude for min X/Y
1223  double minFoundX1, minFoundX2;
1224  double minFoundY1, minFoundY2;
1225 
1226  // Search for minX between minlat and maxlat along minlon
1228  minFoundX1, MinimumLongitude(), true, true, true);
1229  // Search for minX between minlat and maxlat along maxlon
1231  minFoundX2, MaximumLongitude(), true, true, true);
1232  // Search for minY between minlat and maxlat along minlon
1234  minFoundY1, MinimumLongitude(), false, true, true);
1235  // Search for minY between minlat and maxlat along maxlon
1237  minFoundY2, MaximumLongitude(), false, true, true);
1238 
1239  // Second, search latitude for min X/Y
1240  double minFoundX3, minFoundX4;
1241  double minFoundY3, minFoundY4;
1242 
1243  // Search for minX between minlon and maxlon along minlat
1245  minFoundX3, MinimumLatitude(), true, false, true);
1246  // Search for minX between minlon and maxlon along maxlat
1248  minFoundX4, MaximumLatitude(), true, false, true);
1249  // Search for minY between minlon and maxlon along minlat
1251  minFoundY3, MinimumLatitude(), false, false, true);
1252  // Search for minY between minlon and maxlon along maxlat
1254  minFoundY4, MaximumLatitude(), false, false, true);
1255 
1256  // We've searched all possible minimums, go ahead and store the lowest
1257  double minFoundX5 = min(minFoundX1, minFoundX2);
1258  double minFoundX6 = min(minFoundX3, minFoundX4);
1259  m_minimumX = min(minFoundX5, minFoundX6);
1260 
1261  double minFoundY5 = min(minFoundY1, minFoundY2);
1262  double minFoundY6 = min(minFoundY3, minFoundY4);
1263  m_minimumY = min(minFoundY5, minFoundY6);
1264 
1265  // Search longitude for max X/Y
1266  double maxFoundX1, maxFoundX2;
1267  double maxFoundY1, maxFoundY2;
1268 
1269  // Search for maxX between minlat and maxlat along minlon
1271  maxFoundX1, MinimumLongitude(), true, true, false);
1272  // Search for maxX between minlat and maxlat along maxlon
1274  maxFoundX2, MaximumLongitude(), true, true, false);
1275  // Search for maxY between minlat and maxlat along minlon
1277  maxFoundY1, MinimumLongitude(), false, true, false);
1278  // Search for maxY between minlat and maxlat along maxlon
1280  maxFoundY2, MaximumLongitude(), false, true, false);
1281 
1282  // Search latitude for max X/Y
1283  double maxFoundX3, maxFoundX4;
1284  double maxFoundY3, maxFoundY4;
1285 
1286  // Search for maxX between minlon and maxlon along minlat
1288  maxFoundX3, MinimumLatitude(), true, false, false);
1289  // Search for maxX between minlon and maxlon along maxlat
1291  maxFoundX4, MaximumLatitude(), true, false, false);
1292  // Search for maxY between minlon and maxlon along minlat
1294  maxFoundY3, MinimumLatitude(), false, false, false);
1295  // Search for maxY between minlon and maxlon along maxlat
1297  maxFoundY4, MaximumLatitude(), false, false, false);
1298 
1299  // We've searched all possible maximums, go ahead and store the highest
1300  double maxFoundX5 = max(maxFoundX1, maxFoundX2);
1301  double maxFoundX6 = max(maxFoundX3, maxFoundX4);
1302  m_maximumX = max(maxFoundX5, maxFoundX6);
1303 
1304  double maxFoundY5 = max(maxFoundY1, maxFoundY2);
1305  double maxFoundY6 = max(maxFoundY3, maxFoundY4);
1306  m_maximumY = max(maxFoundY5, maxFoundY6);
1307 
1308  // Look along discontinuities for more extremes
1309  vector<double> specialLatCases = m_specialLatCases;
1310  for (unsigned int specialLatCase = 0;
1311  specialLatCase < specialLatCases.size();
1312  specialLatCase ++) {
1313  double minX, maxX, minY, maxY;
1314 
1315  // Search for minX between minlon and maxlon along latitude discontinuities
1317  minX, specialLatCases[specialLatCase], true, false, true);
1318  // Search for minY between minlon and maxlon along latitude discontinuities
1320  minY, specialLatCases[specialLatCase], false, false, true);
1321  // Search for maxX between minlon and maxlon along latitude discontinuities
1323  maxX, specialLatCases[specialLatCase], true, false, false);
1324  // Search for maxX between minlon and maxlon along latitude discontinuities
1326  maxY, specialLatCases[specialLatCase], false, false, false);
1327 
1328  m_minimumX = min(minX, m_minimumX);
1329  m_maximumX = max(maxX, m_maximumX);
1330  m_minimumY = min(minY, m_minimumY);
1331  m_maximumY = max(maxY, m_maximumY);
1332  }
1333 
1334  vector<double> specialLonCases = m_specialLonCases;
1335  for (unsigned int specialLonCase = 0;
1336  specialLonCase < specialLonCases.size();
1337  specialLonCase ++) {
1338  double minX, maxX, minY, maxY;
1339 
1340  // Search for minX between minlat and maxlat along longitude discontinuities
1342  minX, specialLonCases[specialLonCase], true, true, true);
1343  // Search for minY between minlat and maxlat along longitude discontinuities
1345  minY, specialLonCases[specialLonCase], false, true, true);
1346  // Search for maxX between minlat and maxlat along longitude discontinuities
1348  maxX, specialLonCases[specialLonCase], true, true, false);
1349  // Search for maxY between minlat and maxlat along longitude discontinuities
1351  maxY, specialLonCases[specialLonCase], false, true, false);
1352 
1353  m_minimumX = min(minX, m_minimumX);
1354  m_maximumX = max(maxX, m_maximumX);
1355  m_minimumY = min(minY, m_minimumY);
1356  m_maximumY = max(maxY, m_maximumY);
1357  }
1358 
1359  m_specialLatCases.clear();
1360  m_specialLonCases.clear();
1361 
1362  // Make sure everything is ordered
1363  if (m_minimumX >= m_maximumX) return false;
1364  if (m_minimumY >= m_maximumY) return false;
1365 
1366  // Return X/Y min/maxs
1367  minX = m_minimumX;
1368  maxX = m_maximumX;
1369  minY = m_minimumY;
1370  maxY = m_maximumY;
1371 
1372  return true;
1373  }
1374 
1412  void TProjection::doSearch(double minBorder, double maxBorder,
1413  double &extremeVal, const double constBorder,
1414  bool searchX, bool searchLongitude, bool findMin) {
1415  if (minBorder == Null || maxBorder == Null || constBorder == Null) {
1416  return;
1417  }
1418  const double TOLERANCE = PixelResolution()/2;
1419  const int NUM_ATTEMPTS = (unsigned int)DBL_DIG; // It's unsafe to go past
1420  // this precision
1421 
1422  double minBorderX, minBorderY, maxBorderX, maxBorderY;
1423  int attempts = 0;
1424 
1425  do {
1426  findExtreme(minBorder, maxBorder, minBorderX, minBorderY, maxBorderX,
1427  maxBorderY, constBorder, searchX, searchLongitude, findMin);
1428  if (minBorderX == Null && maxBorderX == Null
1429  && minBorderY == Null && maxBorderY == Null ) {
1430  attempts = NUM_ATTEMPTS;
1431  continue;
1432  }
1433  attempts ++;
1434  }
1435  while ((fabs(minBorderX - maxBorderX) > TOLERANCE
1436  || fabs(minBorderY - maxBorderY) > TOLERANCE)
1437  && (attempts < NUM_ATTEMPTS));
1438  // check both x and y distance in case symmetry of map
1439  // For example, if minBorderX = maxBorderX but minBorderY = -maxBorderY,
1440  // these points may not be close enough.
1441 
1442  if (attempts >= NUM_ATTEMPTS) {
1443  // We zoomed in on a discontinuity because our range never shrank, this
1444  // will need to be rechecked later.
1445  // *min and max border should be nearly identical, so it doesn't matter
1446  // which is used here
1447  if (searchLongitude) {
1448  m_specialLatCases.push_back(minBorder);
1449  }
1450  else {
1451  m_specialLonCases.push_back(minBorder);
1452  }
1453  }
1454 
1455  // These values will always be accurate, even over a discontinuity
1456  if (findMin) {
1457  if (searchX) extremeVal = min(minBorderX, maxBorderX);
1458  else extremeVal = min(minBorderY, maxBorderY);
1459  }
1460  else {
1461  if (searchX) extremeVal = max(minBorderX, maxBorderX);
1462  else extremeVal = max(minBorderY, maxBorderY);
1463  }
1464  return;
1465  }
1466 
1526  void TProjection::findExtreme(double &minBorder, double &maxBorder,
1527  double &minBorderX, double &minBorderY,
1528  double &maxBorderX, double &maxBorderY,
1529  const double constBorder, bool searchX,
1530  bool searchLongitude, bool findMin) {
1531  if (minBorder == Null || maxBorder == Null || constBorder == Null) {
1532  minBorderX = Null;
1533  minBorderY = minBorderX;
1534  minBorderY = minBorderX;
1535  return;
1536  }
1537  if (!searchLongitude && (fabs(fabs(constBorder) - 90.0) < DBL_EPSILON)) {
1538  // it is impossible to search "along" a pole
1539  setSearchGround(minBorder, constBorder, searchLongitude);
1540  minBorderX = XCoord();
1541  minBorderY = YCoord();
1542  maxBorderY = minBorderY;
1543  return;
1544  }
1545  // Always do 10 steps
1546  const double STEP_SIZE = (maxBorder - minBorder) / 10.0;
1547  const double LOOP_END = maxBorder + (STEP_SIZE / 2.0); // This ensures we do
1548  // all of the steps
1549  // properly
1550  double currBorderVal = minBorder;
1551  setSearchGround(minBorder, constBorder, searchLongitude);
1552 
1553  // this makes sure that the initial currBorderVal is valid before entering
1554  // the loop below
1555  if (!m_good){
1556  // minBorder = currBorderVal+STEP_SIZE < LOOP_END until setGround is good?
1557  // then, if still not good return?
1558  while (!m_good && currBorderVal <= LOOP_END) {
1559  currBorderVal+=STEP_SIZE;
1560  if (searchLongitude && (currBorderVal - 90.0 > DBL_EPSILON)) {
1561  currBorderVal = 90.0;
1562  }
1563  setSearchGround(currBorderVal, constBorder, searchLongitude);
1564  }
1565  if (!m_good) {
1566  minBorderX = Null;
1567  minBorderY = Null;
1568  return;
1569  }
1570  }
1571 
1572  // save the values of three consecutive steps from the minBorder towards
1573  // the maxBorder along the constBorder. initialize these three border
1574  // values (the non-constant lat or lon)
1575  double border1 = currBorderVal;
1576  double border2 = currBorderVal;
1577  double border3 = currBorderVal;
1578 
1579  // save the coordinate (x or y) values that correspond to the first
1580  // two borders that are being saved.
1581  // initialize these two coordinate values (x or y)
1582  double value1 = (searchX) ? XCoord() : YCoord();
1583  double value2 = value1;
1584 
1585  // initialize the extreme coordinate value
1586  // -- this is the largest coordinate value found so far
1587  double extremeVal2 = value2;
1588 
1589  // initialize the extreme border values
1590  // -- these are the borders on either side of the extreme coordinate value
1591  double extremeBorder1 = minBorder;
1592  double extremeBorder3 = minBorder;
1593 
1594  while (currBorderVal <= LOOP_END) {
1595 
1596  // this conditional was added to prevent trying to SetGround with an
1597  // invalid latitude greater than 90 degrees. There is no need check for
1598  // latitude less than -90 since we start at the minBorder (already
1599  // assumed to be valid) and step forward toward (and possibly past)
1600  // maxBorder
1601  if (searchLongitude && (currBorderVal - 90.0 > DBL_EPSILON)) {
1602  currBorderVal = 90.0;
1603  }
1604 
1605  // update the current border value along constBorder
1606  currBorderVal += STEP_SIZE;
1607  setSearchGround(currBorderVal, constBorder, searchLongitude);
1608  if (!m_good){
1609  continue;
1610  }
1611 
1612  // update the border and coordinate values
1613  border3 = border2;
1614  border2 = border1;
1615  border1 = currBorderVal;
1616  value2 = value1;
1617  value1 = (searchX) ? XCoord() : YCoord();
1618 
1619  if ((findMin && value2 < extremeVal2)
1620  || (!findMin && value2 > extremeVal2)) {
1621  // Compare the coordinate value associated with the center border with
1622  // the current extreme. If the updated coordinate value is more extreme
1623  // (smaller or larger, depending on findMin), then we update the
1624  // extremeVal and it's borders.
1625  extremeVal2 = value2;
1626 
1627  extremeBorder3 = border3;
1628  extremeBorder1 = border1;
1629  }
1630  }
1631 
1632  // update min/max border values to the values on either side of the most
1633  // extreme coordinate found in this call to this method
1634 
1635  minBorder = extremeBorder3; // Border 3 is lagging and thus smaller
1636 
1637  // since the loop steps past the original maxBorder, we want to retain
1638  // the original maxBorder value so we don't go outside of the original
1639  // min/max range given
1640  if (extremeBorder1 <= maxBorder ) {
1641  maxBorder = extremeBorder1; // Border 1 is leading and thus larger
1642  }
1643 
1644  // update minBorder coordinate values
1645  setSearchGround(minBorder, constBorder, searchLongitude);
1646  // if (!m_good){
1647  // this should not happen since minBorder has already been verified in
1648  // the while loop above
1649  // }
1650 
1651  minBorderX = XCoord();
1652  minBorderY = YCoord();
1653 
1654  // update maxBorder coordinate values
1655  setSearchGround(maxBorder, constBorder, searchLongitude);
1656  // if (!m_good){
1657  // this should not happen since maxBorder has already been verified in
1658  // the while loop above
1659  // }
1660 
1661  maxBorderX = XCoord();
1662  maxBorderY = YCoord();
1663  return;
1664  }
1665 
1688  void TProjection::setSearchGround(const double variableBorder,
1689  const double constBorder,
1690  bool variableIsLat) {
1691  if (variableBorder == Null || constBorder == Null) {
1692  return;
1693  }
1694  double lat, lon;
1695  if (variableIsLat) {
1696  lat = variableBorder;
1697  lon = constBorder;
1698  }
1699  else {
1700  lat = constBorder;
1701  lon = variableBorder;
1702  }
1703  SetGround(lat, lon);
1704  return;
1705  }
1706 
1707 
1714  PvlGroup mapping("Mapping");
1715 
1716  QStringList keyNames;
1717  keyNames << "TargetName" << "ProjectionName" << "EquatorialRadius" << "PolarRadius"
1718  << "LatitudeType" << "LongitudeDirection" << "LongitudeDomain"
1719  << "PixelResolution" << "Scale" << "UpperLeftCornerX" << "UpperLeftCornerY"
1720  << "MinimumLatitude" << "MaximumLatitude" << "MinimumLongitude" << "MaximumLongitude"
1721  << "Rotation";
1722 
1723  foreach (QString keyName, keyNames) {
1724  if (m_mappingGrp.hasKeyword(keyName)) {
1725  mapping += m_mappingGrp[keyName];
1726  }
1727  }
1728 
1729  return mapping;
1730  }
1731 
1732 
1739  PvlGroup mapping("Mapping");
1740 
1741  if (HasGroundRange()) {
1742  mapping += m_mappingGrp["MinimumLatitude"];
1743  mapping += m_mappingGrp["MaximumLatitude"];
1744  }
1745 
1746  return mapping;
1747  }
1748 
1755  PvlGroup mapping("Mapping");
1756 
1757  if (HasGroundRange()) {
1758  mapping += m_mappingGrp["MinimumLongitude"];
1759  mapping += m_mappingGrp["MaximumLongitude"];
1760  }
1761 
1762  return mapping;
1763  }
1764 
1785  double TProjection::qCompute(const double sinPhi) const {
1786  if (m_eccentricity < DBL_EPSILON) {
1787  QString msg = "Snyder's q variable should only be computed for "
1788  "ellipsoidal projections.";
1790  }
1791  double eSinPhi = m_eccentricity * sinPhi;
1792  return (1 - m_eccentricity * m_eccentricity)
1793  * (sinPhi / (1 - eSinPhi * eSinPhi)
1794  - 1 / (2 * m_eccentricity) * log( (1 - eSinPhi) / (1 + eSinPhi) ));
1795  // Note: We know that q is well defined since
1796  // 0 < e < 1 and -1 <= sin(phi) <= 1
1797  // implies that -1 < e*sin(phi) < 1
1798  // Thus, there are no 0 denominators and the log domain is
1799  // satisfied, (1-e*sin(phi))/(1+e*sin(phi)) > 0
1800  }
1801 
1818  double TProjection::phi2Compute(const double t) const {
1819  double localPhi = HALFPI - 2.0 * atan(t);
1820  double halfEcc = 0.5 * Eccentricity();
1821  double difference = DBL_MAX;
1822  int iteration = 0;
1823  // a failure in this loop means an exception will be thrown, which is
1824  // an expensive operation. So letting let the loop iterate quite a bit
1825  // is not a big deal. Also, the user will be unable to project at all
1826  // if this fails so more reason to let this loop iterate: better to
1827  // function slow than not at all.
1828  const int MAX_ITERATIONS = 45;
1829 
1830  while ((iteration < MAX_ITERATIONS) && (difference > 0.0000000001)) {
1831  double eccTimesSinphi = Eccentricity() * sin(localPhi);
1832  double newPhi = HALFPI -
1833  2.0 * atan(t * pow((1.0 - eccTimesSinphi) /
1834  (1.0 + eccTimesSinphi), halfEcc));
1835  difference = fabs(newPhi - localPhi);
1836  localPhi = newPhi;
1837  iteration++;
1838  }
1839 
1840  if (iteration >= MAX_ITERATIONS) {
1841  QString msg = "Failed to converge in TProjection::phi2Compute()";
1843  }
1844 
1845  return localPhi;
1846  }
1847 
1862  double TProjection::mCompute(const double sinphi, const double cosphi) const {
1863  double eccTimesSinphi = Eccentricity() * sinphi;
1864  double denominator = sqrt(1.0 - eccTimesSinphi * eccTimesSinphi);
1865  return cosphi / denominator;
1866  }
1867 
1885  double TProjection::tCompute(const double phi, const double sinphi) const {
1886  if ((HALFPI) - fabs(phi) < DBL_EPSILON) return 0.0;
1887 
1888  double eccTimesSinphi = Eccentricity() * sinphi;
1889  double denominator = pow((1.0 - eccTimesSinphi) /
1890  (1.0 + eccTimesSinphi),
1891  0.5 * Eccentricity());
1892  return tan(0.5 * (HALFPI - phi)) / denominator;
1893  }
1894 
1906  double TProjection::e4Compute() const {
1907  double onePlusEcc = 1.0 + Eccentricity();
1908  double oneMinusEcc = 1.0 - Eccentricity();
1909 
1910  return sqrt(pow(onePlusEcc, onePlusEcc) *
1911  pow(oneMinusEcc, oneMinusEcc));
1912  }
1913 
1914 } //end namespace isis
1915 
1916 
double EquatorialRadius() const
This returns the equatorial radius of the target.
virtual ~TProjection()
Destroys the TProjection object.
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
bool inLatitudeRange(double latitude)
Determine whether the given latitude is within the range of the MinimumLatitude and MaximumLatitude r...
double MaximumLongitude() const
This returns the maximum longitude of the area of interest.
const double Null
Value for an Isis Null pixel.
Definition: SpecialPixel.h:110
std::vector< double > m_specialLonCases
Constant Longitudes that intersect a discontinuity.
Definition: TProjection.h:383
double mCompute(const double sinphi, const double cosphi) const
A convience method to compute Snyder&#39;s m equation (14-15) for a given latitude, . ...
WorldMapper * m_mapper
This points to a mapper passed into the SetWorldMapper method.
Definition: Projection.h:308
double tCompute(const double phi, const double sinphi) const
A convience method to compute Snyder&#39;s t equation (15-9) for a given latitude, .
bool xyRangeOblique(double &minX, double &maxX, double &minY, double &maxY)
This method is used to find the XY range for oblique aspect projections (non-polar projections) by "w...
bool SetUniversalGround(const double lat, const double lon)
This method is used to set the latitude/longitude which must be Planetocentric (latitude) and Positiv...
const double PI
The mathematical constant PI.
Definition: Constants.h:56
double Scale() const
This method returns the scale for mapping world coordinates into projection coordinates.
bool IsPositiveEast() const
This indicates if the longitude direction type is positive west (as opposed to postive east)...
Longitude values increase in the westerly direction.
Definition: TProjection.h:241
Base class for Map TProjections.
Definition: TProjection.h:182
QString LatitudeTypeString() const
This method returns the latitude type as a string.
const double HALFPI
The mathematical constant PI/2.
Definition: Constants.h:57
virtual bool operator==(const Projection &proj)
This method determines whether two map projection objects are equal by comparing the equatorial radiu...
virtual bool IsEquatorialCylindrical()
This method returns true if the projection is equatorial cylindrical.
double PixelResolution() const
Returns the pixel resolution value from the PVL mapping group in meters/pixel.
Definition: Projection.cpp:855
Namespace for the standard library.
std::vector< double > m_specialLatCases
Constant Latitudes that intersect a discontinuity.
Definition: TProjection.h:381
Latitudes are measured as the angle from the equatorial plane to the normal to the surface of the pla...
Definition: TProjection.h:223
Longitude force180Domain() const
This returns a longitude that is constricted to -180 to 180 degrees.
Definition: Longitude.cpp:302
double UniversalLongitude()
This returns a universal longitude (positive east in 0 to 360 domain).
bool IsGood() const
This indicates if the last invocation of SetGround, SetCoordinate, SetUniversalGround, or SetWorld was with successful or not.
Definition: Projection.cpp:389
double XCoord() const
This returns the projection X provided SetGround, SetCoordinate, SetUniversalGround, or SetWorld returned with success.
Definition: Projection.cpp:402
double m_minimumX
The data elements m_minimumX, m_minimumY, m_maximumX, and m_maximumY are convience data elements when...
Definition: Projection.h:333
double m_polarRadius
Polar radius of the target.
Definition: TProjection.h:358
bool inLongitudeRange(double longitude)
Determine whether the given longitude is within the range of the MinimumLongitude and MaximumLongitud...
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:226
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
void SetComputedXY(double x, double y)
This protected method is a helper for derived classes.
Definition: Projection.cpp:795
Latitudes are measured as the angle from the equatorial plane to the plane through the center of the ...
Definition: TProjection.h:220
double m_latitude
This contains the currently set latitude value.
Definition: TProjection.h:332
Longitude force360Domain() const
This returns a longitude that is constricted to 0-360 degrees.
Definition: Longitude.cpp:280
void setSearchGround(const double variableBorder, const double constBorder, bool variableIsLat)
This function sets the ground for the given border values.
double degrees() const
Get the angle in units of Degrees.
Definition: Angle.h:249
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.
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
This class is designed to encapsulate the concept of a Longitude.
Definition: Longitude.h:52
double m_minimumLatitude
Contains the minimum latitude for the entire ground range.
Definition: TProjection.h:370
static double ToPositiveWest(const double lon, const int domain)
This method converts a longitude into the positive west direction.
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
double Eccentricity() const
This returns the eccentricity of the target,.
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
Definition: Angle.h:73
double m_equatorialRadius
Polar radius of the target.
Definition: TProjection.h:351
bool HasGroundRange() const
This indicates if the longitude direction type is positive west (as opposed to postive east)...
Definition: Projection.cpp:364
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.
bool Has180Domain() const
This indicates if the longitude domain is -180 to 180 (as opposed to 0 to 360).
void findExtreme(double &minBorder, double &maxBorder, double &minBorderX, double &minBorderY, double &maxBorderX, double &maxBorderY, const double constBorder, bool searchX, bool searchLongitude, bool findMin)
Searches for extreme (min/max/discontinuity) coordinate values across latitudes/longitudes.
bool IsPlanetographic() const
This indicates if the latitude type is planetographic (as opposed to planetocentric).
double Longitude() const
This returns a longitude with correct longitude direction and domain as specified in the label object...
Contains multiple PvlContainers.
Definition: PvlGroup.h:57
void setProjectionType(const ProjectionType ptype)
Sets the projection subclass type.
Definition: Projection.cpp:203
bool m_groundRangeGood
Indicates if the ground range (min/max lat/lons) were read from the labels.
Definition: Projection.h:329
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
double LocalRadius() const
This method returns the local radius in meters at the current latitude position.
static PvlGroup radiiGroup(QString target)
Creates a Pvl Group with keywords TargetName, EquitorialRadius, and PolarRadius.
Definition: Target.cpp:393
virtual 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.
A type of error that cannot be classified as any of the other error types.
Definition: IException.h:134
double YCoord() const
This returns the projection Y provided SetGround, SetCoordinate, SetUniversalGround, or SetWorld returned with success.
Definition: Projection.cpp:415
QString LongitudeDirectionString() const
This method returns the longitude direction as a string.
bool Has360Domain() const
This indicates if the longitude domain is 0 to 360 (as opposed to -180 to 180).
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.
double phi2Compute(const double t) const
A convience method to compute latitude angle phi2 given small t, from Syder&#39;s recursive equation (7-9...
double MinimumLatitude() const
This returns the minimum latitude of the area of interest.
virtual PvlGroup Mapping()
This function returns the keywords that this projection uses.
double UniversalLatitude()
This returns a universal latitude (planetocentric).
bool m_good
Indicates if the contents of m_x, m_y, m_latitude, and m_longitude are valid.
Definition: Projection.h:316
static double To360Domain(const double lon)
This method converts a longitude into the 0 to 360 domain.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
double e4Compute() const
A convience method to compute.
LongitudeDirection m_longitudeDirection
An enumerated type indicating the LongitudeDirection read from the labels.
Definition: TProjection.h:340
bool IsPositiveWest() const
This indicates if the longitude direction type is positive east (as opposed to postive west)...
virtual bool SetCoordinate(const double x, const double y)
This method is used to set the projection x/y.
double PolarRadius() const
This returns the polar radius of the target.
LatitudeType m_latitudeType
An enumerated type indicating the LatitudeType read from the labels.
Definition: TProjection.h:337
double Latitude() const
This returns a latitude with correct latitude type as specified in the label object.
double m_eccentricity
The eccentricity of the target body.
Definition: TProjection.h:368
Isis exception class.
Definition: IException.h:107
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
double ToPlanetocentric(const double lat) const
This method converts a planetographic latitude to a planetocentric latitude.
QString LongitudeDomainString() const
This method returns the longitude domain as a string.
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
virtual double Resolution() const
This virtual method will the resolution of the world system relative to one unit in the projection sy...
Definition: WorldMapper.h:116
void XYRangeCheck(const double latitude, const double longitude)
This convience function is established to assist in the development of the XYRange virtual method...
double m_minimumLongitude
Contains the minimum longitude for the entire ground range.
Definition: TProjection.h:374
bool SetUnboundUniversalGround(const double coord1, const double coord2)
This method is used to set the latitude/longitude.
double m_maximumX
See minimumX description.
Definition: Projection.h:342
void doSearch(double minBorder, double maxBorder, double &extremeVal, const double constBorder, bool searchX, bool searchLongitude, bool findMin)
This method searches for extreme (min/max/discontinuity) coordinate values along the constBorder line...
double qCompute(const double sinPhi) const
A convience method to compute Snyder&#39;s q equation (3-12) for a given latitude, .
static double ToPositiveEast(const double lon, const int domain)
This method converts a longitude into the positive east direction.
Longitude values increase in the easterly direction.
Definition: TProjection.h:239
PvlGroup m_mappingGrp
Mapping group that created this projection.
Definition: Projection.h:345
virtual double TrueScaleLatitude() const
This method returns the latitude of true scale.
These projections are used to map triaxial and irregular-shaped bodies.
Definition: Projection.h:182
virtual 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 MaximumLatitude() const
This returns the maximum latitude of the area of interest.