7 #include "LambertAzimuthalEqualArea.h"
15 #include "Constants.h"
16 #include "IException.h"
18 #include "TProjection.h"
21 #include "PvlKeyword.h"
22 #include "SpecialPixel.h"
62 LambertAzimuthalEqualArea::LambertAzimuthalEqualArea(
65 TProjection::TProjection(label) {
71 PvlGroup &mapGroup = label.findGroup(
"Mapping",
Pvl::Traverse);
75 if (!mapGroup.hasKeyword(
"CenterLongitude")) {
77 double centerLon = (MinimumLongitude() + MaximumLongitude()) / 2.0;
78 mapGroup += PvlKeyword(
"CenterLongitude",
toString(centerLon),
"Degrees");
81 QString message =
"Cannot project using Lambert Azimuthal equal-area";
82 message +=
" without [CenterLongitude] value. Keyword does not exist";
83 message +=
" in labels and defaults are not allowed.";
90 if (!mapGroup.hasKeyword(
"CenterLatitude")) {
92 double centerLat = (MinimumLatitude() + MaximumLatitude()) / 2.0;
93 mapGroup += PvlKeyword(
"CenterLatitude",
toString(centerLat),
"Degrees");
96 QString message =
"Cannot project using Lambert Azimuthal equal-area";
97 message +=
" without [CenterLatitude] value. Keyword does not exist";
98 message +=
" in labels and defaults are not allowed.";
104 double centerLongitude = mapGroup[
"CenterLongitude"];
105 double centerLatitude = mapGroup[
"CenterLatitude"];
107 if (fabs(MinimumLongitude() - centerLongitude) > 360.0 ||
108 fabs(MaximumLongitude() - centerLongitude) > 360.0) {
109 IString message =
"[MinimumLongitude,MaximumLongitude] range of [";
110 message += IString(MinimumLongitude())+
","+IString(MaximumLongitude());
111 message +=
"] is invalid. No more than 360 degrees from the "
112 "CenterLongitude [" + IString(centerLongitude) +
"] is allowed.";
116 if (MaximumLongitude() - MinimumLongitude() > 360.0) {
117 IString message =
"[MinimumLongitude,MaximumLongitude] range of ["
118 + IString(MinimumLongitude()) +
","
119 + IString(MaximumLongitude()) +
"] is invalid. "
120 "No more than 360 degree range width is allowed.";
125 init(centerLatitude, centerLongitude);
127 catch(IException &e) {
128 IString message =
"Invalid label group [Mapping]";
140 LambertAzimuthalEqualArea::~LambertAzimuthalEqualArea() {
156 if (!Projection::operator==(proj))
return false;
159 LambertAzimuthalEqualArea *lama = (LambertAzimuthalEqualArea *) &proj;
160 if ((lama->m_phi1 !=
m_phi1) ||
162 (lama->m_a !=
m_a) ||
163 (lama->m_e !=
m_e))
return false;
178 return "LambertAzimuthalEqualArea";
232 void LambertAzimuthalEqualArea::init(
double centerLatitude,
233 double centerLongitude) {
243 if (fabs(centerLongitude) > 360.0) {
244 IString message =
"CenterLongitude [" + IString(centerLongitude);
245 message +=
"] is outside the range of [-360, 360]";
250 if (fabs(centerLatitude) > 90.0) {
251 IString message =
"CenterLatitude [" + IString(centerLatitude);
252 message +=
"] is outside the range of [-90, 90]";
265 centerLongitude = -1.0*centerLongitude;
286 if (qFuzzyCompare(0.0,
m_e)) {
298 if (qFuzzyCompare(0.0,
m_phi1)) {
338 IString message =
"Invalid latitude and/or longitude range. ";
339 message +=
"Non-polar Lambert Azimuthal equal-area "
340 "projections can not project the antipodal point on "
354 void LambertAzimuthalEqualArea::initEllipsoid() {
424 if (fabs(lat) - 90.0 > DBL_EPSILON || lat ==
Null || lon ==
Null) {
425 if (!qFuzzyCompare(90.0, fabs(lat))) {
436 double phi = lat *
PI / 180.0;
437 double lambda = lon *
PI / 180.0;
440 if (lat > 90.0 && qFuzzyCompare(90.0, lat)) {
443 if (lat < -90.0 && qFuzzyCompare(-90.0, lat)) {
450 if (!
m_spherical)
return setGroundEllipsoid(phi, lambda);
453 double sinPhi = sin(phi);
454 double cosPhi = cos(phi);
455 double sinLambdaDiff = sin(lambda -
m_lambda0);
456 double cosLambdaDiff = cos(lambda -
m_lambda0);
465 double sinQuarterPiMinusHalfPhi = sin(
PI/4-phi/2);
466 x = 2*R*sinQuarterPiMinusHalfPhi*sinLambdaDiff;
467 y = -2*R*sinQuarterPiMinusHalfPhi*cosLambdaDiff;
468 m_h = cos(
PI/4-phi/2);
473 double cosQuarterPiMinusHalfPhi = cos(
PI/4-phi/2);
474 x = 2*R*cosQuarterPiMinusHalfPhi*sinLambdaDiff;
475 y = 2*R*cosQuarterPiMinusHalfPhi*cosLambdaDiff;
476 m_h = sin(
PI/4-phi/2);
483 if (qFuzzyCompare(-
m_phi1, phi)
485 && fabs(fmod(lambda-
m_lambda0, 2*
PI)) > DBL_EPSILON) {
500 trigTerms = cosPhi*cosLambdaDiff;
505 double cosProduct =
m_cosPhi1 * cosPhi * cosLambdaDiff;
506 trigTerms = sinProduct + cosProduct;
509 if (qFuzzyCompare(-1.0, trigTerms)) {
513 double denominator = 1 + trigTerms;
514 double kprime = sqrt(2/denominator);
515 x = R*kprime*cosPhi*sinLambdaDiff;
550 bool LambertAzimuthalEqualArea::setGroundEllipsoid(
double phi,
553 double sinPhi = sin(phi);
554 double cosPhi = cos(phi);
555 double sinLambdaDiff = sin(lambda -
m_lambda0);
556 double cosLambdaDiff = cos(lambda -
m_lambda0);
563 if (qFuzzyCompare(
PI/2, phi)) {
566 if (qFuzzyCompare(-
PI/2, phi)) {
571 double m =
mCompute(sinPhi, cosPhi);
573 double rho =
m_a* sqrt(
m_qp - q);
575 x = rho * sinLambdaDiff;
576 y = -1.0 * rho * cosLambdaDiff;
577 if (qFuzzyCompare(
m_phi1, phi)) {
587 double rho =
m_a* sqrt(
m_qp + q);
589 x = rho * sinLambdaDiff;
590 y = rho * cosLambdaDiff;
591 if (qFuzzyCompare(
m_phi1, phi)) {
605 double beta = asin( q/
m_qp);
610 double sinBeta = sin(beta);
611 double cosBeta = cos(beta);
617 double trigTerm = cosBeta*cosLambdaDiff;
618 if (qFuzzyCompare(-1, trigTerm)) {
628 double denominator = 1 + trigTerm;
629 x =
m_a * cosBeta * sinLambdaDiff * sqrt(2/denominator);
631 y = (
m_Rq*
m_Rq/
m_a) * sinBeta * sqrt(2/denominator);
648 double cosProduct =
m_cosBeta1 * cosBeta * cosLambdaDiff;
649 double trigTerms = sinProduct + cosProduct;
650 if (qFuzzyCompare(-1, trigTerms)) {
663 double denominator = 1 + trigTerms;
664 double kprime = sqrt(2/denominator);
665 double B =
m_Rq * kprime;
667 x = B *
m_D * cosBeta*sinLambdaDiff;
715 if (!
m_spherical)
return setCoordinateEllipsoid(x,y);
724 double rho = sqrt(x*x+y*y);
725 if (rho < DBL_EPSILON) {
731 if (fabs(rho/(2*R)) > 1 + DBL_EPSILON) {
738 else if (fabs(rho/(2*R)) > 1) {
743 double c = 2*asin(rho/(2*R));
745 double sinC = sin(c);
746 double cosC = cos(c);
766 lambda =
m_lambda0 + atan2(x * sinC, denominator);
813 bool LambertAzimuthalEqualArea::setCoordinateEllipsoid(
const double x,
821 double rho = sqrt(x*x+y*y);
834 double rho = sqrt((xD*xD)+(Dy*Dy));
835 if (fabs(rho) > fabs(2*
m_Rq)) {
839 double Ce = 2*asin(rho/(2*
m_Rq));
843 double sinCe = sin(Ce);
844 double cosCe = cos(Ce);
846 if (rho < DBL_EPSILON) {
856 double numerator = x * sinCe;
861 lambda =
m_lambda0 + atan2(numerator, denominator);
866 if (qFuzzyCompare(fabs(q), fabs(
m_qp))) {
879 bool converge =
false;
880 double tolerance = 1e-10;
882 int maximumIterations = 100;
887 double sinPhi = sin(phi);
888 double eSinPhi =
m_e*sinPhi;
889 double cosPhi = cos(phi);
890 double squareESinPhi = eSinPhi*eSinPhi;
891 double oneMinusSquareESinPhi = 1 - squareESinPhi;
893 phi += oneMinusSquareESinPhi*oneMinusSquareESinPhi / (2 * cosPhi)
895 - sinPhi / (oneMinusSquareESinPhi)
896 + log( (1 - eSinPhi) / (1 + eSinPhi) ) / (2 *
m_e));
898 if (fabs(phiOld - phi) < tolerance) {
901 if (i > maximumIterations) {
972 double &minY,
double &maxY) {
981 double maxCoordValue = 0;
987 maxCoordValue = sqrt(2*eRad*eRad
997 return xyRangeLambertAzimuthalPolar(minX, maxX, minY, maxY);
1067 bool LambertAzimuthalEqualArea::xyRangeLambertAzimuthalPolar(
double &minX,
1090 for (
double lon = centerLonDeg; lon < centerLonDeg + 360; lon += 90) {
1091 checkLongitude(lon);
1123 void LambertAzimuthalEqualArea::checkLongitude(
double longitude) {
1125 double centerLatDeg =
m_phi1 * 180.0 /
PI;
1127 double innerLatitude, outerLatitude;
1137 IString message =
"checkLongitude() should only be called for a "
1138 "polar aspect projection. CenterLatitude is [";
1139 message = message + IString(centerLatDeg) +
"] degrees.";
1144 bool thisLongitudeInMinMaxRange =
false;
1146 thisLongitudeInMinMaxRange =
true;
1152 if (adjustedMinLon > adjustedMaxLon) {
1153 if (adjustedLon > adjustedMinLon) {
1156 adjustedMinLon -= 360;
1159 if (adjustedMinLon <= adjustedLon
1160 && adjustedLon <= adjustedMaxLon) {
1161 thisLongitudeInMinMaxRange =
true;
1164 thisLongitudeInMinMaxRange =
false;
1167 if (thisLongitudeInMinMaxRange) {
1174 double distToMinLon = fabs(adjustedMinLon - adjustedLon);
1175 double distToMaxLon = fabs(adjustedMaxLon - adjustedLon);
1177 if (distToMinLon >= 180 ) {
1178 distToMinLon = fabs(360 - distToMinLon);
1180 if (distToMaxLon >= 180 ) {
1181 distToMaxLon = fabs(360 - distToMaxLon);
1184 double nearestBoundary = 0;
1185 if (distToMinLon < distToMaxLon) {
1193 if (distToMinLon <= 90 + DBL_EPSILON
1194 || distToMaxLon <= 90 + DBL_EPSILON) {
1317 double LambertAzimuthalEqualArea::relativeScaleFactorLongitude()
const {
1318 validateRelativeScaleFactor();
1336 double LambertAzimuthalEqualArea::relativeScaleFactorLatitude()
const {
1337 validateRelativeScaleFactor();
1363 void LambertAzimuthalEqualArea::validateRelativeScaleFactor()
const {
1365 IString message =
"Projection failed or ground and coordinates have not "
1366 "been set. Relative scale factor can not be computed.";
1370 IString message =
"For ellipsidal bodies, relative scale factor can only "
1371 "be computed for polar aspect projections.";
1375 IString message =
"Relative scale factor can not be computed for north "
1376 "polar aspect projection when ground is set to "
1381 IString message =
"Relative scale factor can not be computed for south "
1382 "polar aspect projection when ground is set to "
1387 IString message =
"Relative scale factor can not be computed.";
1412 bool allowDefaults) {