Isis 3.0 Programmer Reference
Back | Home
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 
890  double lat = m_latitude;
892  return lat;
893  }
894 
904  double lon = m_longitude;
905  if (m_longitudeDirection == PositiveWest) lon = -lon;
906  lon = To360Domain(lon);
907  return lon;
908  }
909 
910 
921  double TProjection::Scale() const {
922  if (m_mapper != NULL) {
923  double lat = TrueScaleLatitude() * PI / 180.0;
924  double a = m_polarRadius * cos(lat);
925  double b = m_equatorialRadius * sin(lat);
926  double localRadius = m_equatorialRadius * m_polarRadius /
927  sqrt(a * a + b * b);
928 
929  return localRadius / m_mapper->Resolution();
930  }
931  else {
932  return 1.0;
933  }
934  }
935 
936 
974  bool TProjection::XYRange(double &minX, double &maxX,
975  double &minY, double &maxY) {
976  if (minX == Null || maxX == Null || minY == Null || maxY == Null) {
977  return false;
978  }
979  if (m_groundRangeGood) {
980  minX = m_minimumLongitude;
981  maxX = m_maximumLongitude;
982  minY = m_minimumLatitude;
983  maxY = m_maximumLatitude;
984  return true;
985  }
986  return false;
987  }
988 
1043  void TProjection::XYRangeCheck(const double latitude, const double longitude) {
1044 
1045  if (latitude == Null || longitude == Null) {
1046  m_good = false;
1047  return;
1048  }
1049 
1050  SetGround(latitude, longitude);
1051  if (!IsGood()) return;
1052 
1053  if (XCoord() < m_minimumX) m_minimumX = XCoord();
1054  if (XCoord() > m_maximumX) m_maximumX = XCoord();
1055  if (YCoord() < m_minimumY) m_minimumY = YCoord();
1056  if (YCoord() > m_maximumY) m_maximumY = YCoord();
1057  return;
1058  }
1059 
1060 
1078  bool TProjection::inLongitudeRange(double minLon,
1079  double maxLon,
1080  double longitude) {
1081  // get the min/max range closest to 0.0 lon
1082  double adjustedLon = To360Domain(longitude);
1083  double adjustedMinLon = To360Domain(minLon);
1084  double adjustedMaxLon = To360Domain(maxLon);
1085 
1086  if (adjustedMinLon > adjustedMaxLon) {
1087  if (adjustedLon > adjustedMinLon) {
1088  adjustedLon -= 360;
1089  }
1090  adjustedMinLon -= 360;
1091  }
1092 
1093  // if this range covers all longitudes, then the given longitude is clearly in range
1094  if (qFuzzyCompare(maxLon - minLon, 360.0)) {
1095  return true;
1096  }
1097  else if (adjustedMinLon <= adjustedLon && adjustedLon <= adjustedMaxLon) {
1098  return true;
1099  }
1100  else {
1101  return false;
1102  }
1103  }
1104 
1105 
1121  bool TProjection::inLongitudeRange(double longitude) {
1122  return inLongitudeRange(MinimumLongitude(), MaximumLongitude(), longitude);
1123  }
1124 
1125 
1141  bool TProjection::inLatitudeRange(double latitude) {
1142  if (MaximumLatitude() - MinimumLatitude() == 180) {
1143  return true;
1144  }
1145  else if (MinimumLatitude() <= latitude && latitude <= MaximumLatitude()) {
1146  return true;
1147  }
1148  else {
1149  return false;
1150  }
1151  }
1152 
1153 
1176  bool TProjection::xyRangeOblique(double &minX, double &maxX,
1177  double &minY, double &maxY) {
1178  if (minX == Null || maxX == Null || minY == Null || maxY == Null) {
1179  return false;
1180  }
1181  //For oblique, we'll have to walk all 4 sides to find out min/max x/y values
1182  if (!HasGroundRange()) return false; // Don't have min/max lat/lon,
1183  //can't continue
1184 
1185  m_specialLatCases.clear();
1186  m_specialLonCases.clear();
1187 
1188  // First, search longitude for min X/Y
1189  double minFoundX1, minFoundX2;
1190  double minFoundY1, minFoundY2;
1191 
1192  // Search for minX between minlat and maxlat along minlon
1194  minFoundX1, MinimumLongitude(), true, true, true);
1195  // Search for minX between minlat and maxlat along maxlon
1197  minFoundX2, MaximumLongitude(), true, true, true);
1198  // Search for minY between minlat and maxlat along minlon
1200  minFoundY1, MinimumLongitude(), false, true, true);
1201  // Search for minY between minlat and maxlat along maxlon
1203  minFoundY2, MaximumLongitude(), false, true, true);
1204 
1205  // Second, search latitude for min X/Y
1206  double minFoundX3, minFoundX4;
1207  double minFoundY3, minFoundY4;
1208 
1209  // Search for minX between minlon and maxlon along minlat
1211  minFoundX3, MinimumLatitude(), true, false, true);
1212  // Search for minX between minlon and maxlon along maxlat
1214  minFoundX4, MaximumLatitude(), true, false, true);
1215  // Search for minY between minlon and maxlon along minlat
1217  minFoundY3, MinimumLatitude(), false, false, true);
1218  // Search for minY between minlon and maxlon along maxlat
1220  minFoundY4, MaximumLatitude(), false, false, true);
1221 
1222  // We've searched all possible minimums, go ahead and store the lowest
1223  double minFoundX5 = min(minFoundX1, minFoundX2);
1224  double minFoundX6 = min(minFoundX3, minFoundX4);
1225  m_minimumX = min(minFoundX5, minFoundX6);
1226 
1227  double minFoundY5 = min(minFoundY1, minFoundY2);
1228  double minFoundY6 = min(minFoundY3, minFoundY4);
1229  m_minimumY = min(minFoundY5, minFoundY6);
1230 
1231  // Search longitude for max X/Y
1232  double maxFoundX1, maxFoundX2;
1233  double maxFoundY1, maxFoundY2;
1234 
1235  // Search for maxX between minlat and maxlat along minlon
1237  maxFoundX1, MinimumLongitude(), true, true, false);
1238  // Search for maxX between minlat and maxlat along maxlon
1240  maxFoundX2, MaximumLongitude(), true, true, false);
1241  // Search for maxY between minlat and maxlat along minlon
1243  maxFoundY1, MinimumLongitude(), false, true, false);
1244  // Search for maxY between minlat and maxlat along maxlon
1246  maxFoundY2, MaximumLongitude(), false, true, false);
1247 
1248  // Search latitude for max X/Y
1249  double maxFoundX3, maxFoundX4;
1250  double maxFoundY3, maxFoundY4;
1251 
1252  // Search for maxX between minlon and maxlon along minlat
1254  maxFoundX3, MinimumLatitude(), true, false, false);
1255  // Search for maxX between minlon and maxlon along maxlat
1257  maxFoundX4, MaximumLatitude(), true, false, false);
1258  // Search for maxY between minlon and maxlon along minlat
1260  maxFoundY3, MinimumLatitude(), false, false, false);
1261  // Search for maxY between minlon and maxlon along maxlat
1263  maxFoundY4, MaximumLatitude(), false, false, false);
1264 
1265  // We've searched all possible maximums, go ahead and store the highest
1266  double maxFoundX5 = max(maxFoundX1, maxFoundX2);
1267  double maxFoundX6 = max(maxFoundX3, maxFoundX4);
1268  m_maximumX = max(maxFoundX5, maxFoundX6);
1269 
1270  double maxFoundY5 = max(maxFoundY1, maxFoundY2);
1271  double maxFoundY6 = max(maxFoundY3, maxFoundY4);
1272  m_maximumY = max(maxFoundY5, maxFoundY6);
1273 
1274  // Look along discontinuities for more extremes
1275  vector<double> specialLatCases = m_specialLatCases;
1276  for (unsigned int specialLatCase = 0;
1277  specialLatCase < specialLatCases.size();
1278  specialLatCase ++) {
1279  double minX, maxX, minY, maxY;
1280 
1281  // Search for minX between minlon and maxlon along latitude discontinuities
1283  minX, specialLatCases[specialLatCase], true, false, true);
1284  // Search for minY between minlon and maxlon along latitude discontinuities
1286  minY, specialLatCases[specialLatCase], false, false, true);
1287  // Search for maxX between minlon and maxlon along latitude discontinuities
1289  maxX, specialLatCases[specialLatCase], true, false, false);
1290  // Search for maxX between minlon and maxlon along latitude discontinuities
1292  maxY, specialLatCases[specialLatCase], false, false, false);
1293 
1294  m_minimumX = min(minX, m_minimumX);
1295  m_maximumX = max(maxX, m_maximumX);
1296  m_minimumY = min(minY, m_minimumY);
1297  m_maximumY = max(maxY, m_maximumY);
1298  }
1299 
1300  vector<double> specialLonCases = m_specialLonCases;
1301  for (unsigned int specialLonCase = 0;
1302  specialLonCase < specialLonCases.size();
1303  specialLonCase ++) {
1304  double minX, maxX, minY, maxY;
1305 
1306  // Search for minX between minlat and maxlat along longitude discontinuities
1308  minX, specialLonCases[specialLonCase], true, true, true);
1309  // Search for minY between minlat and maxlat along longitude discontinuities
1311  minY, specialLonCases[specialLonCase], false, true, true);
1312  // Search for maxX between minlat and maxlat along longitude discontinuities
1314  maxX, specialLonCases[specialLonCase], true, true, false);
1315  // Search for maxY between minlat and maxlat along longitude discontinuities
1317  maxY, specialLonCases[specialLonCase], false, true, false);
1318 
1319  m_minimumX = min(minX, m_minimumX);
1320  m_maximumX = max(maxX, m_maximumX);
1321  m_minimumY = min(minY, m_minimumY);
1322  m_maximumY = max(maxY, m_maximumY);
1323  }
1324 
1325  m_specialLatCases.clear();
1326  m_specialLonCases.clear();
1327 
1328  // Make sure everything is ordered
1329  if (m_minimumX >= m_maximumX) return false;
1330  if (m_minimumY >= m_maximumY) return false;
1331 
1332  // Return X/Y min/maxs
1333  minX = m_minimumX;
1334  maxX = m_maximumX;
1335  minY = m_minimumY;
1336  maxY = m_maximumY;
1337 
1338  return true;
1339  }
1340 
1378  void TProjection::doSearch(double minBorder, double maxBorder,
1379  double &extremeVal, const double constBorder,
1380  bool searchX, bool searchLongitude, bool findMin) {
1381  if (minBorder == Null || maxBorder == Null || constBorder == Null) {
1382  return;
1383  }
1384  const double TOLERANCE = PixelResolution()/2;
1385  const int NUM_ATTEMPTS = (unsigned int)DBL_DIG; // It's unsafe to go past
1386  // this precision
1387 
1388  double minBorderX, minBorderY, maxBorderX, maxBorderY;
1389  int attempts = 0;
1390 
1391  do {
1392  findExtreme(minBorder, maxBorder, minBorderX, minBorderY, maxBorderX,
1393  maxBorderY, constBorder, searchX, searchLongitude, findMin);
1394  if (minBorderX == Null && maxBorderX == Null
1395  && minBorderY == Null && maxBorderY == Null ) {
1396  attempts = NUM_ATTEMPTS;
1397  continue;
1398  }
1399  attempts ++;
1400  }
1401  while ((fabs(minBorderX - maxBorderX) > TOLERANCE
1402  || fabs(minBorderY - maxBorderY) > TOLERANCE)
1403  && (attempts < NUM_ATTEMPTS));
1404  // check both x and y distance in case symmetry of map
1405  // For example, if minBorderX = maxBorderX but minBorderY = -maxBorderY,
1406  // these points may not be close enough.
1407 
1408  if (attempts >= NUM_ATTEMPTS) {
1409  // We zoomed in on a discontinuity because our range never shrank, this
1410  // will need to be rechecked later.
1411  // *min and max border should be nearly identical, so it doesn't matter
1412  // which is used here
1413  if (searchLongitude) {
1414  m_specialLatCases.push_back(minBorder);
1415  }
1416  else {
1417  m_specialLonCases.push_back(minBorder);
1418  }
1419  }
1420 
1421  // These values will always be accurate, even over a discontinuity
1422  if (findMin) {
1423  if (searchX) extremeVal = min(minBorderX, maxBorderX);
1424  else extremeVal = min(minBorderY, maxBorderY);
1425  }
1426  else {
1427  if (searchX) extremeVal = max(minBorderX, maxBorderX);
1428  else extremeVal = max(minBorderY, maxBorderY);
1429  }
1430  return;
1431  }
1432 
1492  void TProjection::findExtreme(double &minBorder, double &maxBorder,
1493  double &minBorderX, double &minBorderY,
1494  double &maxBorderX, double &maxBorderY,
1495  const double constBorder, bool searchX,
1496  bool searchLongitude, bool findMin) {
1497  if (minBorder == Null || maxBorder == Null || constBorder == Null) {
1498  minBorderX = Null;
1499  minBorderY = minBorderX;
1500  minBorderY = minBorderX;
1501  return;
1502  }
1503  if (!searchLongitude && (fabs(fabs(constBorder) - 90.0) < DBL_EPSILON)) {
1504  // it is impossible to search "along" a pole
1505  setSearchGround(minBorder, constBorder, searchLongitude);
1506  minBorderX = XCoord();
1507  minBorderY = YCoord();
1508  maxBorderY = minBorderY;
1509  return;
1510  }
1511  // Always do 10 steps
1512  const double STEP_SIZE = (maxBorder - minBorder) / 10.0;
1513  const double LOOP_END = maxBorder + (STEP_SIZE / 2.0); // This ensures we do
1514  // all of the steps
1515  // properly
1516  double currBorderVal = minBorder;
1517  setSearchGround(minBorder, constBorder, searchLongitude);
1518 
1519  // this makes sure that the initial currBorderVal is valid before entering
1520  // the loop below
1521  if (!m_good){
1522  // minBorder = currBorderVal+STEP_SIZE < LOOP_END until setGround is good?
1523  // then, if still not good return?
1524  while (!m_good && currBorderVal <= LOOP_END) {
1525  currBorderVal+=STEP_SIZE;
1526  if (searchLongitude && (currBorderVal - 90.0 > DBL_EPSILON)) {
1527  currBorderVal = 90.0;
1528  }
1529  setSearchGround(currBorderVal, constBorder, searchLongitude);
1530  }
1531  if (!m_good) {
1532  minBorderX = Null;
1533  minBorderY = minBorderX;
1534  minBorderX = minBorderX;
1535  minBorderY = minBorderX;
1536  return;
1537  }
1538  }
1539 
1540  // save the values of three consecutive steps from the minBorder towards
1541  // the maxBorder along the constBorder. initialize these three border
1542  // values (the non-constant lat or lon)
1543  double border1 = currBorderVal;
1544  double border2 = currBorderVal;
1545  double border3 = currBorderVal;
1546 
1547  // save the coordinate (x or y) values that correspond to the first
1548  // two borders that are being saved.
1549  // initialize these two coordinate values (x or y)
1550  double value1 = (searchX) ? XCoord() : YCoord();
1551  double value2 = value1;
1552 
1553  // initialize the extreme coordinate value
1554  // -- this is the largest coordinate value found so far
1555  double extremeVal2 = value2;
1556 
1557  // initialize the extreme border values
1558  // -- these are the borders on either side of the extreme coordinate value
1559  double extremeBorder1 = minBorder;
1560  double extremeBorder3 = minBorder;
1561 
1562  while (currBorderVal <= LOOP_END) {
1563 
1564  // this conditional was added to prevent trying to SetGround with an
1565  // invalid latitude greater than 90 degrees. There is no need check for
1566  // latitude less than -90 since we start at the minBorder (already
1567  // assumed to be valid) and step forward toward (and possibly past)
1568  // maxBorder
1569  if (searchLongitude && (currBorderVal - 90.0 > DBL_EPSILON)) {
1570  currBorderVal = 90.0;
1571  }
1572 
1573  // update the current border value along constBorder
1574  currBorderVal += STEP_SIZE;
1575  setSearchGround(currBorderVal, constBorder, searchLongitude);
1576  if (!m_good){
1577  continue;
1578  }
1579 
1580  // update the border and coordinate values
1581  border3 = border2;
1582  border2 = border1;
1583  border1 = currBorderVal;
1584  value2 = value1;
1585  value1 = (searchX) ? XCoord() : YCoord();
1586 
1587  if ((findMin && value2 < extremeVal2)
1588  || (!findMin && value2 > extremeVal2)) {
1589  // Compare the coordinate value associated with the center border with
1590  // the current extreme. If the updated coordinate value is more extreme
1591  // (smaller or larger, depending on findMin), then we update the
1592  // extremeVal and it's borders.
1593  extremeVal2 = value2;
1594 
1595  extremeBorder3 = border3;
1596  extremeBorder1 = border1;
1597  }
1598  }
1599 
1600  // update min/max border values to the values on either side of the most
1601  // extreme coordinate found in this call to this method
1602 
1603  minBorder = extremeBorder3; // Border 3 is lagging and thus smaller
1604 
1605  // since the loop steps past the original maxBorder, we want to retain
1606  // the original maxBorder value so we don't go outside of the original
1607  // min/max range given
1608  if (extremeBorder1 <= maxBorder ) {
1609  maxBorder = extremeBorder1; // Border 1 is leading and thus larger
1610  }
1611 
1612  // update minBorder coordinate values
1613  setSearchGround(minBorder, constBorder, searchLongitude);
1614  // if (!m_good){
1615  // this should not happen since minBorder has already been verified in
1616  // the while loop above
1617  // }
1618 
1619  minBorderX = XCoord();
1620  minBorderY = YCoord();
1621 
1622  // update maxBorder coordinate values
1623  setSearchGround(maxBorder, constBorder, searchLongitude);
1624  // if (!m_good){
1625  // this should not happen since maxBorder has already been verified in
1626  // the while loop above
1627  // }
1628 
1629  maxBorderX = XCoord();
1630  maxBorderY = YCoord();
1631  return;
1632  }
1633 
1656  void TProjection::setSearchGround(const double variableBorder,
1657  const double constBorder,
1658  bool variableIsLat) {
1659  if (variableBorder == Null || constBorder == Null) {
1660  return;
1661  }
1662  double lat, lon;
1663  if (variableIsLat) {
1664  lat = variableBorder;
1665  lon = constBorder;
1666  }
1667  else {
1668  lat = constBorder;
1669  lon = variableBorder;
1670  }
1671  SetGround(lat, lon);
1672  return;
1673  }
1674 
1675 
1682  PvlGroup mapping("Mapping");
1683 
1684  QStringList keyNames;
1685  keyNames << "TargetName" << "ProjectionName" << "EquatorialRadius" << "PolarRadius"
1686  << "LatitudeType" << "LongitudeDirection" << "LongitudeDomain"
1687  << "PixelResolution" << "Scale" << "UpperLeftCornerX" << "UpperLeftCornerY"
1688  << "MinimumLatitude" << "MaximumLatitude" << "MinimumLongitude" << "MaximumLongitude"
1689  << "Rotation";
1690 
1691  foreach (QString keyName, keyNames) {
1692  if (m_mappingGrp.hasKeyword(keyName)) {
1693  mapping += m_mappingGrp[keyName];
1694  }
1695  }
1696 
1697  return mapping;
1698  }
1699 
1700 
1707  PvlGroup mapping("Mapping");
1708 
1709  if (HasGroundRange()) {
1710  mapping += m_mappingGrp["MinimumLatitude"];
1711  mapping += m_mappingGrp["MaximumLatitude"];
1712  }
1713 
1714  return mapping;
1715  }
1716 
1723  PvlGroup mapping("Mapping");
1724 
1725  if (HasGroundRange()) {
1726  mapping += m_mappingGrp["MinimumLongitude"];
1727  mapping += m_mappingGrp["MaximumLongitude"];
1728  }
1729 
1730  return mapping;
1731  }
1732 
1753  double TProjection::qCompute(const double sinPhi) const {
1754  if (m_eccentricity < DBL_EPSILON) {
1755  QString msg = "Snyder's q variable should only be computed for "
1756  "ellipsoidal projections.";
1758  }
1759  double eSinPhi = m_eccentricity * sinPhi;
1760  return (1 - m_eccentricity * m_eccentricity)
1761  * (sinPhi / (1 - eSinPhi * eSinPhi)
1762  - 1 / (2 * m_eccentricity) * log( (1 - eSinPhi) / (1 + eSinPhi) ));
1763  // Note: We know that q is well defined since
1764  // 0 < e < 1 and -1 <= sin(phi) <= 1
1765  // implies that -1 < e*sin(phi) < 1
1766  // Thus, there are no 0 denominators and the log domain is
1767  // satisfied, (1-e*sin(phi))/(1+e*sin(phi)) > 0
1768  }
1769 
1786  double TProjection::phi2Compute(const double t) const {
1787  double localPhi = HALFPI - 2.0 * atan(t);
1788  double halfEcc = 0.5 * Eccentricity();
1789  double difference = DBL_MAX;
1790  int iteration = 0;
1791  // a failure in this loop means an exception will be thrown, which is
1792  // an expensive operation. So letting let the loop iterate quite a bit
1793  // is not a big deal. Also, the user will be unable to project at all
1794  // if this fails so more reason to let this loop iterate: better to
1795  // function slow than not at all.
1796  const int MAX_ITERATIONS = 45;
1797 
1798  while ((iteration < MAX_ITERATIONS) && (difference > 0.0000000001)) {
1799  double eccTimesSinphi = Eccentricity() * sin(localPhi);
1800  double newPhi = HALFPI -
1801  2.0 * atan(t * pow((1.0 - eccTimesSinphi) /
1802  (1.0 + eccTimesSinphi), halfEcc));
1803  difference = fabs(newPhi - localPhi);
1804  localPhi = newPhi;
1805  iteration++;
1806  }
1807 
1808  if (iteration >= MAX_ITERATIONS) {
1809  QString msg = "Failed to converge in TProjection::phi2Compute()";
1811  }
1812 
1813  return localPhi;
1814  }
1815 
1830  double TProjection::mCompute(const double sinphi, const double cosphi) const {
1831  double eccTimesSinphi = Eccentricity() * sinphi;
1832  double denominator = sqrt(1.0 - eccTimesSinphi * eccTimesSinphi);
1833  return cosphi / denominator;
1834  }
1835 
1853  double TProjection::tCompute(const double phi, const double sinphi) const {
1854  if ((HALFPI) - fabs(phi) < DBL_EPSILON) return 0.0;
1855 
1856  double eccTimesSinphi = Eccentricity() * sinphi;
1857  double denominator = pow((1.0 - eccTimesSinphi) /
1858  (1.0 + eccTimesSinphi),
1859  0.5 * Eccentricity());
1860  return tan(0.5 * (HALFPI - phi)) / denominator;
1861  }
1862 
1874  double TProjection::e4Compute() const {
1875  double onePlusEcc = 1.0 + Eccentricity();
1876  double oneMinusEcc = 1.0 - Eccentricity();
1877 
1878  return sqrt(pow(onePlusEcc, onePlusEcc) *
1879  pow(oneMinusEcc, oneMinusEcc));
1880  }
1881 
1882 } //end namespace isis
1883 
1884 
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
virtual ~TProjection()
Destroys the TProjection object.
QString LongitudeDirectionString() const
This method returns the longitude direction as a string.
bool inLatitudeRange(double latitude)
Determine whether the given latitude is within the range of the MinimumLatitude and MaximumLatitude r...
const double Null
Value for an Isis Null pixel.
Definition: SpecialPixel.h:109
std::vector< double > m_specialLonCases
Constant Longitudes that intersect a discontinuity.
Definition: TProjection.h:378
WorldMapper * m_mapper
This points to a mapper passed into the SetWorldMapper method.
Definition: Projection.h:305
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 &quot;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...
double degrees() const
Get the angle in units of Degrees.
Definition: Angle.h:245
Longitude values increase in the westerly direction.
Definition: TProjection.h:237
bool IsPositiveEast() const
This indicates if the longitude direction type is positive west (as opposed to postive east)...
Base class for Map TProjections.
Definition: TProjection.h:178
bool HasGroundRange() const
This indicates if the longitude direction type is positive west (as opposed to postive east)...
Definition: Projection.cpp:364
bool Has360Domain() const
This indicates if the longitude domain is 0 to 360 (as opposed to -180 to 180).
double ToPlanetographic(const double lat) const
This method converts a planetocentric latitude to a planetographic latitude.
const double PI(3.14159265358979323846)
The mathematical constant PI.
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 XCoord() const
This returns the projection X provided SetGround, SetCoordinate, SetUniversalGround, or SetWorld returned with success.
Definition: Projection.cpp:402
std::vector< double > m_specialLatCases
Constant Latitudes that intersect a discontinuity.
Definition: TProjection.h:376
Latitudes are measured as the angle from the equatorial plane to the normal to the surface of the pla...
Definition: TProjection.h:219
double Eccentricity() const
This returns the eccentricity of the target,.
double UniversalLongitude()
This returns a universal longitude (positive east in 0 to 360 domain).
double m_minimumX
The data elements m_minimumX, m_minimumY, m_maximumX, and m_maximumY are convience data elements when...
Definition: Projection.h:330
double m_polarRadius
Polar radius of the target.
Definition: TProjection.h:353
bool IsPositiveWest() const
This indicates if the longitude direction type is positive east (as opposed to postive west)...
const double HALFPI(1.57079632679489661923)
The mathematical constant PI/2.
bool inLongitudeRange(double longitude)
Determine whether the given longitude is within the range of the MinimumLongitude and MaximumLongitud...
Longitude force180Domain() const
This returns a longitude that is constricted to -180 to 180 degrees.
Definition: Longitude.cpp:302
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:226
double e4Compute() const
A convience method to compute.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
double LocalRadius() const
This method returns the local radius in meters at the current latitude position.
void SetComputedXY(double x, double y)
This protected method is a helper for derived classes.
Definition: Projection.cpp:766
Latitudes are measured as the angle from the equatorial plane to the plane through the center of the ...
Definition: TProjection.h:216
double m_latitude
This contains the currently set latitude value.
Definition: TProjection.h:327
void setSearchGround(const double variableBorder, const double constBorder, bool variableIsLat)
This function sets the ground for the given border values.
virtual PvlGroup MappingLatitudes()
This function returns the latitude keywords that this projection uses.
double MinimumLatitude() const
This returns the minimum latitude of the area of interest.
double m_maximumY
See minimumX description.
Definition: Projection.h:341
Longitude force360Domain() const
This returns a longitude that is constricted to 0-360 degrees.
Definition: Longitude.cpp:280
double m_longitude
This contains the currently set longitude value.
Definition: TProjection.h:329
double m_maximumLongitude
Contains the maximum longitude for the entire ground range.
Definition: TProjection.h:371
double EquatorialRadius() const
This returns the equatorial radius of the target.
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:365
static double ToPositiveWest(const double lon, const int domain)
This method converts a longitude into the positive west direction.
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 m_maximumLatitude
Contains the maximum latitude for the entire ground range.
Definition: TProjection.h:367
Base class for Map Projections.
Definition: Projection.h:169
double m_minimumY
See minimumX description.
Definition: Projection.h:340
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
Definition: Angle.h:69
double m_equatorialRadius
Polar radius of the target.
Definition: TProjection.h:346
int m_longitudeDomain
This integer is either 180 or 360 and is read from the labels.
Definition: TProjection.h:342
static double To180Domain(const double lon)
This method converts a longitude into the -180 to 180 domain.
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.
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:326
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:38
double MinimumLongitude() const
This returns the minimum longitude of the area of interest.
static PvlGroup radiiGroup(QString target)
Creates a Pvl Group with keywords TargetName, EquitorialRadius, and PolarRadius.
Definition: Target.cpp:372
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:126
double MaximumLatitude() const
This returns the maximum latitude of the area of interest.
Container for cube-like labels.
Definition: Pvl.h:135
bool Has180Domain() const
This indicates if the longitude domain is -180 to 180 (as opposed to 0 to 360).
bool IsPlanetocentric() const
This indicates if the latitude type is planetocentric (as opposed to planetographic).
double ToPlanetocentric(const double lat) const
This method converts a planetographic latitude to a planetocentric latitude.
virtual PvlGroup Mapping()
This function returns the keywords that this projection uses.
double UniversalLatitude()
This returns a universal latitude (planetocentric).
bool IsGood() const
This indicates if the last invocation of SetGround, SetCoordinate, SetUniversalGround, or SetWorld was with successful or not.
Definition: Projection.cpp:389
bool m_good
Indicates if the contents of m_x, m_y, m_latitude, and m_longitude are valid.
Definition: Projection.h:313
double YCoord() const
This returns the projection Y provided SetGround, SetCoordinate, SetUniversalGround, or SetWorld returned with success.
Definition: Projection.cpp:415
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.
LongitudeDirection m_longitudeDirection
An enumerated type indicating the LongitudeDirection read from the labels.
Definition: TProjection.h:335
double Longitude() const
This returns a longitude with correct longitude direction and domain as specified in the label object...
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, . ...
virtual bool SetCoordinate(const double x, const double y)
This method is used to set the projection x/y.
virtual double TrueScaleLatitude() const
This method returns the latitude of true scale.
LatitudeType m_latitudeType
An enumerated type indicating the LatitudeType read from the labels.
Definition: TProjection.h:332
double PolarRadius() const
This returns the polar radius of the target.
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:363
Isis exception class.
Definition: IException.h:99
double qCompute(const double sinPhi) const
A convience method to compute Snyder&#39;s q equation (3-12) for a given latitude, .
double MaximumLongitude() const
This returns the maximum longitude of the area of interest.
bool IsPlanetographic() const
This indicates if the latitude type is planetographic (as opposed to planetocentric).
QString LatitudeTypeString() const
This method returns the latitude type 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:790
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, .
void XYRangeCheck(const double latitude, const double longitude)
This convience function is established to assist in the development of the XYRange virtual method...
QString LongitudeDomainString() const
This method returns the longitude domain as a string.
double m_minimumLongitude
Contains the minimum longitude for the entire ground range.
Definition: TProjection.h:369
double m_maximumX
See minimumX description.
Definition: Projection.h:339
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...
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
static double ToPositiveEast(const double lon, const int domain)
This method converts a longitude into the positive east direction.
double PixelResolution() const
Returns the pixel resolution value from the PVL mapping group in meters/pixel.
Definition: Projection.cpp:826
Longitude values increase in the easterly direction.
Definition: TProjection.h:235
PvlGroup m_mappingGrp
Mapping group that created this projection.
Definition: Projection.h:342
These projections are used to map triaxial and irregular-shaped bodies.
Definition: Projection.h:180
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 Scale() const
This method returns the scale for mapping world coordinates into projection coordinates.

U.S. Department of the Interior | U.S. Geological Survey
ISIS | Privacy & Disclaimers | Astrogeology Research Program
To contact us, please post comments and questions on the ISIS Support Center
File Modified: 07/12/2023 23:30:16