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,
 
   49      TProjection::TProjection(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.
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.