78 LambertAzimuthalEqualArea::LambertAzimuthalEqualArea(
81 TProjection::TProjection(label) {
87 PvlGroup &mapGroup = label.findGroup(
"Mapping",
Pvl::Traverse);
91 if (!mapGroup.hasKeyword(
"CenterLongitude")) {
93 double centerLon = (MinimumLongitude() + MaximumLongitude()) / 2.0;
94 mapGroup += PvlKeyword(
"CenterLongitude",
toString(centerLon),
"Degrees");
97 QString message =
"Cannot project using Lambert Azimuthal equal-area";
98 message +=
" without [CenterLongitude] value. Keyword does not exist";
99 message +=
" in labels and defaults are not allowed.";
106 if (!mapGroup.hasKeyword(
"CenterLatitude")) {
108 double centerLat = (MinimumLatitude() + MaximumLatitude()) / 2.0;
109 mapGroup += PvlKeyword(
"CenterLatitude",
toString(centerLat),
"Degrees");
112 QString message =
"Cannot project using Lambert Azimuthal equal-area";
113 message +=
" without [CenterLatitude] value. Keyword does not exist";
114 message +=
" in labels and defaults are not allowed.";
120 double centerLongitude = mapGroup[
"CenterLongitude"];
121 double centerLatitude = mapGroup[
"CenterLatitude"];
123 if (fabs(MinimumLongitude() - centerLongitude) > 360.0 ||
124 fabs(MaximumLongitude() - centerLongitude) > 360.0) {
125 IString message =
"[MinimumLongitude,MaximumLongitude] range of [";
126 message += IString(MinimumLongitude())+
","+IString(MaximumLongitude());
127 message +=
"] is invalid. No more than 360 degrees from the " 128 "CenterLongitude [" + IString(centerLongitude) +
"] is allowed.";
132 if (MaximumLongitude() - MinimumLongitude() > 360.0) {
133 IString message =
"[MinimumLongitude,MaximumLongitude] range of [" 134 + IString(MinimumLongitude()) +
"," 135 + IString(MaximumLongitude()) +
"] is invalid. " 136 "No more than 360 degree range width is allowed.";
141 init(centerLatitude, centerLongitude);
143 catch(IException &e) {
144 IString message =
"Invalid label group [Mapping]";
156 LambertAzimuthalEqualArea::~LambertAzimuthalEqualArea() {
172 if (!Projection::operator==(proj))
return false;
175 LambertAzimuthalEqualArea *lama = (LambertAzimuthalEqualArea *) &proj;
176 if ((lama->m_phi1 !=
m_phi1) ||
178 (lama->m_a !=
m_a) ||
179 (lama->m_e !=
m_e))
return false;
194 return "LambertAzimuthalEqualArea";
248 void LambertAzimuthalEqualArea::init(
double centerLatitude,
249 double centerLongitude) {
259 if (fabs(centerLongitude) > 360.0) {
260 IString message =
"CenterLongitude [" + IString(centerLongitude);
261 message +=
"] is outside the range of [-360, 360]";
266 if (fabs(centerLatitude) > 90.0) {
267 IString message =
"CenterLatitude [" + IString(centerLatitude);
268 message +=
"] is outside the range of [-90, 90]";
281 centerLongitude = -1.0*centerLongitude;
302 if (qFuzzyCompare(0.0,
m_e)) {
314 if (qFuzzyCompare(0.0,
m_phi1)) {
354 IString message =
"Invalid latitude and/or longitude range. ";
355 message +=
"Non-polar Lambert Azimuthal equal-area " 356 "projections can not project the antipodal point on " 370 void LambertAzimuthalEqualArea::initEllipsoid() {
440 if (fabs(lat) - 90.0 > DBL_EPSILON || lat ==
Null || lon ==
Null) {
441 if (!qFuzzyCompare(90.0, fabs(lat))) {
452 double phi = lat *
PI / 180.0;
453 double lambda = lon *
PI / 180.0;
456 if (lat > 90.0 && qFuzzyCompare(90.0, lat)) {
459 if (lat < -90.0 && qFuzzyCompare(-90.0, lat)) {
466 if (!
m_spherical)
return setGroundEllipsoid(phi, lambda);
469 double sinPhi = sin(phi);
470 double cosPhi = cos(phi);
471 double sinLambdaDiff = sin(lambda -
m_lambda0);
472 double cosLambdaDiff = cos(lambda -
m_lambda0);
481 double sinQuarterPiMinusHalfPhi = sin(
PI/4-phi/2);
482 x = 2*R*sinQuarterPiMinusHalfPhi*sinLambdaDiff;
483 y = -2*R*sinQuarterPiMinusHalfPhi*cosLambdaDiff;
484 m_h = cos(
PI/4-phi/2);
489 double cosQuarterPiMinusHalfPhi = cos(
PI/4-phi/2);
490 x = 2*R*cosQuarterPiMinusHalfPhi*sinLambdaDiff;
491 y = 2*R*cosQuarterPiMinusHalfPhi*cosLambdaDiff;
492 m_h = sin(
PI/4-phi/2);
499 if (qFuzzyCompare(-
m_phi1, phi)
501 && fabs(fmod(lambda-
m_lambda0, 2*
PI)) > DBL_EPSILON) {
516 trigTerms = cosPhi*cosLambdaDiff;
521 double cosProduct =
m_cosPhi1 * cosPhi * cosLambdaDiff;
522 trigTerms = sinProduct + cosProduct;
525 if (qFuzzyCompare(-1.0, trigTerms)) {
529 double denominator = 1 + trigTerms;
530 double kprime = sqrt(2/denominator);
531 x = R*kprime*cosPhi*sinLambdaDiff;
566 bool LambertAzimuthalEqualArea::setGroundEllipsoid(
double phi,
569 double sinPhi = sin(phi);
570 double cosPhi = cos(phi);
571 double sinLambdaDiff = sin(lambda -
m_lambda0);
572 double cosLambdaDiff = cos(lambda -
m_lambda0);
579 if (qFuzzyCompare(
PI/2, phi)) {
582 if (qFuzzyCompare(-
PI/2, phi)) {
587 double m =
mCompute(sinPhi, cosPhi);
589 double rho =
m_a* sqrt(
m_qp - q);
591 x = rho * sinLambdaDiff;
592 y = -1.0 * rho * cosLambdaDiff;
593 if (qFuzzyCompare(
m_phi1, phi)) {
603 double rho =
m_a* sqrt(
m_qp + q);
605 x = rho * sinLambdaDiff;
606 y = rho * cosLambdaDiff;
607 if (qFuzzyCompare(
m_phi1, phi)) {
621 double beta = asin( q/
m_qp);
626 double sinBeta = sin(beta);
627 double cosBeta = cos(beta);
633 double trigTerm = cosBeta*cosLambdaDiff;
634 if (qFuzzyCompare(-1, trigTerm)) {
644 double denominator = 1 + trigTerm;
645 x =
m_a * cosBeta * sinLambdaDiff * sqrt(2/denominator);
647 y = (
m_Rq*
m_Rq/
m_a) * sinBeta * sqrt(2/denominator);
664 double cosProduct =
m_cosBeta1 * cosBeta * cosLambdaDiff;
665 double trigTerms = sinProduct + cosProduct;
666 if (qFuzzyCompare(-1, trigTerms)) {
679 double denominator = 1 + trigTerms;
680 double kprime = sqrt(2/denominator);
681 double B =
m_Rq * kprime;
683 x = B *
m_D * cosBeta*sinLambdaDiff;
731 if (!
m_spherical)
return setCoordinateEllipsoid(x,y);
740 double rho = sqrt(x*x+y*y);
741 if (rho < DBL_EPSILON) {
747 if (fabs(rho/(2*R)) > 1 + DBL_EPSILON) {
754 else if (fabs(rho/(2*R)) > 1) {
759 double c = 2*asin(rho/(2*R));
761 double sinC = sin(c);
762 double cosC = cos(c);
782 lambda =
m_lambda0 + atan2(x * sinC, denominator);
829 bool LambertAzimuthalEqualArea::setCoordinateEllipsoid(
const double x,
837 double rho = sqrt(x*x+y*y);
850 double rho = sqrt((xD*xD)+(Dy*Dy));
851 if (fabs(rho) > fabs(2*
m_Rq)) {
855 double Ce = 2*asin(rho/(2*
m_Rq));
859 double sinCe = sin(Ce);
860 double cosCe = cos(Ce);
862 if (rho < DBL_EPSILON) {
872 double numerator = x * sinCe;
877 lambda =
m_lambda0 + atan2(numerator, denominator);
882 if (qFuzzyCompare(fabs(q), fabs(
m_qp))) {
895 bool converge =
false;
896 double tolerance = 1e-10;
898 int maximumIterations = 100;
903 double sinPhi = sin(phi);
904 double eSinPhi =
m_e*sinPhi;
905 double cosPhi = cos(phi);
906 double squareESinPhi = eSinPhi*eSinPhi;
907 double oneMinusSquareESinPhi = 1 - squareESinPhi;
909 phi += oneMinusSquareESinPhi*oneMinusSquareESinPhi / (2 * cosPhi)
911 - sinPhi / (oneMinusSquareESinPhi)
912 + log( (1 - eSinPhi) / (1 + eSinPhi) ) / (2 *
m_e));
914 if (fabs(phiOld - phi) < tolerance) {
917 if (i > maximumIterations) {
988 double &minY,
double &maxY) {
997 double maxCoordValue = 0;
1003 maxCoordValue = sqrt(2*eRad*eRad
1013 return xyRangeLambertAzimuthalPolar(minX, maxX, minY, maxY);
1083 bool LambertAzimuthalEqualArea::xyRangeLambertAzimuthalPolar(
double &minX,
1106 for (
double lon = centerLonDeg; lon < centerLonDeg + 360; lon += 90) {
1107 checkLongitude(lon);
1139 void LambertAzimuthalEqualArea::checkLongitude(
double longitude) {
1141 double centerLatDeg =
m_phi1 * 180.0 /
PI;
1143 double innerLatitude, outerLatitude;
1153 IString message =
"checkLongitude() should only be called for a " 1154 "polar aspect projection. CenterLatitude is [";
1155 message = message + IString(centerLatDeg) +
"] degrees.";
1160 bool thisLongitudeInMinMaxRange =
false;
1162 thisLongitudeInMinMaxRange =
true;
1168 if (adjustedMinLon > adjustedMaxLon) {
1169 if (adjustedLon > adjustedMinLon) {
1172 adjustedMinLon -= 360;
1175 if (adjustedMinLon <= adjustedLon
1176 && adjustedLon <= adjustedMaxLon) {
1177 thisLongitudeInMinMaxRange =
true;
1180 thisLongitudeInMinMaxRange =
false;
1183 if (thisLongitudeInMinMaxRange) {
1190 double distToMinLon = fabs(adjustedMinLon - adjustedLon);
1191 double distToMaxLon = fabs(adjustedMaxLon - adjustedLon);
1193 if (distToMinLon >= 180 ) {
1194 distToMinLon = fabs(360 - distToMinLon);
1196 if (distToMaxLon >= 180 ) {
1197 distToMaxLon = fabs(360 - distToMaxLon);
1200 double nearestBoundary = 0;
1201 if (distToMinLon < distToMaxLon) {
1209 if (distToMinLon <= 90 + DBL_EPSILON
1210 || distToMaxLon <= 90 + DBL_EPSILON) {
1333 double LambertAzimuthalEqualArea::relativeScaleFactorLongitude()
const {
1334 validateRelativeScaleFactor();
1352 double LambertAzimuthalEqualArea::relativeScaleFactorLatitude()
const {
1353 validateRelativeScaleFactor();
1379 void LambertAzimuthalEqualArea::validateRelativeScaleFactor()
const {
1381 IString message =
"Projection failed or ground and coordinates have not " 1382 "been set. Relative scale factor can not be computed.";
1386 IString message =
"For ellipsidal bodies, relative scale factor can only " 1387 "be computed for polar aspect projections.";
1391 IString message =
"Relative scale factor can not be computed for north " 1392 "polar aspect projection when ground is set to " 1397 IString message =
"Relative scale factor can not be computed for south " 1398 "polar aspect projection when ground is set to " 1403 IString message =
"Relative scale factor can not be computed.";
1428 bool allowDefaults) {
double EquatorialRadius() const
This returns the equatorial radius of the target.
double m_cosPhi1
The cosine of the center latitude.
double m_cosBeta1
The cosine of m_beta1.
bool m_northPolarAspect
Indicates whether this is a north polar aspect projection.
double m_sinPhi1
The sine of the center latitude.
PvlGroup MappingLongitudes()
This function returns the longitude keywords that this projection uses.
double MaximumLongitude() const
This returns the maximum longitude of the area of interest.
const double Null
Value for an Isis Null pixel.
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 m_lambda0
The center longitude for the map projection measured positive east, in radians.
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...
double m_beta1
The authalaic latitude.
double TrueScaleLatitude() const
This method returns the latitude of true scale.
const double PI
The mathematical constant PI.
Longitude values increase in the westerly direction.
double m_e
Eccentricity of the ellipsoid.
bool SetCoordinate(const double x, const double y)
This method is used to set the projection x/y.
double m_Rq
The radius of the sphere having the same surface area as the ellipsoid.
const double HALFPI
The mathematical constant PI/2.
Namespace for the standard library.
double m_q1
Snyder's q variable from equation (3-12) on page 187, computed for the center latitude, phi = m_phi1.
double m_minimumX
The data elements m_minimumX, m_minimumY, m_maximumX, and m_maximumY are convience data elements when...
QString Version() const
This method returns the Version of the map projection.
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
double m_D
Value used to correct scale in all directions at the center of the projection.
This error is for when a programmer made an API call that was illegal.
void SetComputedXY(double x, double y)
This protected method is a helper for derived classes.
double m_latitude
This contains the currently set latitude value.
virtual PvlGroup MappingLatitudes()
This function returns the latitude keywords that this projection uses.
double m_h
Relative scale factor along a meridian of longitude.
double MinimumLongitude() const
This returns the minimum longitude of the area of interest.
double m_qp
Snyder's q variable from equation (3-12) on page 187, computed for the north pole, phi = 90.
double m_maximumY
See minimumX description.
PvlGroup Mapping()
This function returns the keywords that this projection uses.
double m_longitude
This contains the currently set longitude value.
bool IsPlanetocentric() const
This indicates if the latitude type is planetocentric (as opposed to planetographic).
double m_a
Equitorial Radius of the ellipsoid.
Base class for Map Projections.
double m_minimumY
See minimumX description.
double Eccentricity() const
This returns the eccentricity of the target,.
int m_longitudeDomain
This integer is either 180 or 360 and is read from the labels.
static double To180Domain(const double lon)
This method converts a longitude into the -180 to 180 domain.
#define _FILEINFO_
Macro for the filename and line number.
double LocalRadius() const
This method returns the local radius in meters at the current latitude position.
A type of error that cannot be classified as any of the other error types.
bool operator==(const Projection &proj)
This method determines whether two map projection objects are equal by comparing the equatorial radiu...
Lambert Azimuthal Equal Area Map Projection.
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.
double m_m1
Snyder's m variable from equation (14-15) on page 187, computed from the center latitude, phi = m_phi1.
Container for cube-like labels.
double ToPlanetographic(const double lat) const
This method converts a planetocentric latitude to a planetographic latitude.
double MinimumLatitude() const
This returns the minimum latitude of the area of interest.
virtual PvlGroup Mapping()
This function returns the keywords that this projection uses.
bool m_spherical
Indicates whether the body to be projected is spherical.
bool m_good
Indicates if the contents of m_x, m_y, m_latitude, and m_longitude are valid.
static double To360Domain(const double lon)
This method converts a longitude into the 0 to 360 domain.
LongitudeDirection m_longitudeDirection
An enumerated type indicating the LongitudeDirection read from the labels.
double m_sinBeta1
The sine of m_beta1.
double m_phi1
The center latitude for the map projection, in radians.
double PolarRadius() const
This returns the polar radius of the target.
bool m_southPolarAspect
Indicates whether this is a south polar aspect projection.
Namespace for ISIS/Bullet specific routines.
double m_k
Relative scale factor along a parallel of latitude.
PvlGroup MappingLatitudes()
This function returns the latitude keywords that this projection uses.
double ToPlanetocentric(const double lat) const
This method converts a planetographic latitude to a planetocentric latitude.
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.
QString Name() const
This method returns the name of the map projection.
void XYRangeCheck(const double latitude, const double longitude)
This convience function is established to assist in the development of the XYRange virtual method...
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_maximumX
See minimumX description.
double qCompute(const double sinPhi) const
A convience method to compute Snyder's q equation (3-12) for a given latitude, .
PvlGroup m_mappingGrp
Mapping group that created this projection.
bool m_equatorialAspect
Indicates whether this is a equatorial aspect projection.
double MaximumLatitude() const
This returns the maximum latitude of the area of interest.