7#include "UpturnedEllipsoidTransverseAzimuthal.h" 
   17#include "IException.h" 
   19#include "TProjection.h" 
   22#include "PvlKeyword.h" 
   23#include "SpecialPixel.h" 
   47  UpturnedEllipsoidTransverseAzimuthal::UpturnedEllipsoidTransverseAzimuthal(
Pvl &label,
 
   57      PvlGroup &mapGroup = label.findGroup(
"Mapping", Pvl::Traverse);
 
   61      double centerLongitude = 0.0;
 
   62      if (!mapGroup.hasKeyword(
"CenterLongitude")) {
 
   65          mapGroup += PvlKeyword(
"CenterLongitude", 
toString(centerLongitude), 
"Degrees");
 
   68          QString message = 
"Cannot project using upturned ellipsoid Transverse Azimuthal";
 
   69          message += 
" without [CenterLongitude] value.  Keyword does not exist";
 
   70          message += 
" in labels and defaults are not allowed.";
 
   71          throw IException(IException::Unknown, message, _FILEINFO_);
 
   75        centerLongitude = mapGroup[
"CenterLongitude"];
 
   78      if (MinimumLongitude() < centerLongitude - 90.0) {
 
   79        QString message = 
"MinimumLongitude ["   
   81                          + 
"] is invalid. Must be within -90 degrees of the CenterLongitude [" 
   83        throw IException(IException::Unknown, message, _FILEINFO_);
 
   85      if (MaximumLongitude() > centerLongitude + 90.0) {
 
   86        QString message = 
"MaximumLongitude ["   
   88                          + 
"] is invalid. Must be within +90 degrees of the CenterLongitude [" 
   90        throw IException(IException::Unknown, message, _FILEINFO_);
 
   95      init(centerLongitude);
 
   97    catch(IException &e) {
 
   98      QString message = 
"Invalid label group [Mapping]";
 
   99      throw IException(e, IException::Unknown, message, _FILEINFO_);
 
  109  UpturnedEllipsoidTransverseAzimuthal::~UpturnedEllipsoidTransverseAzimuthal() {
 
  123  bool UpturnedEllipsoidTransverseAzimuthal::operator== (
const Projection &proj) {
 
  128    UpturnedEllipsoidTransverseAzimuthal *tcyl = (UpturnedEllipsoidTransverseAzimuthal *) &proj;
 
  129    if ((tcyl->m_lambda0 != m_lambda0) ||
 
  130        (tcyl->m_a != m_a) ||
 
  131        (tcyl->m_b != m_b)) 
return false;
 
  144  QString UpturnedEllipsoidTransverseAzimuthal::Name()
 const {
 
  145    return "UpturnedEllipsoidUpturnedEllipsoidTransverseAzimuthal";
 
  154  QString  UpturnedEllipsoidTransverseAzimuthal::Version()
 const {
 
  170  void UpturnedEllipsoidTransverseAzimuthal::init(
double centerLongitude) {
 
  173    if (IsPositiveEast()) {
 
  174      m_lambda0 = centerLongitude * 
DEG2RAD;
 
  177      m_lambda0 = ToPositiveEast(centerLongitude, 360) * 
DEG2RAD;
 
  179    if (Has180Domain()) {
 
  180      m_lambda0 = To180Domain(m_lambda0);
 
  183      m_lambda0 = To360Domain(m_lambda0);
 
  189    m_minimumX = DBL_MAX;
 
  190    m_maximumX = -DBL_MAX;
 
  191    m_minimumY = DBL_MAX;
 
  192    m_maximumY = -DBL_MAX;
 
  195    double axis1 = EquatorialRadius();
 
  196    double axis2 = PolarRadius();
 
  197    m_a = qMax(axis1, axis2); 
 
  198    m_b = qMin(axis1, axis2); 
 
  199    m_e = Eccentricity();  
 
  201    if (qFuzzyCompare(0.0, m_e)) {
 
  206    double t0 = m_b / m_a; 
 
  209    double k1 = 2 * m_a * exp(m_t1 * atan(m_t1));
 
  233  bool UpturnedEllipsoidTransverseAzimuthal::SetGround(
const double lat, 
 
  236    if (lat == Null || lon == Null) {
 
  245    if (qFuzzyCompare(90.0, qAbs(lat)) && qAbs(lat) > 90.0) {
 
  246      phiNorm = copysign(HALFPI, lat); 
 
  249    else if (qAbs(lat) > 90.0) {
 
  253    else if (IsPlanetocentric()) {
 
  258      phiNorm = ToPlanetocentric(lat) * 
DEG2RAD;
 
  264    if (IsPositiveEast()) {
 
  265      lambdaNorm = lon * 
DEG2RAD - m_lambda0;
 
  268      lambdaNorm = ToPositiveEast(lon, 360) * 
DEG2RAD - m_lambda0;
 
  275    double cosz = cos(phiNorm) * cos(lambdaNorm);
 
  293    else if (cosz > 0.5) { 
 
  295      double sinz = sqrt( 1 - cosz*cosz ); 
 
  299      double phi = 
HALFPI - atan2( sinz, m_t * cosz );
 
  300      double sinPhi = sin(phi);
 
  307      double rhoOverTanZ = m_k * sinPhi / ( ( 1 + sinPhi )
 
  309                                            * exp( m_t1 * atan( m_t1 * sinPhi ) ) );
 
  310      x = rhoOverTanZ * tan(lambdaNorm);
 
  311      y = rhoOverTanZ * (sin(phiNorm) / cosz ); 
 
  327      double tolerance = 0.0016;
 
  328      double coszmax = cos(PI - tolerance);
 
  329      double lambdaModulus = fmod(lambdaNorm, TWOPI);
 
  330      if (cosz < coszmax) {
 
  337        if (-PI - tolerance < lambdaModulus && lambdaModulus <= -PI) {
 
  338          lambdaNorm = -
PI - tolerance;
 
  344        else if (-PI < lambdaModulus && lambdaModulus <= -PI + tolerance) {
 
  345          lambdaNorm = -
PI + tolerance;
 
  351        else if (PI - tolerance < lambdaModulus && lambdaModulus <= PI) {
 
  352          lambdaNorm = 
PI - tolerance;
 
  358        else if (PI < lambdaModulus && lambdaModulus < PI + tolerance) {
 
  359          lambdaNorm = 
PI + tolerance;
 
  363      double sinz = sqrt( 1 - cosz*cosz ); 
 
  369      double phi = atan2( m_t * cosz, sinz );
 
  370      double sinPhi = sin(phi);
 
  375      double rhoOverSinZ = m_k * cos(phi) / ( ( 1 + sinPhi )
 
  377                                              * exp( m_t1 * atan(m_t1 * sinPhi ) ) );
 
  378      x = rhoOverSinZ * cos(phiNorm) * sin(lambdaNorm);
 
  379      y = rhoOverSinZ * sin(phiNorm);
 
  409  bool UpturnedEllipsoidTransverseAzimuthal::SetCoordinate(
const double x, 
 
  411    if (x == Null || y == Null) {
 
  418    if (qFuzzyCompare(x + 1.0,  1.0) && qFuzzyCompare(y + 1.0,  1.0)) {
 
  421      m_longitude = m_lambda0 * 
RAD2DEG;
 
  449      double phi0, fphi0, fprimephi0, phi1;
 
  450      bool converged = 
false;
 
  452      double tolerance = 10e-10;
 
  454      while (!converged && iterations < 1000) {
 
  455        fphi0 = m_k * cos(phi0) / ((1 + sin(phi0)) * exp(m_t1 * atan( m_t1 * sin(phi0) )))
 
  458        fprimephi0 = -m_k * (1 + m_t1 * m_t1)
 
  460                        * exp(m_t1 * atan( m_t1 * sin(phi0) )) 
 
  461                        * (1 + m_t1 * m_t1 * sin(phi0) * sin(phi0)));
 
  462        phi1 = phi0 - fphi0 / fprimephi0;
 
  464        if (qAbs(phi1) > HALFPI) {
 
  465          double phiDegrees = To180Domain(phi1*RAD2DEG);
 
  466          if (phiDegrees > 90.0) {
 
  469          if (phiDegrees < -180.0) {
 
  474        if (qAbs(phi0 - phi1) < tolerance) {
 
  496      double z = atan2((1 - m_e * m_e), tan(phi)); 
 
  504      double rho = sqrt(x*x + y*y);
 
  505      double phiNorm = asin( y * sin(z) / rho );
 
  512      double cosLambdaNorm = cos(z) / cos(phiNorm);    
 
  513      if (cosLambdaNorm > 1.0) {
 
  516      else if (cosLambdaNorm < -1.0) {
 
  519      else if (x >= 0 && y >= 0) {
 
  520        lambdaNorm = acos(cosLambdaNorm);
 
  522      else if (x < 0 && y >= 0) {
 
  523        lambdaNorm = -acos(cosLambdaNorm);
 
  525      else if (y < 0 && x >= 0) {
 
  526        lambdaNorm = acos(cosLambdaNorm);
 
  529        lambdaNorm = -acos(cosLambdaNorm);
 
  533      m_longitude = (lambdaNorm + m_lambda0) * RAD2DEG;
 
  534      m_latitude = phiNorm * 
RAD2DEG;
 
  538    if (IsPlanetocentric()) {
 
  539      m_latitude = ToPlanetocentric(m_latitude);
 
  543    if (IsPositiveWest()) {
 
  544      m_longitude = ToPositiveWest(m_longitude, m_longitudeDomain);
 
  547    if (Has180Domain()) {
 
  548      m_longitude = To180Domain(m_longitude);
 
  552      m_longitude = To360Domain(m_longitude);
 
  596  bool UpturnedEllipsoidTransverseAzimuthal::XYRange(
double &minX, 
double &maxX, 
 
  597                                      double &minY, 
double &maxY) {
 
  600    XYRangeCheck(m_minimumLatitude, m_minimumLongitude);
 
  601    XYRangeCheck(m_maximumLatitude, m_minimumLongitude);
 
  602    XYRangeCheck(m_minimumLatitude, m_maximumLongitude);
 
  603    XYRangeCheck(m_maximumLatitude, m_maximumLongitude);
 
  605    double centerLongitude = m_lambda0 * 
RAD2DEG;
 
  607    bool centerLongitudeInRange =    TProjection::inLongitudeRange(centerLongitude);
 
  608    bool centerLongitude90InRange =  TProjection::inLongitudeRange(centerLongitude + 90.0);
 
  609    bool centerLongitude180InRange = TProjection::inLongitudeRange(centerLongitude + 180.0);
 
  610    bool centerLongitude270InRange = TProjection::inLongitudeRange(centerLongitude + 270.0);
 
  612    if (centerLongitudeInRange) {
 
  613      XYRangeCheck(m_minimumLatitude, centerLongitude);
 
  614      XYRangeCheck(m_maximumLatitude, centerLongitude);
 
  616    if (centerLongitude90InRange) {
 
  617      XYRangeCheck(m_minimumLatitude, centerLongitude + 90.0);
 
  618      XYRangeCheck(m_maximumLatitude, centerLongitude + 90.0);
 
  620    if (centerLongitude180InRange) {
 
  621      XYRangeCheck(m_minimumLatitude, centerLongitude + 180.0);
 
  622      XYRangeCheck(m_maximumLatitude, centerLongitude + 180.0);
 
  624    if (centerLongitude270InRange) {
 
  625      XYRangeCheck(m_minimumLatitude, centerLongitude + 270.0);
 
  626      XYRangeCheck(m_maximumLatitude, centerLongitude + 270.0);
 
  629    if (TProjection::inLatitudeRange(0.0)) {
 
  630      XYRangeCheck(0.0, m_minimumLongitude);
 
  631      XYRangeCheck(0.0, m_maximumLongitude);
 
  632      if (centerLongitudeInRange) {
 
  633        XYRangeCheck(0.0, centerLongitude);
 
  635      if (centerLongitude90InRange) {
 
  636        XYRangeCheck(0.0, centerLongitude + 90.0);
 
  638      if (centerLongitude180InRange) {
 
  639        XYRangeCheck(0.0, centerLongitude + 180.0);
 
  641      if (centerLongitude270InRange) {
 
  642        XYRangeCheck(0.0, centerLongitude + 270.0);
 
  647    if (m_minimumX >= m_maximumX) 
return false;
 
  648    if (m_minimumY >= m_maximumY) 
return false;
 
  683  PvlGroup UpturnedEllipsoidTransverseAzimuthal::Mapping() {
 
  684    PvlGroup mapping = TProjection::Mapping();
 
  685    mapping += PvlKeyword(
"CenterLongitude", 
toString(m_lambda0 * RAD2DEG));
 
  704  PvlGroup UpturnedEllipsoidTransverseAzimuthal::MappingLatitudes() {
 
  705    return TProjection::MappingLatitudes();
 
  724  PvlGroup UpturnedEllipsoidTransverseAzimuthal::MappingLongitudes() {
 
  725    PvlGroup mapping = TProjection::MappingLongitudes();
 
  726    mapping += PvlKeyword(
"CenterLongitude", 
toString(m_lambda0 * RAD2DEG));
 
  748                                                                          bool allowDefaults) {
 
Base class for Map Projections.
 
Container for cube-like labels.
 
Base class for Map TProjections.
 
Upturned Ellipsoid Transverse Azimuthal Map Projection.
 
This is free and unencumbered software released into the public domain.
 
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
 
const double DEG2RAD
Multiplier for converting from degrees to radians.
 
const double HALFPI
The mathematical constant PI/2.
 
const double RAD2DEG
Multiplier for converting from radians to degrees.
 
const double PI
The mathematical constant PI.
 
Namespace for the standard library.