7#include "TProjection.h"
13#include "Displacement.h"
15#include "IException.h"
20#include "PvlKeyword.h"
21#include "SpecialPixel.h"
23#include "WorldMapper.h"
97 catch (IException &e) {
98 QString msg =
"Projection failed. No target radii are available "
99 "through keywords [EquatorialRadius and PolarRadius] "
101 throw IException(e, IException::Unknown, msg, _FILEINFO_);
106 QString msg =
"Projection failed. Invalid value for keyword "
107 "[EquatorialRadius]. It must be greater than zero";
108 throw IException(IException::Unknown, msg, _FILEINFO_);
111 QString msg =
"Projection failed. Invalid value for keyword "
112 "[PolarRadius]. It must be greater than zero";
113 throw IException(IException::Unknown, msg, _FILEINFO_);
117 if ((QString)
m_mappingGrp[
"LatitudeType"] ==
"Planetographic") {
120 else if ((QString)
m_mappingGrp[
"LatitudeType"] ==
"Planetocentric") {
124 QString msg =
"Projection failed. Invalid value for keyword "
125 "[LatitudeType] must be "
126 "[Planetographic or Planetocentric]";
127 throw IException(IException::Unknown, msg, _FILEINFO_);
131 if ((QString)
m_mappingGrp[
"LongitudeDirection"] ==
"PositiveWest") {
134 else if ((QString)
m_mappingGrp[
"LongitudeDirection"] ==
"PositiveEast") {
138 QString msg =
"Projection failed. Invalid value for keyword "
139 "[LongitudeDirection] must be "
140 "[PositiveWest or PositiveEast]";
141 throw IException(IException::Unknown, msg, _FILEINFO_);
145 if ((QString)
m_mappingGrp[
"LongitudeDomain"] ==
"360") {
148 else if ((QString)
m_mappingGrp[
"LongitudeDomain"] ==
"180") {
152 QString msg =
"Projection failed. Invalid value for keyword "
153 "[LongitudeDomain] must be [180 or 360]";
154 throw IException(IException::Unknown, msg, _FILEINFO_);
169 QString msg =
"Projection failed. "
171 +
"] is outside the range of [-90:90]";
172 throw IException(IException::Unknown, msg, _FILEINFO_);
176 QString msg =
"Projection failed. "
178 +
"] is outside the range of [-90:90]";
179 throw IException(IException::Unknown, msg, _FILEINFO_);
183 QString msg =
"Projection failed. "
184 "[MinimumLatitude,MaximumLatitude] of ["
187 +
"properly ordered";
188 throw IException(IException::Unknown, msg, _FILEINFO_);
192 QString msg =
"Projection failed. "
193 "[MinimumLongitude,MaximumLongitude] of ["
196 +
"properly ordered";
197 throw IException(IException::Unknown, msg, _FILEINFO_);
212 QString msg =
"Projection failed. Invalid keyword value(s). "
214 +
" must be greater than or equal to [PolarRadius] = "
216 throw IException(IException::Unknown, msg, _FILEINFO_);
232 catch (IException &e) {
233 QString msg =
"Projection failed. Invalid label group [Mapping]";
234 throw IException(e, IException::Unknown, msg, _FILEINFO_);
253 if (!Projection::operator==(proj))
return false;
319 if (latitude == Null) {
320 throw IException(IException::Unknown,
321 "Unable to calculate local radius. The given latitude value ["
322 +
toString(latitude) +
"] is invalid.",
328 if (a - c < DBL_EPSILON) {
332 double lat = latitude * PI / 180.0;
333 return a * c / sqrt(pow(c * cos(lat), 2) + pow(a * sin(lat), 2));
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 ["
435 if (abs(mylat) < 90.0) {
437 mylat = atan(tan(mylat) * (pRadius / eRadius) *
438 (pRadius / eRadius));
473 double eRadius,
double pRadius) {
475 if (qFuzzyCompare(fabs(lat), 90.0)) {
478 if (lat == Null || fabs(lat) > 90.0) {
479 throw IException(IException::Unknown,
480 "Unable to convert to Planetographic. The given latitude value ["
485 if (fabs(mylat) < 90.0) {
487 mylat = atan(tan(mylat) * (eRadius / pRadius) *
488 (eRadius / pRadius));
502 return "Planetocentric";
542 throw IException(IException::Unknown,
543 "Unable to convert to PositiveEast. The given longitude value ["
554 else if (domain == 180) {
558 QString msg =
"Unable to convert longitude. Domain [" +
toString(domain)
559 +
"] is not 180 or 360.";
560 throw IException(IException::Unknown, msg, _FILEINFO_);
581 throw IException(IException::Unknown,
582 "Unable to convert to PositiveWest. The given longitude value ["
593 else if (domain == 180) {
597 QString msg =
"Unable to convert longitude. Domain [" +
toString(domain)
598 +
"] is not 180 or 360.";
599 throw IException(IException::Unknown, msg, _FILEINFO_);
614 return "PositiveWest";
651 throw IException(IException::Unknown,
652 "Unable to convert to 180 degree domain. The given longitude value ["
669 throw IException(IException::Unknown,
670 "Unable to convert to 360 degree domain. The given longitude value ["
676 if ( (lon < 0.0 || lon > 360.0) &&
677 !qFuzzyCompare(lon, 0.0) && !qFuzzyCompare(lon, 360.0)) {
753 if (lat == Null || lon == Null) {
782 if (x == Null || y == Null) {
832 if (lat == Null || lon == Null) {
872 if (lat == Null || lon == Null) {
940 return localRadius /
m_mapper->Resolution();
986 double &minY,
double &maxY) {
987 if (minX == Null || maxX == Null || minY == Null || maxY == Null) {
1056 if (latitude == Null || longitude == Null) {
1097 if (adjustedMinLon > adjustedMaxLon) {
1098 if (adjustedLon > adjustedMinLon) {
1101 adjustedMinLon -= 360;
1105 if (qFuzzyCompare(maxLon - minLon, 360.0)) {
1108 else if (adjustedMinLon <= adjustedLon && adjustedLon <= adjustedMaxLon) {
1188 double &minY,
double &maxY) {
1189 if (minX == Null || maxX == Null || minY == Null || maxY == Null) {
1200 double minFoundX1, minFoundX2;
1201 double minFoundY1, minFoundY2;
1217 double minFoundX3, minFoundX4;
1218 double minFoundY3, minFoundY4;
1234 double minFoundX5 = min(minFoundX1, minFoundX2);
1235 double minFoundX6 = min(minFoundX3, minFoundX4);
1238 double minFoundY5 = min(minFoundY1, minFoundY2);
1239 double minFoundY6 = min(minFoundY3, minFoundY4);
1243 double maxFoundX1, maxFoundX2;
1244 double maxFoundY1, maxFoundY2;
1260 double maxFoundX3, maxFoundX4;
1261 double maxFoundY3, maxFoundY4;
1277 double maxFoundX5 = max(maxFoundX1, maxFoundX2);
1278 double maxFoundX6 = max(maxFoundX3, maxFoundX4);
1281 double maxFoundY5 = max(maxFoundY1, maxFoundY2);
1282 double maxFoundY6 = max(maxFoundY3, maxFoundY4);
1287 for (
unsigned int specialLatCase = 0;
1288 specialLatCase < specialLatCases.size();
1289 specialLatCase ++) {
1290 double minX, maxX, minY, maxY;
1294 minX, specialLatCases[specialLatCase],
true,
false,
true);
1297 minY, specialLatCases[specialLatCase],
false,
false,
true);
1300 maxX, specialLatCases[specialLatCase],
true,
false,
false);
1303 maxY, specialLatCases[specialLatCase],
false,
false,
false);
1312 for (
unsigned int specialLonCase = 0;
1313 specialLonCase < specialLonCases.size();
1314 specialLonCase ++) {
1315 double minX, maxX, minY, maxY;
1319 minX, specialLonCases[specialLonCase],
true,
true,
true);
1322 minY, specialLonCases[specialLonCase],
false,
true,
true);
1325 maxX, specialLonCases[specialLonCase],
true,
true,
false);
1328 maxY, specialLonCases[specialLonCase],
false,
true,
false);
1390 double &extremeVal,
const double constBorder,
1391 bool searchX,
bool searchLongitude,
bool findMin) {
1392 if (minBorder == Null || maxBorder == Null || constBorder == Null) {
1396 const int NUM_ATTEMPTS = (
unsigned int)DBL_DIG;
1399 double minBorderX, minBorderY, maxBorderX, maxBorderY;
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;
1412 while ((fabs(minBorderX - maxBorderX) > TOLERANCE
1413 || fabs(minBorderY - maxBorderY) > TOLERANCE)
1414 && (attempts < NUM_ATTEMPTS));
1419 if (attempts >= NUM_ATTEMPTS) {
1424 if (searchLongitude) {
1434 if (searchX) extremeVal = min(minBorderX, maxBorderX);
1435 else extremeVal = min(minBorderY, maxBorderY);
1438 if (searchX) extremeVal = max(minBorderX, maxBorderX);
1439 else extremeVal = max(minBorderY, maxBorderY);
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) {
1510 minBorderY = minBorderX;
1511 minBorderY = minBorderX;
1514 if (!searchLongitude && (fabs(fabs(constBorder) - 90.0) < DBL_EPSILON)) {
1519 maxBorderY = minBorderY;
1523 const double STEP_SIZE = (maxBorder - minBorder) / 10.0;
1524 const double LOOP_END = maxBorder + (STEP_SIZE / 2.0);
1527 double currBorderVal = minBorder;
1535 while (!
m_good && currBorderVal <= LOOP_END) {
1536 currBorderVal+=STEP_SIZE;
1537 if (searchLongitude && (currBorderVal - 90.0 > DBL_EPSILON)) {
1538 currBorderVal = 90.0;
1552 double border1 = currBorderVal;
1553 double border2 = currBorderVal;
1554 double border3 = currBorderVal;
1560 double value2 = value1;
1564 double extremeVal2 = value2;
1568 double extremeBorder1 = minBorder;
1569 double extremeBorder3 = minBorder;
1571 while (currBorderVal <= LOOP_END) {
1578 if (searchLongitude && (currBorderVal - 90.0 > DBL_EPSILON)) {
1579 currBorderVal = 90.0;
1583 currBorderVal += STEP_SIZE;
1592 border1 = currBorderVal;
1596 if ((findMin && value2 < extremeVal2)
1597 || (!findMin && value2 > extremeVal2)) {
1602 extremeVal2 = value2;
1604 extremeBorder3 = border3;
1605 extremeBorder1 = border1;
1612 minBorder = extremeBorder3;
1617 if (extremeBorder1 <= maxBorder ) {
1618 maxBorder = extremeBorder1;
1666 const double constBorder,
1667 bool variableIsLat) {
1668 if (variableBorder == Null || constBorder == Null) {
1672 if (variableIsLat) {
1673 lat = variableBorder;
1678 lon = variableBorder;
1691 PvlGroup mapping(
"Mapping");
1694 keyNames <<
"TargetName" <<
"ProjectionName" <<
"EquatorialRadius" <<
"PolarRadius"
1695 <<
"LatitudeType" <<
"LongitudeDirection" <<
"LongitudeDomain"
1696 <<
"PixelResolution" <<
"Scale" <<
"UpperLeftCornerX" <<
"UpperLeftCornerY"
1697 <<
"MinimumLatitude" <<
"MaximumLatitude" <<
"MinimumLongitude" <<
"MaximumLongitude"
1700 foreach (QString keyName, keyNames) {
1716 PvlGroup mapping(
"Mapping");
1732 PvlGroup mapping(
"Mapping");
1764 QString msg =
"Snyder's q variable should only be computed for "
1765 "ellipsoidal projections.";
1766 throw IException(IException::Unknown, msg, _FILEINFO_);
1770 * (sinPhi / (1 - eSinPhi * eSinPhi)
1771 - 1 / (2 *
m_eccentricity) * log( (1 - eSinPhi) / (1 + eSinPhi) ));
1796 double localPhi = HALFPI - 2.0 * atan(t);
1798 double difference = DBL_MAX;
1805 const int MAX_ITERATIONS = 45;
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);
1817 if (iteration >= MAX_ITERATIONS) {
1818 QString msg =
"Failed to converge in TProjection::phi2Compute()";
1819 throw IException(IException::Unknown, msg, _FILEINFO_);
1841 double denominator = sqrt(1.0 - eccTimesSinphi * eccTimesSinphi);
1842 return cosphi / denominator;
1863 if ((HALFPI) - fabs(phi) < DBL_EPSILON)
return 0.0;
1866 double denominator = pow((1.0 - eccTimesSinphi) /
1867 (1.0 + eccTimesSinphi),
1869 return tan(0.5 * (HALFPI - phi)) / denominator;
1887 return sqrt(pow(onePlusEcc, onePlusEcc) *
1888 pow(oneMinusEcc, oneMinusEcc));
double degrees() const
Get the angle in units of Degrees.
@ Degrees
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
This class is designed to encapsulate the concept of a Longitude.
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.
WorldMapper * m_mapper
This points to a mapper passed into the SetWorldMapper method.
double XCoord() const
This returns the projection X provided SetGround, SetCoordinate, SetUniversalGround,...
double m_maximumX
See minimumX description.
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.
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.
double m_minimumX
The data elements m_minimumX, m_minimumY, m_maximumX, and m_maximumY are convience data elements when...
Projection(Pvl &label)
Constructs an empty Projection object.
PvlGroup m_mappingGrp
Mapping group that created this projection.
double m_minimumY
See minimumX description.
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.
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.
This is free and unencumbered software released into the public domain.
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.