#include <cfloat>
#include <cmath>
#include <iomanip>
#include <iostream>

#include "Constants.h"
#include "IException.h"
#include "Preference.h"
#include "Projection.h"
#include "ProjectionFactory.h"
#include "Pvl.h"
#include "PvlGroup.h"
#include "PvlKeyword.h"
#include "SpecialPixel.h"
#include "Target.h"
#include "TProjection.h"
#include "WorldMapper.h"

/**
  * @internal
  *   @history 2016-08-26 Kelvin Rodriguez - Changed invalidValue varible to have
  *                           both an int and double version to avoid undefined behavior in
  *                           trying to convert double to int. Part of porting to OSX 10.11
  *   @history 2016-12-28 Kristin Berry - Updated to test inLatitude range and two new
  *                           inLongitudeRange functions.
 */

using namespace std;
using namespace Isis;
class MyProjection : public Isis::TProjection {
  public:
    // create a child class
    MyProjection(Pvl &lab) :
      TProjection(lab) {
    }
    // override pure virtual methods
    QString Name() const {
      return "None";
    }
    QString Version() const {
      return "1.0";
    }

    // override other virtual methods
    virtual double TrueScaleLatitude() const {
      return 45.0;
    }
    bool SetGround(const double lat, const double lon) {
      if((lat < -90.0) || (lat > 90.0)) {
        m_good = false;
      }
      else {
        m_latitude = lat;
        m_longitude = lon;
        double x = lon * 10.0;
        double y = lat + 90.0;
        SetComputedXY(x, y);
        m_good = true;
      }
      return m_good;
    }
    virtual bool SetCoordinate(const double x, const double y) {
      SetXY(x, y);
      m_longitude = GetX() / 10.0;
      m_latitude = GetY() - 90.0;
      m_good = true;
      return m_good;
    }
    bool XYRange(double &minX, double &maxX, double &minY, double &maxY) {
      minX = DBL_MAX;
      minY = DBL_MAX;
      maxX = -DBL_MAX;
      maxY = -DBL_MAX;
      if(!m_groundRangeGood) return false;
      XYRangeCheck(m_minimumLatitude, m_minimumLongitude);
      XYRangeCheck(m_minimumLatitude, m_maximumLongitude);
      XYRangeCheck(m_maximumLatitude, m_minimumLongitude);
      XYRangeCheck(m_maximumLatitude, m_maximumLongitude);
      minX = m_minimumX;
      minY = m_minimumY;
      maxX = m_maximumX;
      maxY = m_maximumY;
      return true;
    }

    bool testXYRangeOblique(double &minX, double &maxX,
                            double &minY, double &maxY) {
      return xyRangeOblique(minX, maxX, minY, maxY);
    }

    // create wrappers to test protected methods
    bool testinLongitudeRange(double longitude) { 
      return inLongitudeRange(longitude);
    }

    bool testinLongitudeRange(double minLon, double maxLon, double longitude) {
      return inLongitudeRange(minLon, maxLon, longitude);
    }

    bool testinLatitudeRange(double latitude) {
      return inLatitudeRange(latitude);
    }


    // create output method to print results of private methods
    void Output() const {
      cout << "tCompute(0,sin(0)):             " << tCompute(0.0, 0.0) << endl;
      cout << "tCompute(pi/4,sin(pi/4)):       " << tCompute(HALFPI / 2.0, sin(HALFPI / 2.0)) << endl;
      cout << "tCompute(pi/2,sin(pi/2)):       " << tCompute(HALFPI, sin(HALFPI)) << endl;
      cout << "mCompute(sin(0),cos(0)):        " << mCompute(sin(0.0), cos(0.0)) << endl;
      cout << "mCompute(sin(pi/4),cos(pi/4)):  " << mCompute(sin(HALFPI / 2.0), cos(HALFPI / 2.0)) << endl;
      cout << "e4Compute():                    " << e4Compute() << endl;
      cout << "phi2Compute(0):                 " << phi2Compute(0.0) << endl;
      cout << "phi2Compute(10):                " << phi2Compute(10.0) << endl;
      cout << "phi2Compute(100):               " << phi2Compute(100.0) << endl;
      cout << "phi2Compute(1000):              " << phi2Compute(1000.0) << endl;
      cout << "qCompute(sin(0)):               " << qCompute(0.0) << endl;
    }

    double testqCompute(const double sinPhi) const {
      return qCompute(sinPhi);
    }
};

class EmptyProjection : public TProjection {
  public:
    EmptyProjection(Pvl &lab) :
      TProjection(lab) {
    }

    // pure virtuals, must be overriden, no need to test since MyProjection will test these
    QString Name() const {
      return "None";
    }
    QString Version() const {
      return "1.0";
    }
};


class MyMapper : public WorldMapper {
  public:
    MyMapper() : WorldMapper() {};
    double ProjectionX(const double worldX) const {
      return worldX / 2.0;
    };
    double ProjectionY(const double worldY) const {
      return worldY / 3.0;
    };
    double WorldX(const double projectionX) const {
      return projectionX * 2.0;
    };
    double WorldY(const double projectionY) const {
      return projectionY * 3.0;
    };
    virtual double Resolution() const {
      return 0.5;
    }
};



// unitTest main
int main(int argc, char *argv[]) {
  Preference::Preferences(true);

  // create a MyProjection object with the given labels
  void Doit(Pvl & lab);

  cout.precision(13);
  cout << "Unit test for Projection ..." << endl << endl;
  cout << "///////////////////////////////////////////////////////////" << endl;
  cout << "Test Error Throws from the contructor...\n" << endl;

  cout << "Test for missing Mapping Group" << endl;
  Pvl lab;
  Doit(lab);
  cout << endl;

  cout << "Test for missing Equatorial Radius in the mapping group" << endl;
  lab.addGroup(PvlGroup("Mapping"));
  PvlGroup &mg = lab.findGroup("Mapping");
  Doit(lab);
  cout << endl;

  cout << "Test for missing polar radius in the mapping group" << endl;
  mg += PvlKeyword("EquatorialRadius", "-1.0");
  Doit(lab);
  cout << endl;

  cout << "Test for invalid Equatoral Radius value" << endl;
  mg += PvlKeyword("PolarRadius", "-0.95");
  Doit(lab);
  cout << endl;

  cout << "Test for invalid polar radius value" << endl;
  mg["EquatorialRadius"] = "1.0";
  mg += PvlKeyword("PolarRadius", "-0.95");
  Doit(lab);
  cout << endl;

  cout << "Test for missing latitude type in the mapping group" << endl;
  mg["PolarRadius"] = "0.95";
  Doit(lab);
  cout << endl;

  cout << "Test for invalid latitude type value" << endl;
  mg += PvlKeyword("LatitudeType", "Planeto");
  Doit(lab);
  cout << endl;

  cout << "Test for missing longitude direction in the mapping group" << endl;
  mg["LatitudeType"] = "Planetographic";
  Doit(lab);
  cout << endl;

  cout << "Test for invalid longitude direction value" << endl;
  mg += PvlKeyword("LongitudeDirection", "Up");
  Doit(lab);
  cout << endl;

  cout << "Test for missing longitude domain in the mapping group" << endl;
  mg["LongitudeDirection"] = "PositiveEast";
  Doit(lab);
  cout << endl;

  cout << "Test for invalid longitude domain value in the mapping group" << endl;
  mg += PvlKeyword("LongitudeDomain", "75");
  Doit(lab);
  cout << "///////////////////////////////////////////////////////////" << endl;
  cout << endl;

  mg["LongitudeDomain"] = "360";
  mg += PvlKeyword("ProjectionName", "MyProjection");

  cout << "Projection Specifications" << endl;
  MyProjection p(lab);
  // test methods that return properties of the target
  cout << "Equatorial Radius:         " << p.EquatorialRadius() << endl;
  cout << "Polar Radius:              " << p.PolarRadius() << endl;
  cout << "Eccentricity:              " << p.Eccentricity() << endl;
  // test methods that return properties of the projection
  cout << "Is Equatorial Cylindrical: " << p.IsEquatorialCylindrical() << endl;
  cout << "Latitude Type:             " << p.LatitudeTypeString() << endl;
  cout << "Is Planetographic:         " << p.IsPlanetographic() << endl;
  cout << "Is Planetocentric:         " << p.IsPlanetocentric() << endl;
  cout << "Longitude Direction:       " << p.LongitudeDirectionString() << endl;
  cout << "Is PositiveEast:           " << p.IsPositiveEast() << endl;
  cout << "Is PositiveWest:           " << p.IsPositiveWest() << endl;
  cout << "Longitude Domain:          " << p.LongitudeDomainString() << endl;
  cout << "Has 360 domain:            " << p.Has360Domain() << endl;
  cout << "Has 180 domain:            " << p.Has180Domain() << endl;
  cout << "Has ground range:          " << p.HasGroundRange() << endl;
  cout << "Rotation:                  " << p.Rotation() << endl;
  cout << endl;

  cout << "Testing conversion methods" << endl;
  cout << "Bring -50   into 360 Domain:  " << p.To360Domain(-50.0) << endl;
  cout << "Bring   0-e into 360 Domain:  " << p.To360Domain(0.0 - 1E-10) << endl;
  cout << "Bring   0   into 360 Domain:  " << p.To360Domain(0.0) << endl;
  cout << "Bring   0+e into 360 Domain:  " << p.To360Domain(0.0 + 1E-10) << endl;
  cout << "Bring  50   into 360 Domain:  " << p.To360Domain(50.0) << endl;
  cout << "Bring 360-e into 360 Domain:  " << p.To360Domain(360.0 - 1E-10) << endl;
  cout << "Bring 360   into 360 Domain:  " << p.To360Domain(360.0) << endl;
  cout << "Bring 360+e into 360 Domain:  " << p.To360Domain(360.0 + 1E-10) << endl;
  cout << "Bring 380   into 360 Domain:  " << p.To360Domain(380.0) << endl;
  cout << endl;
  cout << "Bring 240  into 180 Domain:  " << p.To180Domain(240.0) << endl;
  cout << "Bring 140  into 180 Domain:  " << p.To180Domain(140.0) << endl;
  cout << "Bring -180 into 180 Domain:  " << p.To180Domain(-180.0) << endl;
  cout << "Bring 180  into 180 Domain:  " << p.To180Domain(180.0) << endl;
  cout << endl;

  cout << "Change -90 to planetographic: " << p.ToPlanetographic(-90.0) << endl;
  cout << "Change -45 to planetographic: " << p.ToPlanetographic(-45.0) << endl;
  cout << "Change   0 to planetographic: " << p.ToPlanetographic(0.0) << endl;
  cout << "Change  45 to planetographic: " << p.ToPlanetographic(45.0) << endl;
  cout << "Change  90 to planetographic: " << p.ToPlanetographic(90.0) << endl;

  cout << "Change -90 to planetocentric: " << p.ToPlanetocentric(-90.0) << endl;
  cout << "Change -45 to planetocentric: " << p.ToPlanetocentric(-45.0) << endl;
  cout << "Change   0 to planetocentric: " << p.ToPlanetocentric(0.0) << endl;
  cout << "Change  45 to planetocentric: " << p.ToPlanetocentric(45.0) << endl;
  cout << "Change  90 to planetocentric: " << p.ToPlanetocentric(90.0) << endl;
  cout << endl;

  cout << "ocentric to ographic to ocentric = " << p.ToPlanetocentric(p.ToPlanetographic(45.0)) << endl;
  cout << "ographic to ocentric to ographic = " << p.ToPlanetographic(p.ToPlanetocentric(45.0)) << endl;
  cout << endl;

  //Test exceptions

  cout << "///////////////////////////////////////////////////////////" << endl;
  cout << "Test More Error Throws...\n" << endl;
  cout << "Testing unordered latitude range" << endl;
  mg += PvlKeyword("MinimumLatitude", "45.0");
  mg += PvlKeyword("MaximumLatitude", "-80.0");
  mg += PvlKeyword("MinimumLongitude", "15.0");
  mg += PvlKeyword("MaximumLongitude", "-190.0");
  Doit(lab);
  cout << endl;

  cout << "Testing invalid minimum latitude" << endl;
  mg["MinimumLatitude"] = "-95.0";
  Doit(lab);
  cout << endl;

  cout << "Testing invalid maximum latitude" << endl;
  mg["MinimumLatitude"].setValue("45.0", "units");
  mg["MaximumLatitude"].setValue("95.0", "units");
  Doit(lab);
  cout << endl;

  cout << "Testing unordered longitude range" << endl;
  mg["MaximumLatitude"].setValue("80.0", "units");
  Doit(lab);
  cout << "///////////////////////////////////////////////////////////" << endl;
  cout << endl;

  mg["MaximumLongitude"] = "190.0";

  cout << "Testing xyRange methods...\n" << endl;
  MyProjection p2(lab);
  cout << "Get ground range from the labels...  " << endl;
  cout << "Has as longitude range:  " << p2.HasGroundRange() << endl;
  cout << "Minimum latitude:        " << p2.MinimumLatitude() << endl;
  cout << "Maximum latitude:        " << p2.MaximumLatitude() << endl;
  cout << "Minimum longitude:       " << p2.MinimumLongitude() << endl;
  cout << "Maximum longitude:       " << p2.MaximumLongitude() << endl;
  cout << endl; 

  cout << "Testing inLatitudeRange, inLongitudeRange methods...\n" << endl;
  cout << "InLongitudeRange (15,190,0):   " << p2.testinLongitudeRange(15.0, 190.0, 0.0) << endl;
  cout << "InLongitudeRange (15,190,100): " << p2.testinLongitudeRange(15.0,190.0, 100.0) << endl;
  cout << "InLongitudeRange (100):        " << p2.testinLongitudeRange(100.0) << endl;
  cout << "InLongitudeRange (-12):        " << p2.testinLongitudeRange(-12.0) << endl;
  cout << "InLatitudeRange (-70):         " << p2.testinLatitudeRange(-70.0) << endl;
  cout << "InLatitudeRange (70):          " << p2.testinLatitudeRange(70.0) << endl;
  cout << endl; 

  double minX, maxX, minY, maxY;
  p2.XYRange(minX, maxX, minY, maxY);
  cout << "Find coordinate range ...  " << endl;
  cout << "Minimum X:              " << minX << endl;
  cout << "Maximum X:              " << maxX << endl;
  cout << "Minimum Y:              " << minY << endl;
  cout << "Maximum Y:              " << maxY << endl;

  p2.testXYRangeOblique(minX,  maxX,  minY,  maxY);
  cout << "Find coordinate range using xyRangeOblique...  " << endl;
  cout << "Minimum X:              " << minX << endl;
  cout << "Maximum X:              " << maxX << endl;
  cout << "Minimum Y:              " << minY << endl;
  cout << "Maximum Y:              " << maxY << endl;
  cout << endl;

  cout << "Testing Ground coordinate routines" << endl;
  cout << "Setting latitude to (-91,  0):  " << p2.SetGround(-91.0, 0.0) << endl;
  cout << "Is Good:                        " << p2.IsGood() << endl;
  cout << "Setting latitude to ( 91,  0):  " << p2.SetGround(91.0, 0.0) << endl;
  cout << "Is Good:                        " << p2.IsGood() << endl;
  cout << "Setting position to (60,  -5):  " << p2.SetGround(60.0, -5.0) << endl;
  cout << "Is Good:                        " << p2.IsGood() << endl;
  cout << "Latitude:                       " << p2.Latitude() << endl;
  cout << "Longitude:                      " << p2.Longitude() << endl;
  cout << "XCoord:                         " << p2.XCoord() << endl;
  cout << "YCoord:                         " << p2.YCoord() << endl;
  cout << "UniversalLatitude:              " << p2.UniversalLatitude() << endl;
  cout << "UniversalLongitude:             " << p2.UniversalLongitude() << endl;
  cout << endl;


  cout << "Testing Universal Ground coordinate routines" << endl;
  cout << "Setting position to (57.3920057293825,  355):  " << p2.SetUniversalGround(57.3920057293825, -5.0) << endl;
  cout << "Is Good:                                       " << p2.IsGood() << endl;
  cout << "Latitude:                                      " << p2.Latitude() << endl;
  cout << "Longitude:                                     " << p2.Longitude() << endl;
  cout << "XCoord:                                        " << p2.XCoord() << endl;
  cout << "YCoord:                                        " << p2.YCoord() << endl;
  cout << "UniversalLatitude:                             " << p2.UniversalLatitude() << endl;
  cout << "UniversalLongitude:                            " << p2.UniversalLongitude() << endl;
  cout << endl;

  /* The following projection is used to test the ToPlanetographic() methods
   *   at the latitude boundaries (-90 and 90 degrees). qFuzzyCompare() methods
   *   were added to accommodate for double imprecission.
   */
  Isis::Pvl latRangeTest;
  latRangeTest.addGroup(Isis::PvlGroup("Mapping"));
  Isis::PvlGroup &latTestGroup = latRangeTest.findGroup("Mapping");
  latTestGroup += Isis::PvlKeyword("TargetName", "Moon");
  latTestGroup += Isis::PvlKeyword("ProjectionName", "PolarStereographic");
  latTestGroup += Isis::PvlKeyword("EquatorialRadius", "1737400.0");
  latTestGroup += Isis::PvlKeyword("PolarRadius", "1737400.0");
  latTestGroup += Isis::PvlKeyword("LatitudeType", "Planetocentric");
  latTestGroup += Isis::PvlKeyword("LongitudeDirection", "PositiveEast");
  latTestGroup += Isis::PvlKeyword("LongitudeDomain", "360");
  latTestGroup += Isis::PvlKeyword("Scale", "5.0");
  latTestGroup += Isis::PvlKeyword("MinimumLatitude", "-90.0");
  latTestGroup += Isis::PvlKeyword("MaximumLatitude", "-45.0");
  latTestGroup += Isis::PvlKeyword("MinimumLongitude", "0.0");
  latTestGroup += Isis::PvlKeyword("MaximumLongitude", "360.0");
  latTestGroup += Isis::PvlKeyword("CenterLatitude", "-90.0");
  latTestGroup += Isis::PvlKeyword("CenterLongitude", "0.0");
  Isis::TProjection *latTestProjection =
      (Isis::TProjection *) Isis::ProjectionFactory::Create(latRangeTest);

  cout << "PolarStereographic Projection Specifications" << endl;
  // test methods that return properties of the target
  cout << "Equatorial Radius:         " << latTestProjection->EquatorialRadius() << endl;
  cout << "Polar Radius:              " << latTestProjection->PolarRadius() << endl;
  cout << "Eccentricity:              " << latTestProjection->Eccentricity() << endl;
  // test methods that return properties of the projection
  cout << "Is Equatorial Cylindrical: " << latTestProjection->IsEquatorialCylindrical() << endl;
  cout << "Latitude Type:             " << latTestProjection->LatitudeTypeString() << endl;
  cout << "Is Planetographic:         " << latTestProjection->IsPlanetographic() << endl;
  cout << "Is Planetocentric:         " << latTestProjection->IsPlanetocentric() << endl;
  cout << "Longitude Direction:       " << latTestProjection->LongitudeDirectionString() << endl;
  cout << "Is PositiveEast:           " << latTestProjection->IsPositiveEast() << endl;
  cout << "Is PositiveWest:           " << latTestProjection->IsPositiveWest() << endl;
  cout << "Longitude Domain:          " << latTestProjection->LongitudeDomainString() << endl;
  cout << "Has 360 domain:            " << latTestProjection->Has360Domain() << endl;
  cout << "Has 180 domain:            " << latTestProjection->Has180Domain() << endl;
  cout << "Has ground range:          " << latTestProjection->HasGroundRange() << endl;
  cout << "Rotation:                  " << latTestProjection->Rotation() << endl << endl;
  try {
    cout << "Setting position to (-90.000000000000767, 0.0)" << endl;
    latTestProjection->SetUniversalGround(-90.000000000000767, 0.0);
    cout << "Is Good:                                       "
         << latTestProjection->IsGood() << endl;
    cout << "Latitude:                                      "
         << latTestProjection->Latitude() << endl;
    cout << "Longitude:                                     "
         << latTestProjection->Longitude() << endl;
    cout << "XCoord:                                        "
         << latTestProjection->XCoord() << endl;
    cout << "YCoord:                                        "
         << latTestProjection->YCoord() << endl;
    cout << "UniversalLatitude:                             "
         << latTestProjection->UniversalLatitude() << endl;
    cout << "UniversalLongitude:                            "
         << latTestProjection->UniversalLongitude() << endl;
    cout << endl;
  }
  catch(IException &error) {
    error.print();
  }

  try {
    latTestProjection->SetUniversalGround(90.000000000000767, 0.0);
    cout << "Setting position to (90.000000000000767, 0.0)" << endl;
    cout << "Is Good:                                       "
         << latTestProjection->IsGood() << endl;
    cout << "Latitude:                                      "
         << latTestProjection->Latitude() << endl;
    cout << "Longitude:                                     "
         << latTestProjection->Longitude() << endl;
    cout << "XCoord:                                        "
         << latTestProjection->XCoord() << endl;
    cout << "YCoord:                                        "
         << latTestProjection->YCoord() << endl;
    cout << "UniversalLatitude:                             "
         << latTestProjection->UniversalLatitude() << endl;
    cout << "UniversalLongitude:                            "
         << latTestProjection->UniversalLongitude() << endl;
    cout << endl;
  }
  catch(IException &error) {
    error.print();
  }

  cout << "Testing projection coordinate routines" << endl;
  cout << "Setting x/y position to (-2550,15):  " << p2.SetCoordinate(-2250.0, 15.0) << endl;
  cout << "Is Good:                             " << p2.IsGood() << endl;
  cout << "Latitude:                            " << p2.Latitude() << endl;
  cout << "Longitude:                           " << p2.Longitude() << endl;
  cout << "XCoord:                              " << p2.XCoord() << endl;
  cout << "YCoord:                              " << p2.YCoord() << endl;
  cout << "UniversalLatitude:                   " << p2.UniversalLatitude() << endl;
  cout << "UniversalLongitude:                  " << p2.UniversalLongitude() << endl;
  cout << "WorldX:                              " << p2.WorldX() << endl;
  cout << "WorldY:                              " << p2.WorldY() << endl;
  cout << endl;

  p2.SetWorldMapper(new MyMapper());

  cout << "Testing world coordinate routines" << endl;
  cout << "Setting world x/y position to (-4500,45):  " << p2.SetWorld(-4500.0, 45.0) << endl;
  cout << "Is Good:                                   " << p2.IsGood() << endl;
  cout << "Latitude:                                  " << p2.Latitude() << endl;
  cout << "Longitude:                                 " << p2.Longitude() << endl;
  cout << "XCoord:                                    " << p2.XCoord() << endl;
  cout << "YCoord:                                    " << p2.YCoord() << endl;
  cout << "UniversalLatitude:                         " << p2.UniversalLatitude() << endl;
  cout << "UniversalLongitude:                        " << p2.UniversalLongitude() << endl;
  cout << "WorldX:                                    " << p2.WorldX() << endl;
  cout << "WorldY:                                    " << p2.WorldY() << endl;
  cout << "ToProjectionX (-4500):                     " << p2.ToProjectionX(-4500.0) << endl;
  cout << "ToProjectionY (45):                        " << p2.ToProjectionY(45.0) << endl;
  cout << "ToWorldX:                                  " << p2.ToWorldX(p2.ToProjectionX(-4500.0)) << endl;
  cout << "ToWorldY:                                  " << p2.ToWorldY(p2.ToProjectionY(45.0)) << endl;
  cout << "Resolution:                                " << p2.Resolution() << endl;
  cout << "Scale:                                     " << p2.Scale() << endl;
  cout << "TrueScaleLatitude:                         " << p2.TrueScaleLatitude() << endl;
  cout << endl;

  cout << "Testing IsSky method" << endl;
  cout << p2.IsSky() << endl;
  mg += PvlKeyword("TargetName", "SKY");
  Doit(lab);
  MyProjection p3(lab);
  cout << p3.IsSky() << endl;
  cout << endl;

  cout << "Testing string routines" << endl;
  cout << p2.LatitudeTypeString() << endl;
  cout << p2.LongitudeDirectionString() << endl;
  cout << p2.LongitudeDomainString() << endl;
  cout << endl;

  cout << "Testing Name and comparision routines" << endl;
  cout << "Name:        " << p2.Name() << endl;
  cout << "Version:     " << p2.Version() << endl;
  cout << "operator==:  " << (p == p2) << endl;
  cout << "operator!=:  " << (p != p2) << endl;

  mg["LongitudeDirection"] = "PositiveWest";
  mg["LongitudeDomain"] = "180";
  EmptyProjection noproj(lab);
  cout << endl;

  cout << "Testing no projection" << endl;
  noproj.SetUniversalGround(45.0, 270.0);
  cout << "Latitude:    " << noproj.Latitude() << endl;
  cout << "Longitude:   " << noproj.Longitude() << endl;
  cout << endl;

  cout << "Testing radius methods " << endl;
  cout << noproj.LocalRadius() << endl;
  cout << noproj.LocalRadius(0.0) << endl;
  cout << noproj.LocalRadius(90.0) << endl;
  cout << noproj.LocalRadius(-90.0) << endl;
  cout << endl;
  cout << "///////////////////////////////////////////////////////////" << endl;
  cout << "Test Error Throws for null input for LocalRadius " << endl;
  double nullLatitude = Null;
  try {
    noproj.LocalRadius(nullLatitude);
  }
  catch(IException &error) {
    error.print();
  }
  cout << "///////////////////////////////////////////////////////////" << endl;
  cout << endl;

  cout << "Testing compute methods " << endl;
  p.Output();
  cout << endl;
  cout << "///////////////////////////////////////////////////////////" << endl;
  cout << "Test Error Throw for compute methods..." << endl;
  // qCompute is not used for spherical targets
  // set polar radius = equatorial radius
  mg["PolarRadius"] = "1.0";
  Doit(lab);
  MyProjection p3a(lab);
  try {
    p3a.testqCompute(0);
  }
  catch(IException &error) {
    error.print();
  }
  mg["PolarRadius"] = "0.95";
  cout << "///////////////////////////////////////////////////////////" << endl;
  cout << endl;

  cout << "Testing static conversion methods " << endl;
  cout << " 0 degrees in hours: " << p.ToHours(0.0) << endl;
  cout << " 0 degrees in HMS format: " << p.ToHMS(0.0) << endl;
  cout << " 0 degrees in DMS format: " << p.ToDMS(0.0) << endl;
  cout << " 30.5 degrees in hours: " << p.ToHours(30.5) << endl;
  cout << " 30.5 degrees in HMS format: " << p.ToHMS(30.5) << endl;
  cout << " 30.5 degrees in DMS format: " << p.ToDMS(30.5) << endl;
  cout << " 40.3472 degrees in hours: " << p.ToHours(40.3472) << endl;
  cout << " 40.3472 degrees in HMS format: " << p.ToHMS(40.3472) << endl;
  cout << " 40.3472 degrees in DMS format: " << p.ToDMS(40.3472) << endl;
  cout << " 45 degrees in Hours: " << p.ToHours(45.0) << endl;
  cout << " 45 degrees in HMS format: " << p.ToHMS(45.0) << endl;
  cout << " 45 degrees in DMS format: " << p.ToDMS(45.0) << endl;
  cout << " 180 degrees in Hours: " << p.ToHours(180.0) << endl;
  cout << " 180 degrees in HMS format: " << p.ToHMS(180.0) << endl;
  cout << " 180 degrees in DMS format: " << p.ToDMS(180.0) << endl;
  cout << " 360 degrees in Hours: " << p.ToHours(360.0) << endl;
  cout << " 360 degrees in HMS format: " << p.ToHMS(360.0) << endl;
  cout << " 360 degrees in DMS format: " << p.ToDMS(360.0) << endl;
  cout << "-390 To180Domain:          " << p.To180Domain(-390) << endl;
  cout << "-390 To360Domain:          " << p.To360Domain(-390) << endl;
  cout << " 50 to Planetocentric (sphere): " << p.ToPlanetocentric(50, 180000, 180000) << endl;
  cout << " 50 to Planetographic (sphere): " << p.ToPlanetographic(50, 180000, 180000) << endl;
  cout << "-30 ToPositiveEast (180 domain):  " << p.ToPositiveEast(-30, 180) << endl;
  cout << " 30 ToPositiveEast (360 domain): " << p.ToPositiveEast(30, 360) << endl;
  cout << " 30 ToPositiveWest (180 domain): " << p.ToPositiveWest(30, 180) << endl;
  cout << "-30 ToPositiveWest (360 domain):  " << p.ToPositiveWest(-30, 360) << endl;

  cout << endl;
  cout << "///////////////////////////////////////////////////////////" << endl;
  cout << "Test Error Throws for invalid inputs to conversion methods " << endl;

  // Keep an double and an int for used for invalid data.
  // Seperating the two prevents undefined behavior from trying to
  // convert Isis::Null to an integer.
  double invalidValue = Null;
  int invalidInt = -INT_MAX;
  try {
    TProjection::To180Domain(invalidValue);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    TProjection::To360Domain(invalidValue);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    p.ToPlanetocentric(-100);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    p.ToPlanetocentric(100);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    p.ToPlanetocentric(invalidValue);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    TProjection::ToPlanetocentric(invalidValue, 1, 1);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    p.ToPlanetographic(invalidValue);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    p.ToPlanetographic(-100);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    p.ToPlanetographic(100);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    TProjection::ToPlanetographic(invalidValue, 1, 1);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    TProjection::ToPositiveEast(invalidValue, 180);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    TProjection::ToPositiveEast(0, invalidInt);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    TProjection::ToPositiveWest(invalidValue, 360);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    TProjection::ToPositiveWest(0, invalidInt);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    p.ToProjectionX(invalidValue);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    p.ToProjectionY(invalidValue);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    p.ToWorldX(invalidValue);
  }
  catch(IException &error) {
    error.print();
  }
  try {
    p.ToWorldY(invalidValue);
  }
  catch(IException &error) {
    error.print();
  }
  cout << "///////////////////////////////////////////////////////////" << endl;
  cout << endl;
  cout << endl;

  cout << "Rotation Tests" << endl;
  mg += PvlKeyword("Rotation", "90.0");
  mg["LongitudeDirection"] = "PositiveEast";
  MyProjection p4(lab);
  cout << "Rotation:     " << p4.Rotation() << endl;
  cout << "Testing Ground coordinate routines" << endl;
  cout << "Setting latitude to (-91,  0):  " << p4.SetGround(-91.0, 0.0) << endl;
  cout << "Is Good:                        " << p4.IsGood() << endl;
  cout << "Setting latitude to ( 91,  0):  " << p4.SetGround(91.0, 0.0) << endl;
  cout << "Is Good:                        " << p4.IsGood() << endl;
  cout << "Setting position to (60,  -5):  " << p4.SetGround(60.0, -5.0) << endl;
  cout << "Is Good:                        " << p4.IsGood() << endl;
  cout << "Latitude:                       " << p4.Latitude() << endl;
  cout << "Longitude:                      " << p4.Longitude() << endl;
  cout << "XCoord:                         " << p4.XCoord() << endl;
  cout << "YCoord:                         " << p4.YCoord() << endl;
  cout << "UniversalLatitude:              " << p4.UniversalLatitude() << endl;
  cout << "UniversalLongitude:             " << p4.UniversalLongitude() << endl;
  cout << endl;

  cout << "Testing projection coordinate routines" << endl;
  cout << "Setting x/y position to (150,50):  " << p4.SetCoordinate(150.0, 50.0) << endl;
  cout << "Is Good:                             " << p4.IsGood() << endl;
  cout << "Latitude:                            " << p4.Latitude() << endl;
  cout << "Longitude:                           " << p4.Longitude() << endl;
  cout << "XCoord:                              " << p4.XCoord() << endl;
  cout << "YCoord:                              " << p4.YCoord() << endl;
  cout << "UniversalLatitude:                   " << p4.UniversalLatitude() << endl;
  cout << "UniversalLongitude:                  " << p4.UniversalLongitude() << endl;
  cout << "WorldX:                              " << p4.WorldX() << endl;
  cout << "WorldY:                              " << p4.WorldY() << endl;
  cout << endl;

  Pvl mapping;
  mapping.addGroup(p4.Mapping());
  cout << "Testing Mapping() methods" << endl;
  cout << "Mapping() = " << endl;
  cout << mapping << endl;
  mapping.deleteGroup("Mapping");
  mapping.addGroup(p4.MappingLatitudes());
  cout << "MappingLatitudes() = " << endl;
  cout << mapping << endl;
  mapping.deleteGroup("Mapping");
  mapping.addGroup(p4.MappingLongitudes());
  cout << "MappingLongitudes() = " << endl;
  cout << mapping << endl;
  mapping.deleteGroup("Mapping");
  cout << endl;

  //SetUpperLeftCorner(Displacement x, Displacement y)
}


void Doit(Pvl &lab) {
  try {
    MyProjection p(lab);
  }
  catch(IException &error) {
    error.print();
  }
}
