Official websites use .gov
A .gov website belongs to an official government organization in the United States.

Secure .gov websites use HTTPS
A lock ( ) or https:// means you’ve safely connected to the .gov website. Share sensitive information only on official, secure websites.

Isis 3 Programmer Reference
TProjection.cpp
1
5
6/* SPDX-License-Identifier: CC0-1.0 */
7#include "TProjection.h"
8
9#include <QObject>
10
11#include <vector>
12
13#include "Displacement.h"
14#include "FileName.h"
15#include "IException.h"
16#include "IString.h"
17#include "Longitude.h"
18#include "Pvl.h"
19#include "PvlGroup.h"
20#include "PvlKeyword.h"
21#include "SpecialPixel.h"
22#include "Target.h"
23#include "WorldMapper.h"
24
25using namespace std;
26namespace Isis {
83 try {
84 // Mapping group is read by the parent
85 // Get the radii from the EquatorialRadius and PolarRadius keywords
86 if ((m_mappingGrp.hasKeyword("EquatorialRadius")) &&
87 (m_mappingGrp.hasKeyword("PolarRadius"))) {
88 m_equatorialRadius = m_mappingGrp["EquatorialRadius"];
89 m_polarRadius = m_mappingGrp["PolarRadius"];
90 }
91 // Get the radii
92 try {
93 PvlGroup radii = Target::radiiGroup(label, m_mappingGrp);
94 m_equatorialRadius = radii["EquatorialRadius"];
95 m_polarRadius = radii["PolarRadius"];
96 }
97 catch (IException &e) {
98 QString msg = "Projection failed. No target radii are available "
99 "through keywords [EquatorialRadius and PolarRadius] "
100 "or [TargetName].";
101 throw IException(e, IException::Unknown, msg, _FILEINFO_);
102 }
103
104 // Check the radii for validity
105 if (m_equatorialRadius <= 0.0) {
106 QString msg = "Projection failed. Invalid value for keyword "
107 "[EquatorialRadius]. It must be greater than zero";
108 throw IException(IException::Unknown, msg, _FILEINFO_);
109 }
110 if (m_polarRadius <= 0.0) {
111 QString msg = "Projection failed. Invalid value for keyword "
112 "[PolarRadius]. It must be greater than zero";
113 throw IException(IException::Unknown, msg, _FILEINFO_);
114 }
115
116 // Get the LatitudeType
117 if ((QString) m_mappingGrp["LatitudeType"] == "Planetographic") {
119 }
120 else if ((QString) m_mappingGrp["LatitudeType"] == "Planetocentric") {
122 }
123 else {
124 QString msg = "Projection failed. Invalid value for keyword "
125 "[LatitudeType] must be "
126 "[Planetographic or Planetocentric]";
127 throw IException(IException::Unknown, msg, _FILEINFO_);
128 }
129
130 // Get the LongitudeDirection
131 if ((QString) m_mappingGrp["LongitudeDirection"] == "PositiveWest") {
133 }
134 else if ((QString) m_mappingGrp["LongitudeDirection"] == "PositiveEast") {
136 }
137 else {
138 QString msg = "Projection failed. Invalid value for keyword "
139 "[LongitudeDirection] must be "
140 "[PositiveWest or PositiveEast]";
141 throw IException(IException::Unknown, msg, _FILEINFO_);
142 }
143
144 // Get the LongitudeDomain
145 if ((QString) m_mappingGrp["LongitudeDomain"] == "360") {
146 m_longitudeDomain = 360;
147 }
148 else if ((QString) m_mappingGrp["LongitudeDomain"] == "180") {
149 m_longitudeDomain = 180;
150 }
151 else {
152 QString msg = "Projection failed. Invalid value for keyword "
153 "[LongitudeDomain] must be [180 or 360]";
154 throw IException(IException::Unknown, msg, _FILEINFO_);
155 }
156
157 // Get the ground range if it exists
158 m_groundRangeGood = false;
159 if ((m_mappingGrp.hasKeyword("MinimumLatitude")) &&
160 (m_mappingGrp.hasKeyword("MaximumLatitude")) &&
161 (m_mappingGrp.hasKeyword("MinimumLongitude")) &&
162 (m_mappingGrp.hasKeyword("MaximumLongitude"))) {
163 m_minimumLatitude = m_mappingGrp["MinimumLatitude"];
164 m_maximumLatitude = m_mappingGrp["MaximumLatitude"];
165 m_minimumLongitude = m_mappingGrp["MinimumLongitude"];
166 m_maximumLongitude = m_mappingGrp["MaximumLongitude"];
167
168 if ((m_minimumLatitude < -90.0) || (m_minimumLatitude > 90.0)) {
169 QString msg = "Projection failed. "
170 "[MinimumLatitude] of [" + toString(m_minimumLatitude)
171 + "] is outside the range of [-90:90]";
172 throw IException(IException::Unknown, msg, _FILEINFO_);
173 }
174
175 if ((m_maximumLatitude < -90.0) || (m_maximumLatitude > 90.0)) {
176 QString msg = "Projection failed. "
177 "[MaximumLatitude] of [" + toString(m_maximumLatitude)
178 + "] is outside the range of [-90:90]";
179 throw IException(IException::Unknown, msg, _FILEINFO_);
180 }
181
183 QString msg = "Projection failed. "
184 "[MinimumLatitude,MaximumLatitude] of ["
186 + toString(m_maximumLatitude) + "] are not "
187 + "properly ordered";
188 throw IException(IException::Unknown, msg, _FILEINFO_);
189 }
190
192 QString msg = "Projection failed. "
193 "[MinimumLongitude,MaximumLongitude] of ["
195 + toString(m_maximumLongitude) + "] are not "
196 + "properly ordered";
197 throw IException(IException::Unknown, msg, _FILEINFO_);
198 }
199
200 m_groundRangeGood = true;
201 }
202 else {
203 // if no ground range is given, initialize the min/max lat/lon to 0
204 m_minimumLatitude = 0.0;
205 m_maximumLatitude = 0.0;
206 m_minimumLongitude = 0.0;
207 m_maximumLongitude = 0.0;
208 }
209
210 // Initialize miscellaneous protected data elements
212 QString msg = "Projection failed. Invalid keyword value(s). "
213 "[EquatorialRadius] = " + toString(m_equatorialRadius)
214 + " must be greater than or equal to [PolarRadius] = "
216 throw IException(IException::Unknown, msg, _FILEINFO_);
217 }
218 else {
219 m_eccentricity = 1.0 -
223 }
224
225 // initialize the rest of the x,y,lat,lon member variables
226 m_latitude = Null;
227 m_longitude = Null;
228
229 // If we made it to this point, we have what we need for a triaxial projection
231 }
232 catch (IException &e) {
233 QString msg = "Projection failed. Invalid label group [Mapping]";
234 throw IException(e, IException::Unknown, msg, _FILEINFO_);
235 }
236 }
237
241
253 if (!Projection::operator==(proj)) return false;
254 TProjection *tproj = (TProjection *) &proj;
255 if (EquatorialRadius() != tproj->EquatorialRadius()) return false;
256 if (PolarRadius() != tproj->PolarRadius()) return false;
257 if (IsPlanetocentric() != tproj->IsPlanetocentric()) return false;
258 if (IsPositiveWest() != tproj->IsPositiveWest()) return false;
259 return true;
260 }
261
262
270 return m_equatorialRadius;
271 }
272
280 return m_polarRadius;
281 }
282
297 return m_eccentricity;
298 }
299
318 double TProjection::LocalRadius(double latitude) const {
319 if (latitude == Null) {
320 throw IException(IException::Unknown,
321 "Unable to calculate local radius. The given latitude value ["
322 + toString(latitude) + "] is invalid.",
323 _FILEINFO_);
324 }
325 double a = m_equatorialRadius;
326 double c = m_polarRadius;
327 // to save calculations, if the target is spherical, return the eq. rad
328 if (a - c < DBL_EPSILON) {
329 return a;
330 }
331 else {
332 double lat = latitude * PI / 180.0;
333 return a * c / sqrt(pow(c * cos(lat), 2) + pow(a * sin(lat), 2));
334 }
335 }
336
346 return LocalRadius(m_latitude);
347 }
348
349
361 return 0.0;
362 }
363
374 return false;
375 }
376
387
398
410 double TProjection::ToPlanetocentric(const double lat) const {
412 }
413
426 double TProjection::ToPlanetocentric(const double lat,
427 double eRadius, double pRadius) {
428 if (lat == Null || abs(lat) > 90.0) {
429 throw IException(IException::Unknown,
430 "Unable to convert to Planetocentric. The given latitude value ["
431 + toString(lat) + "] is invalid.",
432 _FILEINFO_);
433 }
434 double mylat = lat;
435 if (abs(mylat) < 90.0) { // So tan doesn't fail
436 mylat *= PI / 180.0;
437 mylat = atan(tan(mylat) * (pRadius / eRadius) *
438 (pRadius / eRadius));
439 mylat *= 180.0 / PI;
440 }
441 return mylat;
442 }
443
455 double TProjection::ToPlanetographic(const double lat) const {
457 }
458
473 double eRadius, double pRadius) {
474 //Account for double rounding error.
475 if (qFuzzyCompare(fabs(lat), 90.0)) {
476 lat = qRound(lat);
477 }
478 if (lat == Null || fabs(lat) > 90.0) {
479 throw IException(IException::Unknown,
480 "Unable to convert to Planetographic. The given latitude value ["
481 + toString(lat) + "] is invalid.",
482 _FILEINFO_);
483 }
484 double mylat = lat;
485 if (fabs(mylat) < 90.0) { // So tan doesn't fail
486 mylat *= PI / 180.0;
487 mylat = atan(tan(mylat) * (eRadius / pRadius) *
488 (eRadius / pRadius));
489 mylat *= 180.0 / PI;
490 }
491 return mylat;
492 }
493
501 if (m_latitudeType == Planetographic) return "Planetographic";
502 return "Planetocentric";
503 }
504
515
526
540 double TProjection::ToPositiveEast(const double lon, const int domain) {
541 if (lon == Null) {
542 throw IException(IException::Unknown,
543 "Unable to convert to PositiveEast. The given longitude value ["
544 + toString(lon) + "] is invalid.",
545 _FILEINFO_);
546 }
547 double mylon = lon;
548
549 mylon *= -1;
550
551 if (domain == 360) {
552 mylon = To360Domain(mylon);
553 }
554 else if (domain == 180) {
555 mylon = To180Domain(mylon);
556 }
557 else {
558 QString msg = "Unable to convert longitude. Domain [" + toString(domain)
559 + "] is not 180 or 360.";
560 throw IException(IException::Unknown, msg, _FILEINFO_);
561 }
562
563 return mylon;
564 }
565
579 double TProjection::ToPositiveWest(const double lon, const int domain) {
580 if (lon == Null) {
581 throw IException(IException::Unknown,
582 "Unable to convert to PositiveWest. The given longitude value ["
583 + toString(lon) + "] is invalid.",
584 _FILEINFO_);
585 }
586 double mylon = lon;
587
588 mylon *= -1;
589
590 if (domain == 360) {
591 mylon = To360Domain(mylon);
592 }
593 else if (domain == 180) {
594 mylon = To180Domain(mylon);
595 }
596 else {
597 QString msg = "Unable to convert longitude. Domain [" + toString(domain)
598 + "] is not 180 or 360.";
599 throw IException(IException::Unknown, msg, _FILEINFO_);
600 }
601
602 return mylon;
603 }
604
613 if (m_longitudeDirection == PositiveEast) return "PositiveEast";
614 return "PositiveWest";
615 }
616
625 return m_longitudeDomain == 180;
626 }
627
636 return m_longitudeDomain == 360;
637 }
638
649 double TProjection::To180Domain(const double lon) {
650 if (lon == Null) {
651 throw IException(IException::Unknown,
652 "Unable to convert to 180 degree domain. The given longitude value ["
653 + toString(lon) + "] is invalid.",
654 _FILEINFO_);
655 }
657 }
658
667 double TProjection::To360Domain(const double lon) {
668 if (lon == Null) {
669 throw IException(IException::Unknown,
670 "Unable to convert to 360 degree domain. The given longitude value ["
671 + toString(lon) + "] is invalid.",
672 _FILEINFO_);
673 }
674 double result = lon;
675
676 if ( (lon < 0.0 || lon > 360.0) &&
677 !qFuzzyCompare(lon, 0.0) && !qFuzzyCompare(lon, 360.0)) {
679 }
680
681 return result;
682 }
683
691 if (m_longitudeDomain == 360) return "360";
692 return "180";
693 }
694
703 return m_minimumLatitude;
704 }
705
714 return m_maximumLatitude;
715 }
716
725 return m_minimumLongitude;
726 }
727
736 return m_maximumLongitude;
737 }
738
752 bool TProjection::SetGround(const double lat, const double lon) {
753 if (lat == Null || lon == Null) {
754 m_good = false;
755 return m_good;
756 }
757 else {
758 m_latitude = lat;
759 m_longitude = lon;
760 m_good = true;
761 SetComputedXY(lon, lat);
762 }
763 return m_good;
764 }
765
781 bool TProjection::SetCoordinate(const double x, const double y) {
782 if (x == Null || y == Null) {
783 m_good = false;
784 }
785 else {
786 m_good = true;
787 SetXY(x, y);
788 m_latitude = XCoord();
790 }
791 return m_good;
792 }
793
794
803 double TProjection::Latitude() const {
804 return m_latitude;
805 }
806
815 double TProjection::Longitude() const {
816 return m_longitude;
817 }
818
819
831 bool TProjection::SetUniversalGround(const double lat, const double lon) {
832 if (lat == Null || lon == Null) {
833 m_good = false;
834 return m_good;
835 }
836 // Deal with the longitude first
837 m_longitude = lon;
839 if (m_longitudeDomain == 180) {
841 }
842 else {
843 // Do this because longitudeDirection could cause (-360,0)
845 }
846
847 // Deal with the latitude
850 }
851 else {
852 m_latitude = lat;
853 }
854
855 // Now the lat/lon are in user defined coordinates so set them
857 }
858
859
871 bool TProjection::SetUnboundUniversalGround(const double lat, const double lon) {
872 if (lat == Null || lon == Null) {
873 m_good = false;
874 return m_good;
875 }
876 // Deal with the longitude first
877 m_longitude = lon;
879
880 // Deal with the latitude
883 }
884 else {
885 m_latitude = lat;
886 }
887
888 // Now the lat/lon are in user defined coordinates so set them
890 }
891
892
901 double lat = m_latitude;
903 return lat;
904 }
905
915 double lon = m_longitude;
916 if (m_longitudeDirection == PositiveWest) lon = -lon;
917 lon = To360Domain(lon);
918 return lon;
919 }
920
921
932 double TProjection::Scale() const {
933 if (m_mapper != NULL) {
934 double lat = TrueScaleLatitude() * PI / 180.0;
935 double a = m_polarRadius * cos(lat);
936 double b = m_equatorialRadius * sin(lat);
937 double localRadius = m_equatorialRadius * m_polarRadius /
938 sqrt(a * a + b * b);
939
940 return localRadius / m_mapper->Resolution();
941 }
942 else {
943 return 1.0;
944 }
945 }
946
947
985 bool TProjection::XYRange(double &minX, double &maxX,
986 double &minY, double &maxY) {
987 if (minX == Null || maxX == Null || minY == Null || maxY == Null) {
988 return false;
989 }
990 if (m_groundRangeGood) {
991 minX = m_minimumLongitude;
992 maxX = m_maximumLongitude;
993 minY = m_minimumLatitude;
994 maxY = m_maximumLatitude;
995 return true;
996 }
997 return false;
998 }
999
1054 void TProjection::XYRangeCheck(const double latitude, const double longitude) {
1055
1056 if (latitude == Null || longitude == Null) {
1057 m_good = false;
1058 return;
1059 }
1060
1061 SetGround(latitude, longitude);
1062 if (!IsGood()) return;
1063
1064 if (XCoord() < m_minimumX) m_minimumX = XCoord();
1065 if (XCoord() > m_maximumX) m_maximumX = XCoord();
1066 if (YCoord() < m_minimumY) m_minimumY = YCoord();
1067 if (YCoord() > m_maximumY) m_maximumY = YCoord();
1068 return;
1069 }
1070
1071
1090 double maxLon,
1091 double longitude) {
1092 // get the min/max range closest to 0.0 lon
1093 double adjustedLon = To360Domain(longitude);
1094 double adjustedMinLon = To360Domain(minLon);
1095 double adjustedMaxLon = To360Domain(maxLon);
1096
1097 if (adjustedMinLon > adjustedMaxLon) {
1098 if (adjustedLon > adjustedMinLon) {
1099 adjustedLon -= 360;
1100 }
1101 adjustedMinLon -= 360;
1102 }
1103
1104 // if this range covers all longitudes, then the given longitude is clearly in range
1105 if (qFuzzyCompare(maxLon - minLon, 360.0)) {
1106 return true;
1107 }
1108 else if (adjustedMinLon <= adjustedLon && adjustedLon <= adjustedMaxLon) {
1109 return true;
1110 }
1111 else {
1112 return false;
1113 }
1114 }
1115
1116
1132 bool TProjection::inLongitudeRange(double longitude) {
1133 return inLongitudeRange(MinimumLongitude(), MaximumLongitude(), longitude);
1134 }
1135
1136
1152 bool TProjection::inLatitudeRange(double latitude) {
1153 if (MaximumLatitude() - MinimumLatitude() == 180) {
1154 return true;
1155 }
1156 else if (MinimumLatitude() <= latitude && latitude <= MaximumLatitude()) {
1157 return true;
1158 }
1159 else {
1160 return false;
1161 }
1162 }
1163
1164
1187 bool TProjection::xyRangeOblique(double &minX, double &maxX,
1188 double &minY, double &maxY) {
1189 if (minX == Null || maxX == Null || minY == Null || maxY == Null) {
1190 return false;
1191 }
1192 //For oblique, we'll have to walk all 4 sides to find out min/max x/y values
1193 if (!HasGroundRange()) return false; // Don't have min/max lat/lon,
1194 //can't continue
1195
1196 m_specialLatCases.clear();
1197 m_specialLonCases.clear();
1198
1199 // First, search longitude for min X/Y
1200 double minFoundX1, minFoundX2;
1201 double minFoundY1, minFoundY2;
1202
1203 // Search for minX between minlat and maxlat along minlon
1205 minFoundX1, MinimumLongitude(), true, true, true);
1206 // Search for minX between minlat and maxlat along maxlon
1208 minFoundX2, MaximumLongitude(), true, true, true);
1209 // Search for minY between minlat and maxlat along minlon
1211 minFoundY1, MinimumLongitude(), false, true, true);
1212 // Search for minY between minlat and maxlat along maxlon
1214 minFoundY2, MaximumLongitude(), false, true, true);
1215
1216 // Second, search latitude for min X/Y
1217 double minFoundX3, minFoundX4;
1218 double minFoundY3, minFoundY4;
1219
1220 // Search for minX between minlon and maxlon along minlat
1222 minFoundX3, MinimumLatitude(), true, false, true);
1223 // Search for minX between minlon and maxlon along maxlat
1225 minFoundX4, MaximumLatitude(), true, false, true);
1226 // Search for minY between minlon and maxlon along minlat
1228 minFoundY3, MinimumLatitude(), false, false, true);
1229 // Search for minY between minlon and maxlon along maxlat
1231 minFoundY4, MaximumLatitude(), false, false, true);
1232
1233 // We've searched all possible minimums, go ahead and store the lowest
1234 double minFoundX5 = min(minFoundX1, minFoundX2);
1235 double minFoundX6 = min(minFoundX3, minFoundX4);
1236 m_minimumX = min(minFoundX5, minFoundX6);
1237
1238 double minFoundY5 = min(minFoundY1, minFoundY2);
1239 double minFoundY6 = min(minFoundY3, minFoundY4);
1240 m_minimumY = min(minFoundY5, minFoundY6);
1241
1242 // Search longitude for max X/Y
1243 double maxFoundX1, maxFoundX2;
1244 double maxFoundY1, maxFoundY2;
1245
1246 // Search for maxX between minlat and maxlat along minlon
1248 maxFoundX1, MinimumLongitude(), true, true, false);
1249 // Search for maxX between minlat and maxlat along maxlon
1251 maxFoundX2, MaximumLongitude(), true, true, false);
1252 // Search for maxY between minlat and maxlat along minlon
1254 maxFoundY1, MinimumLongitude(), false, true, false);
1255 // Search for maxY between minlat and maxlat along maxlon
1257 maxFoundY2, MaximumLongitude(), false, true, false);
1258
1259 // Search latitude for max X/Y
1260 double maxFoundX3, maxFoundX4;
1261 double maxFoundY3, maxFoundY4;
1262
1263 // Search for maxX between minlon and maxlon along minlat
1265 maxFoundX3, MinimumLatitude(), true, false, false);
1266 // Search for maxX between minlon and maxlon along maxlat
1268 maxFoundX4, MaximumLatitude(), true, false, false);
1269 // Search for maxY between minlon and maxlon along minlat
1271 maxFoundY3, MinimumLatitude(), false, false, false);
1272 // Search for maxY between minlon and maxlon along maxlat
1274 maxFoundY4, MaximumLatitude(), false, false, false);
1275
1276 // We've searched all possible maximums, go ahead and store the highest
1277 double maxFoundX5 = max(maxFoundX1, maxFoundX2);
1278 double maxFoundX6 = max(maxFoundX3, maxFoundX4);
1279 m_maximumX = max(maxFoundX5, maxFoundX6);
1280
1281 double maxFoundY5 = max(maxFoundY1, maxFoundY2);
1282 double maxFoundY6 = max(maxFoundY3, maxFoundY4);
1283 m_maximumY = max(maxFoundY5, maxFoundY6);
1284
1285 // Look along discontinuities for more extremes
1286 vector<double> specialLatCases = m_specialLatCases;
1287 for (unsigned int specialLatCase = 0;
1288 specialLatCase < specialLatCases.size();
1289 specialLatCase ++) {
1290 double minX, maxX, minY, maxY;
1291
1292 // Search for minX between minlon and maxlon along latitude discontinuities
1294 minX, specialLatCases[specialLatCase], true, false, true);
1295 // Search for minY between minlon and maxlon along latitude discontinuities
1297 minY, specialLatCases[specialLatCase], false, false, true);
1298 // Search for maxX between minlon and maxlon along latitude discontinuities
1300 maxX, specialLatCases[specialLatCase], true, false, false);
1301 // Search for maxX between minlon and maxlon along latitude discontinuities
1303 maxY, specialLatCases[specialLatCase], false, false, false);
1304
1305 m_minimumX = min(minX, m_minimumX);
1306 m_maximumX = max(maxX, m_maximumX);
1307 m_minimumY = min(minY, m_minimumY);
1308 m_maximumY = max(maxY, m_maximumY);
1309 }
1310
1311 vector<double> specialLonCases = m_specialLonCases;
1312 for (unsigned int specialLonCase = 0;
1313 specialLonCase < specialLonCases.size();
1314 specialLonCase ++) {
1315 double minX, maxX, minY, maxY;
1316
1317 // Search for minX between minlat and maxlat along longitude discontinuities
1319 minX, specialLonCases[specialLonCase], true, true, true);
1320 // Search for minY between minlat and maxlat along longitude discontinuities
1322 minY, specialLonCases[specialLonCase], false, true, true);
1323 // Search for maxX between minlat and maxlat along longitude discontinuities
1325 maxX, specialLonCases[specialLonCase], true, true, false);
1326 // Search for maxY between minlat and maxlat along longitude discontinuities
1328 maxY, specialLonCases[specialLonCase], false, true, false);
1329
1330 m_minimumX = min(minX, m_minimumX);
1331 m_maximumX = max(maxX, m_maximumX);
1332 m_minimumY = min(minY, m_minimumY);
1333 m_maximumY = max(maxY, m_maximumY);
1334 }
1335
1336 m_specialLatCases.clear();
1337 m_specialLonCases.clear();
1338
1339 // Make sure everything is ordered
1340 if (m_minimumX >= m_maximumX) return false;
1341 if (m_minimumY >= m_maximumY) return false;
1342
1343 // Return X/Y min/maxs
1344 minX = m_minimumX;
1345 maxX = m_maximumX;
1346 minY = m_minimumY;
1347 maxY = m_maximumY;
1348
1349 return true;
1350 }
1351
1389 void TProjection::doSearch(double minBorder, double maxBorder,
1390 double &extremeVal, const double constBorder,
1391 bool searchX, bool searchLongitude, bool findMin) {
1392 if (minBorder == Null || maxBorder == Null || constBorder == Null) {
1393 return;
1394 }
1395 const double TOLERANCE = PixelResolution()/2;
1396 const int NUM_ATTEMPTS = (unsigned int)DBL_DIG; // It's unsafe to go past
1397 // this precision
1398
1399 double minBorderX, minBorderY, maxBorderX, maxBorderY;
1400 int attempts = 0;
1401
1402 do {
1403 findExtreme(minBorder, maxBorder, minBorderX, minBorderY, maxBorderX,
1404 maxBorderY, constBorder, searchX, searchLongitude, findMin);
1405 if (minBorderX == Null && maxBorderX == Null
1406 && minBorderY == Null && maxBorderY == Null ) {
1407 attempts = NUM_ATTEMPTS;
1408 continue;
1409 }
1410 attempts ++;
1411 }
1412 while ((fabs(minBorderX - maxBorderX) > TOLERANCE
1413 || fabs(minBorderY - maxBorderY) > TOLERANCE)
1414 && (attempts < NUM_ATTEMPTS));
1415 // check both x and y distance in case symmetry of map
1416 // For example, if minBorderX = maxBorderX but minBorderY = -maxBorderY,
1417 // these points may not be close enough.
1418
1419 if (attempts >= NUM_ATTEMPTS) {
1420 // We zoomed in on a discontinuity because our range never shrank, this
1421 // will need to be rechecked later.
1422 // *min and max border should be nearly identical, so it doesn't matter
1423 // which is used here
1424 if (searchLongitude) {
1425 m_specialLatCases.push_back(minBorder);
1426 }
1427 else {
1428 m_specialLonCases.push_back(minBorder);
1429 }
1430 }
1431
1432 // These values will always be accurate, even over a discontinuity
1433 if (findMin) {
1434 if (searchX) extremeVal = min(minBorderX, maxBorderX);
1435 else extremeVal = min(minBorderY, maxBorderY);
1436 }
1437 else {
1438 if (searchX) extremeVal = max(minBorderX, maxBorderX);
1439 else extremeVal = max(minBorderY, maxBorderY);
1440 }
1441 return;
1442 }
1443
1503 void TProjection::findExtreme(double &minBorder, double &maxBorder,
1504 double &minBorderX, double &minBorderY,
1505 double &maxBorderX, double &maxBorderY,
1506 const double constBorder, bool searchX,
1507 bool searchLongitude, bool findMin) {
1508 if (minBorder == Null || maxBorder == Null || constBorder == Null) {
1509 minBorderX = Null;
1510 minBorderY = minBorderX;
1511 minBorderY = minBorderX;
1512 return;
1513 }
1514 if (!searchLongitude && (fabs(fabs(constBorder) - 90.0) < DBL_EPSILON)) {
1515 // it is impossible to search "along" a pole
1516 setSearchGround(minBorder, constBorder, searchLongitude);
1517 minBorderX = XCoord();
1518 minBorderY = YCoord();
1519 maxBorderY = minBorderY;
1520 return;
1521 }
1522 // Always do 10 steps
1523 const double STEP_SIZE = (maxBorder - minBorder) / 10.0;
1524 const double LOOP_END = maxBorder + (STEP_SIZE / 2.0); // This ensures we do
1525 // all of the steps
1526 // properly
1527 double currBorderVal = minBorder;
1528 setSearchGround(minBorder, constBorder, searchLongitude);
1529
1530 // this makes sure that the initial currBorderVal is valid before entering
1531 // the loop below
1532 if (!m_good){
1533 // minBorder = currBorderVal+STEP_SIZE < LOOP_END until setGround is good?
1534 // then, if still not good return?
1535 while (!m_good && currBorderVal <= LOOP_END) {
1536 currBorderVal+=STEP_SIZE;
1537 if (searchLongitude && (currBorderVal - 90.0 > DBL_EPSILON)) {
1538 currBorderVal = 90.0;
1539 }
1540 setSearchGround(currBorderVal, constBorder, searchLongitude);
1541 }
1542 if (!m_good) {
1543 minBorderX = Null;
1544 minBorderY = Null;
1545 return;
1546 }
1547 }
1548
1549 // save the values of three consecutive steps from the minBorder towards
1550 // the maxBorder along the constBorder. initialize these three border
1551 // values (the non-constant lat or lon)
1552 double border1 = currBorderVal;
1553 double border2 = currBorderVal;
1554 double border3 = currBorderVal;
1555
1556 // save the coordinate (x or y) values that correspond to the first
1557 // two borders that are being saved.
1558 // initialize these two coordinate values (x or y)
1559 double value1 = (searchX) ? XCoord() : YCoord();
1560 double value2 = value1;
1561
1562 // initialize the extreme coordinate value
1563 // -- this is the largest coordinate value found so far
1564 double extremeVal2 = value2;
1565
1566 // initialize the extreme border values
1567 // -- these are the borders on either side of the extreme coordinate value
1568 double extremeBorder1 = minBorder;
1569 double extremeBorder3 = minBorder;
1570
1571 while (currBorderVal <= LOOP_END) {
1572
1573 // this conditional was added to prevent trying to SetGround with an
1574 // invalid latitude greater than 90 degrees. There is no need check for
1575 // latitude less than -90 since we start at the minBorder (already
1576 // assumed to be valid) and step forward toward (and possibly past)
1577 // maxBorder
1578 if (searchLongitude && (currBorderVal - 90.0 > DBL_EPSILON)) {
1579 currBorderVal = 90.0;
1580 }
1581
1582 // update the current border value along constBorder
1583 currBorderVal += STEP_SIZE;
1584 setSearchGround(currBorderVal, constBorder, searchLongitude);
1585 if (!m_good){
1586 continue;
1587 }
1588
1589 // update the border and coordinate values
1590 border3 = border2;
1591 border2 = border1;
1592 border1 = currBorderVal;
1593 value2 = value1;
1594 value1 = (searchX) ? XCoord() : YCoord();
1595
1596 if ((findMin && value2 < extremeVal2)
1597 || (!findMin && value2 > extremeVal2)) {
1598 // Compare the coordinate value associated with the center border with
1599 // the current extreme. If the updated coordinate value is more extreme
1600 // (smaller or larger, depending on findMin), then we update the
1601 // extremeVal and it's borders.
1602 extremeVal2 = value2;
1603
1604 extremeBorder3 = border3;
1605 extremeBorder1 = border1;
1606 }
1607 }
1608
1609 // update min/max border values to the values on either side of the most
1610 // extreme coordinate found in this call to this method
1611
1612 minBorder = extremeBorder3; // Border 3 is lagging and thus smaller
1613
1614 // since the loop steps past the original maxBorder, we want to retain
1615 // the original maxBorder value so we don't go outside of the original
1616 // min/max range given
1617 if (extremeBorder1 <= maxBorder ) {
1618 maxBorder = extremeBorder1; // Border 1 is leading and thus larger
1619 }
1620
1621 // update minBorder coordinate values
1622 setSearchGround(minBorder, constBorder, searchLongitude);
1623 // if (!m_good){
1624 // this should not happen since minBorder has already been verified in
1625 // the while loop above
1626 // }
1627
1628 minBorderX = XCoord();
1629 minBorderY = YCoord();
1630
1631 // update maxBorder coordinate values
1632 setSearchGround(maxBorder, constBorder, searchLongitude);
1633 // if (!m_good){
1634 // this should not happen since maxBorder has already been verified in
1635 // the while loop above
1636 // }
1637
1638 maxBorderX = XCoord();
1639 maxBorderY = YCoord();
1640 return;
1641 }
1642
1665 void TProjection::setSearchGround(const double variableBorder,
1666 const double constBorder,
1667 bool variableIsLat) {
1668 if (variableBorder == Null || constBorder == Null) {
1669 return;
1670 }
1671 double lat, lon;
1672 if (variableIsLat) {
1673 lat = variableBorder;
1674 lon = constBorder;
1675 }
1676 else {
1677 lat = constBorder;
1678 lon = variableBorder;
1679 }
1680 SetGround(lat, lon);
1681 return;
1682 }
1683
1684
1691 PvlGroup mapping("Mapping");
1692
1693 QStringList keyNames;
1694 keyNames << "TargetName" << "ProjectionName" << "EquatorialRadius" << "PolarRadius"
1695 << "LatitudeType" << "LongitudeDirection" << "LongitudeDomain"
1696 << "PixelResolution" << "Scale" << "UpperLeftCornerX" << "UpperLeftCornerY"
1697 << "MinimumLatitude" << "MaximumLatitude" << "MinimumLongitude" << "MaximumLongitude"
1698 << "Rotation";
1699
1700 foreach (QString keyName, keyNames) {
1701 if (m_mappingGrp.hasKeyword(keyName)) {
1702 mapping += m_mappingGrp[keyName];
1703 }
1704 }
1705
1706 return mapping;
1707 }
1708
1709
1716 PvlGroup mapping("Mapping");
1717
1718 if (HasGroundRange()) {
1719 mapping += m_mappingGrp["MinimumLatitude"];
1720 mapping += m_mappingGrp["MaximumLatitude"];
1721 }
1722
1723 return mapping;
1724 }
1725
1732 PvlGroup mapping("Mapping");
1733
1734 if (HasGroundRange()) {
1735 mapping += m_mappingGrp["MinimumLongitude"];
1736 mapping += m_mappingGrp["MaximumLongitude"];
1737 }
1738
1739 return mapping;
1740 }
1741
1762 double TProjection::qCompute(const double sinPhi) const {
1763 if (m_eccentricity < DBL_EPSILON) {
1764 QString msg = "Snyder's q variable should only be computed for "
1765 "ellipsoidal projections.";
1766 throw IException(IException::Unknown, msg, _FILEINFO_);
1767 }
1768 double eSinPhi = m_eccentricity * sinPhi;
1769 return (1 - m_eccentricity * m_eccentricity)
1770 * (sinPhi / (1 - eSinPhi * eSinPhi)
1771 - 1 / (2 * m_eccentricity) * log( (1 - eSinPhi) / (1 + eSinPhi) ));
1772 // Note: We know that q is well defined since
1773 // 0 < e < 1 and -1 <= sin(phi) <= 1
1774 // implies that -1 < e*sin(phi) < 1
1775 // Thus, there are no 0 denominators and the log domain is
1776 // satisfied, (1-e*sin(phi))/(1+e*sin(phi)) > 0
1777 }
1778
1795 double TProjection::phi2Compute(const double t) const {
1796 double localPhi = HALFPI - 2.0 * atan(t);
1797 double halfEcc = 0.5 * Eccentricity();
1798 double difference = DBL_MAX;
1799 int iteration = 0;
1800 // a failure in this loop means an exception will be thrown, which is
1801 // an expensive operation. So letting let the loop iterate quite a bit
1802 // is not a big deal. Also, the user will be unable to project at all
1803 // if this fails so more reason to let this loop iterate: better to
1804 // function slow than not at all.
1805 const int MAX_ITERATIONS = 45;
1806
1807 while ((iteration < MAX_ITERATIONS) && (difference > 0.0000000001)) {
1808 double eccTimesSinphi = Eccentricity() * sin(localPhi);
1809 double newPhi = HALFPI -
1810 2.0 * atan(t * pow((1.0 - eccTimesSinphi) /
1811 (1.0 + eccTimesSinphi), halfEcc));
1812 difference = fabs(newPhi - localPhi);
1813 localPhi = newPhi;
1814 iteration++;
1815 }
1816
1817 if (iteration >= MAX_ITERATIONS) {
1818 QString msg = "Failed to converge in TProjection::phi2Compute()";
1819 throw IException(IException::Unknown, msg, _FILEINFO_);
1820 }
1821
1822 return localPhi;
1823 }
1824
1839 double TProjection::mCompute(const double sinphi, const double cosphi) const {
1840 double eccTimesSinphi = Eccentricity() * sinphi;
1841 double denominator = sqrt(1.0 - eccTimesSinphi * eccTimesSinphi);
1842 return cosphi / denominator;
1843 }
1844
1862 double TProjection::tCompute(const double phi, const double sinphi) const {
1863 if ((HALFPI) - fabs(phi) < DBL_EPSILON) return 0.0;
1864
1865 double eccTimesSinphi = Eccentricity() * sinphi;
1866 double denominator = pow((1.0 - eccTimesSinphi) /
1867 (1.0 + eccTimesSinphi),
1868 0.5 * Eccentricity());
1869 return tan(0.5 * (HALFPI - phi)) / denominator;
1870 }
1871
1883 double TProjection::e4Compute() const {
1884 double onePlusEcc = 1.0 + Eccentricity();
1885 double oneMinusEcc = 1.0 - Eccentricity();
1886
1887 return sqrt(pow(onePlusEcc, onePlusEcc) *
1888 pow(oneMinusEcc, oneMinusEcc));
1889 }
1890
1891} //end namespace isis
1892
1893
double degrees() const
Get the angle in units of Degrees.
Definition Angle.h:232
@ Degrees
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
Definition Angle.h:56
This class is designed to encapsulate the concept of a Longitude.
Definition Longitude.h:40
Longitude force180Domain() const
This returns a longitude that is constricted to -180 to 180 degrees.
Longitude force360Domain() const
This returns a longitude that is constricted to 0-360 degrees.
@ Triaxial
These projections are used to map triaxial and irregular-shaped bodies.
Definition Projection.h:165
WorldMapper * m_mapper
This points to a mapper passed into the SetWorldMapper method.
Definition Projection.h:291
double XCoord() const
This returns the projection X provided SetGround, SetCoordinate, SetUniversalGround,...
double m_maximumX
See minimumX description.
Definition Projection.h:325
virtual bool HasGroundRange() const
This indicates if the longitude direction type is positive west (as opposed to postive east).
bool m_groundRangeGood
Indicates if the ground range (min/max lat/lons) were read from the labels.
Definition Projection.h:312
double PixelResolution() const
Returns the pixel resolution value from the PVL mapping group in meters/pixel.
double YCoord() const
This returns the projection Y provided SetGround, SetCoordinate, SetUniversalGround,...
bool m_good
Indicates if the contents of m_x, m_y, m_latitude, and m_longitude are valid.
Definition Projection.h:299
double m_minimumX
The data elements m_minimumX, m_minimumY, m_maximumX, and m_maximumY are convience data elements when...
Definition Projection.h:316
Projection(Pvl &label)
Constructs an empty Projection object.
PvlGroup m_mappingGrp
Mapping group that created this projection.
Definition Projection.h:328
double m_minimumY
See minimumX description.
Definition Projection.h:326
void SetXY(double x, double y)
This protected method is a helper for derived classes.
bool IsGood() const
This indicates if the last invocation of SetGround, SetCoordinate, SetUniversalGround,...
void setProjectionType(const ProjectionType ptype)
Sets the projection subclass type.
double m_maximumY
See minimumX description.
Definition Projection.h:327
void SetComputedXY(double x, double y)
This protected method is a helper for derived classes.
double m_longitude
This contains the currently set longitude value.
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,...
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 SetUnboundUniversalGround(const double coord1, const double coord2)
This method is used to set the latitude/longitude.
bool IsPlanetocentric() const
This indicates if the latitude type is planetocentric (as opposed to planetographic).
double m_minimumLatitude
Contains the minimum latitude for the entire ground range.
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 m_polarRadius
Polar radius of the target.
std::vector< double > m_specialLatCases
Constant Latitudes that intersect a discontinuity.
double m_maximumLongitude
Contains the maximum longitude for the entire ground range.
static double To180Domain(const double lon)
This method converts a longitude into the -180 to 180 domain.
double m_equatorialRadius
Polar radius of the target.
LongitudeDirection m_longitudeDirection
An enumerated type indicating the LongitudeDirection read from the labels.
virtual PvlGroup MappingLongitudes()
This function returns the longitude keywords that this projection uses.
virtual double MaximumLatitude() const
This returns the maximum latitude of the area of interest.
virtual ~TProjection()
Destroys the TProjection object.
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.
double LocalRadius() const
This method returns the local radius in meters at the current latitude position.
virtual bool SetCoordinate(const double x, const double y)
This method is used to set the projection x/y.
int m_longitudeDomain
This integer is either 180 or 360 and is read from the labels.
double mCompute(const double sinphi, const double cosphi) const
A convience method to compute Snyder's m equation (14-15) for a given latitude, .
double ToPlanetocentric(const double lat) const
This method converts a planetographic latitude to a planetocentric latitude.
bool Has180Domain() const
This indicates if the longitude domain is -180 to 180 (as opposed to 0 to 360).
virtual bool SetUniversalGround(const double lat, const double lon)
This method is used to set the latitude/longitude which must be Planetocentric (latitude) and Positiv...
std::vector< double > m_specialLonCases
Constant Longitudes that intersect a discontinuity.
void setSearchGround(const double variableBorder, const double constBorder, bool variableIsLat)
This function sets the ground for the given border values.
virtual double MinimumLongitude() const
This returns the minimum longitude of the area of interest.
virtual double UniversalLongitude()
This returns a universal longitude (positive east in 0 to 360 domain).
double PolarRadius() const
This returns the polar radius of the target.
bool inLongitudeRange(double longitude)
Determine whether the given longitude is within the range of the MinimumLongitude and MaximumLongitud...
void XYRangeCheck(const double latitude, const double longitude)
This convience function is established to assist in the development of the XYRange virtual method.
double Eccentricity() const
This returns the eccentricity of the target,.
bool IsPositiveWest() const
This indicates if the longitude direction type is positive east (as opposed to postive west).
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...
virtual PvlGroup MappingLatitudes()
This function returns the latitude keywords that this projection uses.
double m_eccentricity
The eccentricity of the target body.
double qCompute(const double sinPhi) const
A convience method to compute Snyder's q equation (3-12) for a given latitude, .
virtual bool operator==(const Projection &proj)
This method determines whether two map projection objects are equal by comparing the equatorial radiu...
double phi2Compute(const double t) const
A convience method to compute latitude angle phi2 given small t, from Syder's recursive equation (7-9...
double m_minimumLongitude
Contains the minimum longitude for the entire ground range.
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 e4Compute() const
A convience method to compute.
double tCompute(const double phi, const double sinphi) const
A convience method to compute Snyder's t equation (15-9) for a given latitude, .
@ PositiveWest
Longitude values increase in the westerly direction.
@ PositiveEast
Longitude values increase in the easterly direction.
double m_maximumLatitude
Contains the maximum latitude for the entire ground range.
virtual double Latitude() const
This returns a latitude with correct latitude type as specified in the label object.
static double ToPositiveWest(const double lon, const int domain)
This method converts a longitude into the positive west direction.
static double To360Domain(const double lon)
This method converts a longitude into the 0 to 360 domain.
TProjection(Pvl &label)
Constructs an empty TProjection object.
QString LongitudeDomainString() const
This method returns the longitude domain as a string.
bool IsPlanetographic() const
This indicates if the latitude type is planetographic (as opposed to planetocentric).
virtual bool IsEquatorialCylindrical()
This method returns true if the projection is equatorial cylindrical.
QString LatitudeTypeString() const
This method returns the latitude type as a string.
bool Has360Domain() const
This indicates if the longitude domain is 0 to 360 (as opposed to -180 to 180).
@ Planetocentric
Latitudes are measured as the angle from the equatorial plane to the plane through the center of the ...
@ Planetographic
Latitudes are measured as the angle from the equatorial plane to the normal to the surface of the pla...
virtual double MinimumLatitude() const
This returns the minimum latitude of the area of interest.
LatitudeType m_latitudeType
An enumerated type indicating the LatitudeType read from the labels.
virtual double MaximumLongitude() const
This returns the maximum longitude of the area of interest.
virtual PvlGroup Mapping()
This function returns the keywords that this projection uses.
static double ToPositiveEast(const double lon, const int domain)
This method converts a longitude into the positive east direction.
virtual double UniversalLatitude()
This returns a universal latitude (planetocentric).
double EquatorialRadius() const
This returns the equatorial radius of the target.
bool IsPositiveEast() const
This indicates if the longitude direction type is positive west (as opposed to postive east).
double Scale() const
This method returns the scale for mapping world coordinates into projection coordinates.
virtual double Longitude() const
This returns a longitude with correct longitude direction and domain as specified in the label object...
double ToPlanetographic(const double lat) const
This method converts a planetocentric latitude to a planetographic latitude.
double m_latitude
This contains the currently set latitude value.
virtual double TrueScaleLatitude() const
This method returns the latitude of true scale.
static PvlGroup radiiGroup(QString target)
Creates a Pvl Group with keywords TargetName, EquitorialRadius, and PolarRadius.
Definition Target.cpp:428
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
QString toString(const LinearAlgebra::Vector &vector, int precision)
A global function to format LinearAlgebra::Vector as a QString with the given precision.
Namespace for the standard library.