Example #1
0
void IsisMain() {
  ProcessRubberSheet p;

  // Open the input cube
  Cube *icube = p.SetInputCube ("FROM");

  // Set up the transform object
  UserInterface &ui = Application::GetUserInterface();
  Transform *transform = new Rotate(icube->Samples(), icube->Lines(),
                                        ui.GetDouble("DEGREES"));

  // Determine the output size
  int samples = transform->OutputSamples();
  int lines = transform->OutputLines();

  // Allocate the output file
  p.SetOutputCube ("TO", samples, lines, icube->Bands());

  // Set up the interpolator
  Interpolator *interp;
  if (ui.GetString("INTERP") == "NEARESTNEIGHBOR") {
    interp = new Interpolator(Interpolator::NearestNeighborType);
  }
  else if (ui.GetString("INTERP") == "BILINEAR") {
    interp = new Interpolator(Interpolator::BiLinearType);
  }
  else if (ui.GetString("INTERP") == "CUBICCONVOLUTION") {
    interp = new Interpolator(Interpolator::CubicConvolutionType);
  }
  else {
    string msg = "Unknow value for INTERP [" +
                 ui.GetString("INTERP") + "]";
    throw iException::Message(iException::Programmer,msg,_FILEINFO_);
  }

  p.StartProcess(*transform, *interp);
  p.EndProcess();

  delete transform;
  delete interp;
}
Example #2
0
void IsisMain() {
  ProcessRubberSheet p;

  // Open the input cube
  Cube *icube = p.SetInputCube("FROM");

  // Set up the transform object
  UserInterface &ui = Application::GetUserInterface();
  Transform *transform = new Translate(icube->sampleCount(), icube->lineCount(),
                                       ui.GetDouble("STRANS"),
                                       ui.GetDouble("LTRANS"));

  // Allocate the output file, same size as input
  p.SetOutputCube("TO", icube->sampleCount(), icube->lineCount(), icube->bandCount());

  // Set up the interpolator
  Interpolator *interp;
  if(ui.GetString("INTERP") == "NEARESTNEIGHBOR") {
    interp = new Interpolator(Interpolator::NearestNeighborType);
  }
  else if(ui.GetString("INTERP") == "BILINEAR") {
    interp = new Interpolator(Interpolator::BiLinearType);
  }
  else if(ui.GetString("INTERP") == "CUBICCONVOLUTION") {
    interp = new Interpolator(Interpolator::CubicConvolutionType);
  }
  else {
    QString msg = "Unknow value for INTERP [" +
                  ui.GetString("INTERP") + "]";
    throw IException(IException::Programmer, msg, _FILEINFO_);
  }

  p.StartProcess(*transform, *interp);
  p.EndProcess();

  delete transform;
  delete interp;
}
Example #3
0
void IsisMain() {
  // Open the match cube and get the camera model on it
  ProcessRubberSheet m;
  Cube *mcube = m.SetInputCube("MATCH");
  Cube *ocube = m.SetOutputCube("TO");

  // Set up the default reference band to the middle of the cube
  // If we have even bands it will be close to the middle
  int referenceBand = ocube->bandCount();
  referenceBand += (referenceBand % 2);
  referenceBand /= 2;

  // See if the user wants to override the reference band
  UserInterface &ui = Application::GetUserInterface();
  if(ui.WasEntered("REFBAND")) {
    referenceBand = ui.GetInteger("REFBAND");
  }

  // Using the Camera method out of the object opack will not work, because the
  // filename required by the Camera is not passed by the process class in this
  // case.  Use the CameraFactory to create the Camera instead to get around this
  // problem.
  Camera *outcam = CameraFactory::Create(*(mcube->label()));

  // Set the reference band we want to match
  PvlGroup instgrp = mcube->group("Instrument");
  if(!outcam->IsBandIndependent()) {
    PvlKeyword rBand("ReferenceBand", toString(referenceBand));
    rBand.addComment("# All bands are aligned to reference band");
    instgrp += rBand;
    mcube->putGroup(instgrp);
    delete outcam;
    outcam = NULL;
  }

  // Only recreate the output camera if it was band dependent
  if(outcam == NULL) outcam = CameraFactory::Create(*(mcube->label()));

  // We might need the instrument group later, so get a copy before clearing the input
  //   cubes.
  m.ClearInputCubes();

  Cube *icube = m.SetInputCube("FROM");
  incam = icube->camera();

  // Set up the transform object which will simply map
  // output line/samps -> output lat/lons -> input line/samps
  Transform *transform = new cam2cam(icube->sampleCount(),
                                     icube->lineCount(),
                                     incam,
                                     ocube->sampleCount(),
                                     ocube->lineCount(),
                                     outcam);


  // Add the reference band to the output if necessary
  ocube->putGroup(instgrp);

  // Set up the interpolator
  Interpolator *interp = NULL;
  if(ui.GetString("INTERP") == "NEARESTNEIGHBOR") {
    interp = new Interpolator(Interpolator::NearestNeighborType);
  }
  else if(ui.GetString("INTERP") == "BILINEAR") {
    interp = new Interpolator(Interpolator::BiLinearType);
  }
  else if(ui.GetString("INTERP") == "CUBICCONVOLUTION") {
    interp = new Interpolator(Interpolator::CubicConvolutionType);
  }

  // See if we need to deal with band dependent camera models
  if(!incam->IsBandIndependent()) {
    m.BandChange(BandChange);
  }

  // Warp the cube
  m.StartProcess(*transform, *interp);
  m.EndProcess();

  // Cleanup
  delete transform;
  delete interp;
}
Example #4
0
void IsisMain() {
  // We will be warping a cube
  ProcessRubberSheet p;

  // Get the map projection file provided by the user
  UserInterface &ui = Application::GetUserInterface();
  Pvl userPvl(ui.GetFileName("MAP"));
  PvlGroup &userMappingGrp = userPvl.findGroup("Mapping", Pvl::Traverse);

  // Open the input cube and get the projection
  Cube *icube = p.SetInputCube("FROM");

  // Get the mapping group
  PvlGroup fromMappingGrp = icube->group("Mapping");
  TProjection *inproj = (TProjection *) icube->projection();
  PvlGroup outMappingGrp = fromMappingGrp;

  // If the default range is FROM, then wipe out any range data in user mapping file
  if(ui.GetString("DEFAULTRANGE").compare("FROM") == 0 && !ui.GetBoolean("MATCHMAP")) {
    if(userMappingGrp.hasKeyword("MinimumLatitude")) {
      userMappingGrp.deleteKeyword("MinimumLatitude");
    }

    if(userMappingGrp.hasKeyword("MaximumLatitude")) {
      userMappingGrp.deleteKeyword("MaximumLatitude");
    }

    if(userMappingGrp.hasKeyword("MinimumLongitude")) {
      userMappingGrp.deleteKeyword("MinimumLongitude");
    }

    if(userMappingGrp.hasKeyword("MaximumLongitude")) {
      userMappingGrp.deleteKeyword("MaximumLongitude");
    }
  }

  // Deal with user overrides entered in the GUI. Do this by changing the user's mapping group, which
  // will then overlay anything in the output mapping group.
  if(ui.WasEntered("MINLAT") && !ui.GetBoolean("MATCHMAP")) {
    userMappingGrp.addKeyword(PvlKeyword("MinimumLatitude", toString(ui.GetDouble("MINLAT"))), Pvl::Replace);
  }

  if(ui.WasEntered("MAXLAT") && !ui.GetBoolean("MATCHMAP")) {
    userMappingGrp.addKeyword(PvlKeyword("MaximumLatitude", toString(ui.GetDouble("MAXLAT"))), Pvl::Replace);
  }

  if(ui.WasEntered("MINLON") && !ui.GetBoolean("MATCHMAP")) {
    userMappingGrp.addKeyword(PvlKeyword("MinimumLongitude", toString(ui.GetDouble("MINLON"))), Pvl::Replace);
  }

  if(ui.WasEntered("MAXLON") && !ui.GetBoolean("MATCHMAP")) {
    userMappingGrp.addKeyword(PvlKeyword("MaximumLongitude", toString(ui.GetDouble("MAXLON"))), Pvl::Replace);
  }

  /**
   * If the user is changing from positive east to positive west, or vice-versa, the output minimum is really
   * the input maximum. However, the user mapping group must be left unaffected (an input minimum must be the
   * output minimum). To accomplish this, we swap the minimums/maximums in the output group ahead of time. This
   * causes the minimums and maximums to correlate to the output minimums and maximums. That way when we copy
   * the user mapping group into the output group a mimimum overrides a minimum and a maximum overrides a maximum.
   */
  bool sameDirection = true;
  if(userMappingGrp.hasKeyword("LongitudeDirection")) {
    if(((QString)userMappingGrp["LongitudeDirection"]).compare(fromMappingGrp["LongitudeDirection"]) != 0) {
      sameDirection = false;
    }
  }

  // Since the out mapping group came from the from mapping group, which came from a valid cube,
  // we can assume both min/max lon exists if min longitude exists.
  if(!sameDirection && outMappingGrp.hasKeyword("MinimumLongitude")) {
    double minLon = outMappingGrp["MinimumLongitude"];
    double maxLon = outMappingGrp["MaximumLongitude"];

    outMappingGrp["MaximumLongitude"] = toString(minLon);
    outMappingGrp["MinimumLongitude"] = toString(maxLon);
  }

  if(ui.GetString("PIXRES").compare("FROM") == 0 && !ui.GetBoolean("MATCHMAP")) {
    // Resolution will be in fromMappingGrp and outMappingGrp at this time
    //   delete from user mapping grp
    if(userMappingGrp.hasKeyword("Scale")) {
      userMappingGrp.deleteKeyword("Scale");
    }

    if(userMappingGrp.hasKeyword("PixelResolution")) {
      userMappingGrp.deleteKeyword("PixelResolution");
    }
  }
  else if(ui.GetString("PIXRES").compare("MAP") == 0 || ui.GetBoolean("MATCHMAP")) {
    // Resolution will be in userMappingGrp - delete all others
    if(outMappingGrp.hasKeyword("Scale")) {
      outMappingGrp.deleteKeyword("Scale");
    }

    if(outMappingGrp.hasKeyword("PixelResolution")) {
      outMappingGrp.deleteKeyword("PixelResolution");
    }

    if(fromMappingGrp.hasKeyword("Scale"));
    {
      fromMappingGrp.deleteKeyword("Scale");
    }

    if(fromMappingGrp.hasKeyword("PixelResolution")) {
      fromMappingGrp.deleteKeyword("PixelResolution");
    }
  }
  else if(ui.GetString("PIXRES").compare("MPP") == 0) {
    // Resolution specified - delete all and add to outMappingGrp
    if(outMappingGrp.hasKeyword("Scale")) {
      outMappingGrp.deleteKeyword("Scale");
    }

    if(outMappingGrp.hasKeyword("PixelResolution")) {
      outMappingGrp.deleteKeyword("PixelResolution");
    }

    if(fromMappingGrp.hasKeyword("Scale")) {
      fromMappingGrp.deleteKeyword("Scale");
    }

    if(fromMappingGrp.hasKeyword("PixelResolution")) {
      fromMappingGrp.deleteKeyword("PixelResolution");
    }

    if(userMappingGrp.hasKeyword("Scale")) {
      userMappingGrp.deleteKeyword("Scale");
    }

    if(userMappingGrp.hasKeyword("PixelResolution")) {
      userMappingGrp.deleteKeyword("PixelResolution");
    }

    outMappingGrp.addKeyword(PvlKeyword("PixelResolution", toString(ui.GetDouble("RESOLUTION")), "meters/pixel"), Pvl::Replace);
  }
  else if(ui.GetString("PIXRES").compare("PPD") == 0) {
    // Resolution specified - delete all and add to outMappingGrp
    if(outMappingGrp.hasKeyword("Scale")) {
      outMappingGrp.deleteKeyword("Scale");
    }

    if(outMappingGrp.hasKeyword("PixelResolution")) {
      outMappingGrp.deleteKeyword("PixelResolution");
    }

    if(fromMappingGrp.hasKeyword("Scale")) {
      fromMappingGrp.deleteKeyword("Scale");
    }

    if(fromMappingGrp.hasKeyword("PixelResolution")) {
      fromMappingGrp.deleteKeyword("PixelResolution");
    }

    if(userMappingGrp.hasKeyword("Scale")) {
      userMappingGrp.deleteKeyword("Scale");
    }

    if(userMappingGrp.hasKeyword("PixelResolution")) {
      userMappingGrp.deleteKeyword("PixelResolution");
    }

    outMappingGrp.addKeyword(PvlKeyword("Scale", toString(ui.GetDouble("RESOLUTION")), "pixels/degree"), Pvl::Replace);
  }

  // Rotation will NOT Propagate
  if(outMappingGrp.hasKeyword("Rotation")) {
    outMappingGrp.deleteKeyword("Rotation");
  }


  /**
   * The user specified map template file overrides what ever is in the
   * cube's mapping group.
   */
  for(int keyword = 0; keyword < userMappingGrp.keywords(); keyword ++) {
    outMappingGrp.addKeyword(userMappingGrp[keyword], Pvl::Replace);
  }

  /**
   * Now, we have to deal with unit conversions. We convert only if the following are true:
   *   1) We used values from the input cube
   *   2) The values are longitudes or latitudes
   *   3) The map file or user-specified information uses a different measurement system than
   *        the input cube for said values.
   *
   * The data is corrected for:
   *   1) Positive east/positive west
   *   2) Longitude domain
   *   3) planetographic/planetocentric.
   */

  // First, the longitude direction
  if(!sameDirection) {
    PvlGroup longitudes = inproj->MappingLongitudes();

    for(int index = 0; index < longitudes.keywords(); index ++) {
      if(!userMappingGrp.hasKeyword(longitudes[index].name())) {
        // use the from domain because that's where our values are coming from
        if(((QString)userMappingGrp["LongitudeDirection"]).compare("PositiveEast") == 0) {
          outMappingGrp[longitudes[index].name()] = toString(
            TProjection::ToPositiveEast(outMappingGrp[longitudes[index].name()],
                                        outMappingGrp["LongitudeDomain"]));
        }
        else {
          outMappingGrp[longitudes[index].name()] = toString(
              TProjection::ToPositiveWest(outMappingGrp[longitudes[index].name()],
                                          outMappingGrp["LongitudeDomain"]));
        }
      }
    }
  }

  // Second, longitude domain
  if(userMappingGrp.hasKeyword("LongitudeDomain")) { // user set a new domain?
    if((int)userMappingGrp["LongitudeDomain"] != (int)fromMappingGrp["LongitudeDomain"]) { // new domain different?
      PvlGroup longitudes = inproj->MappingLongitudes();

      for(int index = 0; index < longitudes.keywords(); index ++) {
        if(!userMappingGrp.hasKeyword(longitudes[index].name())) {
          if((int)userMappingGrp["LongitudeDomain"] == 180) {
            outMappingGrp[longitudes[index].name()] = toString(
                TProjection::To180Domain(outMappingGrp[longitudes[index].name()]));
          }
          else {
            outMappingGrp[longitudes[index].name()] = toString(
                TProjection::To360Domain(outMappingGrp[longitudes[index].name()]));
          }
        }
      }

    }
  }

  // Third, planetographic/planetocentric
  if(userMappingGrp.hasKeyword("LatitudeType")) { // user set a new domain?
    if(((QString)userMappingGrp["LatitudeType"]).compare(fromMappingGrp["LatitudeType"]) != 0) { // new lat type different?

      PvlGroup latitudes = inproj->MappingLatitudes();

      for(int index = 0; index < latitudes.keywords(); index ++) {
        if(!userMappingGrp.hasKeyword(latitudes[index].name())) {
          if(((QString)userMappingGrp["LatitudeType"]).compare("Planetographic") == 0) {
            outMappingGrp[latitudes[index].name()] = toString(TProjection::ToPlanetographic(
                  (double)fromMappingGrp[latitudes[index].name()],
                  (double)fromMappingGrp["EquatorialRadius"],
                  (double)fromMappingGrp["PolarRadius"]));
          }
          else {
            outMappingGrp[latitudes[index].name()] = toString(TProjection::ToPlanetocentric(
                  (double)fromMappingGrp[latitudes[index].name()],
                  (double)fromMappingGrp["EquatorialRadius"],
                  (double)fromMappingGrp["PolarRadius"]));
          }
        }
      }

    }
  }

  // Try a couple equivalent longitudes to fix the ordering of min,max for border cases
  if ((double)outMappingGrp["MinimumLongitude"] >=
      (double)outMappingGrp["MaximumLongitude"]) {

    if ((QString)outMappingGrp["MinimumLongitude"] == "180.0" &&
        (int)userMappingGrp["LongitudeDomain"] == 180)
      outMappingGrp["MinimumLongitude"] = "-180";

    if ((QString)outMappingGrp["MaximumLongitude"] == "-180.0" &&
        (int)userMappingGrp["LongitudeDomain"] == 180)
      outMappingGrp["MaximumLongitude"] = "180";

    if ((QString)outMappingGrp["MinimumLongitude"] == "360.0" &&
        (int)userMappingGrp["LongitudeDomain"] == 360)
      outMappingGrp["MinimumLongitude"] = "0";

    if ((QString)outMappingGrp["MaximumLongitude"] == "0.0" &&
        (int)userMappingGrp["LongitudeDomain"] == 360)
      outMappingGrp["MaximumLongitude"] = "360";
  }

  // If MinLon/MaxLon out of order, we weren't able to calculate the correct values
  if((double)outMappingGrp["MinimumLongitude"] >= (double)outMappingGrp["MaximumLongitude"]) {
    if(!ui.WasEntered("MINLON") || !ui.WasEntered("MAXLON")) {
      QString msg = "Unable to determine the correct [MinimumLongitude,MaximumLongitude].";
      msg += " Please specify these values in the [MINLON,MAXLON] parameters";
      throw IException(IException::Unknown, msg, _FILEINFO_);
    }
  }

  int samples, lines;
  Pvl mapData;
  // Copy to preserve cube labels so we can match cube size
  if(userPvl.hasObject("IsisCube")) {
    mapData = userPvl;
    mapData.findObject("IsisCube").deleteGroup("Mapping");
    mapData.findObject("IsisCube").addGroup(outMappingGrp);
  }
  else {
    mapData.addGroup(outMappingGrp);
  }

  // *NOTE: The UpperLeftX,UpperLeftY keywords will not be used in the CreateForCube
  //   method, and they will instead be recalculated. This is correct.
  TProjection *outproj = (TProjection *) ProjectionFactory::CreateForCube(mapData, samples, lines,
                        ui.GetBoolean("MATCHMAP"));

  // Set up the transform object which will simply map
  // output line/samps -> output lat/lons -> input line/samps
  Transform *transform = new map2map(icube->sampleCount(),
                                     icube->lineCount(),
                                     (TProjection *) icube->projection(),
                                     samples,
                                     lines,
                                     outproj,
                                     ui.GetBoolean("TRIM"));

  // Allocate the output cube and add the mapping labels
  Cube *ocube = p.SetOutputCube("TO", transform->OutputSamples(),
                                transform->OutputLines(),
                                icube->bandCount());

  PvlGroup cleanOutGrp = outproj->Mapping();

  // ProjectionFactory::CreateForCube updated mapData to have the correct
  //   upperleftcornerx, upperleftcornery, scale and resolution. Use these
  //   updated numbers.
  cleanOutGrp.addKeyword(mapData.findGroup("Mapping", Pvl::Traverse)["UpperLeftCornerX"], Pvl::Replace);
  cleanOutGrp.addKeyword(mapData.findGroup("Mapping", Pvl::Traverse)["UpperLeftCornerY"], Pvl::Replace);
  cleanOutGrp.addKeyword(mapData.findGroup("Mapping", Pvl::Traverse)["Scale"], Pvl::Replace);
  cleanOutGrp.addKeyword(mapData.findGroup("Mapping", Pvl::Traverse)["PixelResolution"], Pvl::Replace);

  ocube->putGroup(cleanOutGrp);

  // Set up the interpolator
  Interpolator *interp;
  if(ui.GetString("INTERP") == "NEARESTNEIGHBOR") {
    interp = new Interpolator(Interpolator::NearestNeighborType);
  }
  else if(ui.GetString("INTERP") == "BILINEAR") {
    interp = new Interpolator(Interpolator::BiLinearType);
  }
  else if(ui.GetString("INTERP") == "CUBICCONVOLUTION") {
    interp = new Interpolator(Interpolator::CubicConvolutionType);
  }
  else {
    QString msg = "Unknow value for INTERP [" + ui.GetString("INTERP") + "]";
    throw IException(IException::Programmer, msg, _FILEINFO_);
  }

  // Warp the cube
  p.StartProcess(*transform, *interp);
  p.EndProcess();

  Application::Log(cleanOutGrp);

  // Cleanup
  delete transform;
  delete interp;
}
Example #5
0
void IsisMain() {
  // We will be warping a cube
  ProcessRubberSheet p;

  // Get the map projection file provided by the user
  UserInterface &ui = Application::GetUserInterface();
  Pvl userMap;
  userMap.read(ui.GetFileName("MAP"));
  PvlGroup &userGrp = userMap.findGroup("Mapping", Pvl::Traverse);

  // Open the input cube and get the camera
  icube = p.SetInputCube("FROM");
  incam = icube->camera();

  // Make sure it is not the sky
  if(incam->target()->isSky()) {
    QString msg = "The image [" + ui.GetFileName("FROM") +
                  "] is targeting the sky, use skymap instead.";
    throw IException(IException::User, msg, _FILEINFO_);
  }

  // Get the mapping group from the camera
  Pvl camMap;
  incam->basicRingMapping(camMap);
  PvlGroup &camGrp = camMap.findGroup("Mapping");

  // Make the target info match the user mapfile
  double minrad, maxrad, minaz, maxaz;
  incam->ringRange(minrad, maxrad, minaz, maxaz, userMap);
  camGrp.addKeyword(PvlKeyword("MinimumRingRadius", toString(minrad)), Pvl::Replace);
  camGrp.addKeyword(PvlKeyword("MaximumRingRadius", toString(maxrad)), Pvl::Replace);
  camGrp.addKeyword(PvlKeyword("MinimumRingLongitude", toString(minaz)), Pvl::Replace);
  camGrp.addKeyword(PvlKeyword("MaximumRingLongitude", toString(maxaz)), Pvl::Replace);

  // We want to delete the keywords we just added if the user wants the range
  // out of the mapfile, otherwise they will replace any keywords not in the
  // mapfile
  if (ui.GetString("DEFAULTRANGE") == "MAP" || ui.GetBoolean("MATCHMAP")) {
    camGrp.deleteKeyword("MinimumRingRadius");
    camGrp.deleteKeyword("MaximumRingRadius");
    camGrp.deleteKeyword("MinimumRingLongitude");
    camGrp.deleteKeyword("MaximumRingLongitude");
  }
  // Otherwise, remove the keywords from the map file so the camera keywords
  // will be propogated correctly
  else {
    while(userGrp.hasKeyword("MinimumRingRadius")) {
      userGrp.deleteKeyword("MinimumRingRadius");
    }
    while(userGrp.hasKeyword("MinimumRingLongitude")) {
      userGrp.deleteKeyword("MinimumRingLongitude");
    }
    while(userGrp.hasKeyword("MaximumRingRadius")) {
      userGrp.deleteKeyword("MaximumRingRadius");
    }
    while(userGrp.hasKeyword("MaximumRingLongitude")) {
      userGrp.deleteKeyword("MaximumRingLongitude");
    }
  }

  // If the user decided to enter a ground range then override
  if(ui.WasEntered("MINRINGLON")) {
    userGrp.addKeyword(PvlKeyword("MinimumRingLongitude",
                                  toString(ui.GetDouble("MINRINGLON"))), Pvl::Replace);
  }

  if(ui.WasEntered("MAXRINGLON")) {
    userGrp.addKeyword(PvlKeyword("MaximumRingLongitude",
                                  toString(ui.GetDouble("MAXRINGLON"))), Pvl::Replace);
  }

  if(ui.WasEntered("MINRINGRAD")) {
    userGrp.addKeyword(PvlKeyword("MinimumRingRadius",
                                  toString(ui.GetDouble("MINRINGRAD"))), Pvl::Replace);
  }

  if(ui.WasEntered("MAXRINGRAD")) {
    userGrp.addKeyword(PvlKeyword("MaximumRingRadius",
                                  toString(ui.GetDouble("MAXRINGRAD"))), Pvl::Replace);
  }

  // If they want the res. from the mapfile, delete it from the camera so
  // nothing gets overridden
  if(ui.GetString("PIXRES") == "MAP" || ui.GetBoolean("MATCHMAP")) {
    camGrp.deleteKeyword("PixelResolution");
  }
  // Otherwise, delete any resolution keywords from the mapfile so the camera
  // info is propagated over
  else if(ui.GetString("PIXRES") == "CAMERA") {
    if(userGrp.hasKeyword("Scale")) {
      userGrp.deleteKeyword("Scale");
    }
    if(userGrp.hasKeyword("PixelResolution")) {
      userGrp.deleteKeyword("PixelResolution");
    }
  }

  // Copy any defaults that are not in the user map from the camera map file
  for(int k = 0; k < camGrp.keywords(); k++) {
    if(!userGrp.hasKeyword(camGrp[k].name())) {
      userGrp += camGrp[k];
    }
  }

  // If the user is not matching the map file and entered a resolution, then reset this value
  if (!ui.GetBoolean("MATCHMAP")) {
    if(ui.GetString("PIXRES") == "MPP") {
      userGrp.addKeyword(PvlKeyword("PixelResolution",
                                    toString(ui.GetDouble("RESOLUTION"))),
                         Pvl::Replace);
      if(userGrp.hasKeyword("Scale")) {
        userGrp.deleteKeyword("Scale");
      }
    }
    else if(ui.GetString("PIXRES") == "PPD") {
      userGrp.addKeyword(PvlKeyword("Scale",
                                    toString(ui.GetDouble("RESOLUTION"))),
                         Pvl::Replace);
      if(userGrp.hasKeyword("PixelResolution")) {
        userGrp.deleteKeyword("PixelResolution");
      }
    }
  }

  // See if the user want us to handle the azimuth (ring longitude) seam
  // NOTE: For ringscam2map, if the user chooses to MATCHMAP, then we treat
  // the parameter ringlonseam as if it were set to "continue" (i.e. do nothing)
  if (!ui.GetBoolean("MATCHMAP")) {
    if((ui.GetString("DEFAULTRANGE") == "CAMERA" || ui.GetString("DEFAULTRANGE") == "MINIMIZE")) {
      //TODO This camera method will need attention for rings***** Solution:  just call ringRange directly
      // if(incam->IntersectsLongitudeDomain(userMap)) {
      if (incam->ringRange(minrad, maxrad, minaz, maxaz, userMap)) {
        if(ui.GetString("RINGLONSEAM") == "AUTO") {
          if((int) userGrp["RingLongitudeDomain"] == 360) {
            userGrp.addKeyword(PvlKeyword("RingLongitudeDomain", "180"),
                               Pvl::Replace);
            if(incam->ringRange(minrad, maxrad, minaz, maxaz, userMap)) {
              // Its looks like a global image so switch back to the
              // users preference
              userGrp.addKeyword(PvlKeyword("RingLongitudeDomain", "360"),
                                 Pvl::Replace);
            }
          }
          else {
            userGrp.addKeyword(PvlKeyword("RingLongitudeDomain", "360"),
                               Pvl::Replace);
            if(incam->ringRange(minrad, maxrad, minaz, maxaz, userMap)) {
              // Its looks like a global image so switch back to the
              // users preference
              userGrp.addKeyword(PvlKeyword("RingLongitudeDomain", "180"),
                                 Pvl::Replace);
            }
          }
          // Make the target info match the new azimuth (ring longitude) domain Use radius for where
          // camera expects latitude & azimuth (ring longitude) where the camera expects longitude
          double minrad, maxrad, minaz, maxaz;
          incam->ringRange(minrad, maxrad, minaz, maxaz, userMap);
          if(!ui.WasEntered("MINRINGRAD")) {
            userGrp.addKeyword(PvlKeyword("MinimumRingRadius", toString(minrad)), Pvl::Replace);
          }
          if(!ui.WasEntered("MAXRINGRAD")) {
            userGrp.addKeyword(PvlKeyword("MaximumRingRadius", toString(maxrad)), Pvl::Replace);
          }
          if(!ui.WasEntered("MINRINGLON")) {
            userGrp.addKeyword(PvlKeyword("MinimumRingLongitude", toString(minaz)), Pvl::Replace);
          }
          if(!ui.WasEntered("MAXRINGLON")) {
            userGrp.addKeyword(PvlKeyword("MaximumRingLongitude", toString(maxaz)), Pvl::Replace);
          }
        }
        else if(ui.GetString("RINGLONSEAM") == "ERROR") {
          QString msg = "The image [" + ui.GetFileName("FROM") + "] crosses the " +
                       "ring longitude seam";
          throw IException(IException::User, msg, _FILEINFO_);
        }
      }
    }
  }

  // Use the updated label to create the output projection
  int samples, lines;
  RingPlaneProjection *outmap;
  bool trim;

  // Determine the image size
  if (ui.GetBoolean("MATCHMAP") || ui.GetString("DEFAULTRANGE") == "MAP") { // DEFAULTRANGE = MAP or MATCHMAP=true
    outmap =  (RingPlaneProjection *) ProjectionFactory::RingsCreateForCube(userMap, samples, lines,
                                                                         ui.GetBoolean("MATCHMAP"));//??? if DR=map, this is always false???
    trim = ui.GetBoolean("TRIM"); // trim allowed for defaultrange=map
  }
  else if(ui.GetString("DEFAULTRANGE") == "MINIMIZE") {
    outmap = (RingPlaneProjection *) ProjectionFactory::RingsCreateForCube(userMap, samples, lines, 
                                                                           *incam);
    trim = false;
  }
  else { // if(ui.GetString("DEFAULTRANGE") == "CAMERA") {
    outmap =  (RingPlaneProjection *) ProjectionFactory::RingsCreateForCube(userMap, samples, lines, 
                                                                            false);
    trim = ui.GetBoolean("TRIM");
  }

  // Output the mapping group used to the Gui session log
  PvlGroup cleanMapping = outmap->Mapping();
  Application::GuiLog(cleanMapping);

  // Allocate the output cube and add the mapping labels
  Cube *ocube = p.SetOutputCube("TO", samples, lines, icube->bandCount());

  ocube->putGroup(cleanMapping);

  // Set up the interpolator
  Interpolator *interp = NULL;
  if(ui.GetString("INTERP") == "NEARESTNEIGHBOR") {
    interp = new Interpolator(Interpolator::NearestNeighborType);
  }
  else if(ui.GetString("INTERP") == "BILINEAR") {
    interp = new Interpolator(Interpolator::BiLinearType);
  }
  else {//if(ui.GetString("INTERP") == "CUBICCONVOLUTION") {
    interp = new Interpolator(Interpolator::CubicConvolutionType);
  }

  // See if we need to deal with band dependent camera models
  if(!incam->IsBandIndependent()) {
    p.BandChange(BandChange);
  }

  //  See if center of input image projects.  If it does, force tile
  //  containing this center to be processed in ProcessRubberSheet.
  //  TODO:  WEIRD ... why is this needed ... Talk to Tracie or JAA???
  double centerSamp = icube->sampleCount() / 2.;
  double centerLine = icube->lineCount() / 2.;
  if(incam->SetImage(centerSamp, centerLine)) {
    // Force rings data into Isis by returning ring radius for latitude
    // and azimuth (ring longitude) for longitude
    if(outmap->SetUniversalGround(incam->LocalRadius().meters(),
                                  incam->UniversalLongitude())) {
      p.ForceTile(outmap->WorldX(), outmap->WorldY());
    }
  }
  // Create an alpha cube group for the output cube
  if(!ocube->hasGroup("AlphaCube")) {
    PvlGroup alpha("AlphaCube");
    alpha += PvlKeyword("AlphaSamples", toString(icube->sampleCount()));
    alpha += PvlKeyword("AlphaLines", toString(icube->lineCount()));
    alpha += PvlKeyword("AlphaStartingSample", toString(0.5));
    alpha += PvlKeyword("AlphaStartingLine", toString(0.5));
    alpha += PvlKeyword("AlphaEndingSample", toString(icube->sampleCount() + 0.5));
    alpha += PvlKeyword("AlphaEndingLine", toString(icube->lineCount() + 0.5));
    alpha += PvlKeyword("BetaSamples", toString(icube->sampleCount()));
    alpha += PvlKeyword("BetaLines", toString(icube->lineCount()));
    ocube->putGroup(alpha);
  }

  // We will need a transform class
  Transform *transform = 0;
  
  // Okay we need to decide how to apply the rubbersheeting for the transform
  // Does the user want to define how it is done?
  if (ui.GetString("WARPALGORITHM") == "FORWARDPATCH") {
    transform = new ringscam2mapForward(icube->sampleCount(),
                                   icube->lineCount(), incam, samples,lines,
                                   outmap, trim);
                                   // (Planar*)outmap, trim);

    int patchSize = ui.GetInteger("PATCHSIZE");
    if (patchSize <= 1) patchSize = 3; // Make the patchsize reasonable
    p.setPatchParameters(1, 1, patchSize, patchSize, 
                         patchSize-1, patchSize-1);
    p.processPatchTransform(*transform, *interp);
  }

  else if (ui.GetString("WARPALGORITHM") == "REVERSEPATCH") {
    transform = new ringscam2mapReverse(icube->sampleCount(),
                                   icube->lineCount(), incam, samples,lines,
                                   outmap, trim);
                                   // (Planar*)outmap, trim);

    int patchSize = ui.GetInteger("PATCHSIZE");
    int minPatchSize = 4;
    if (patchSize < minPatchSize) minPatchSize = patchSize;
    p.SetTiling(patchSize, minPatchSize);
    p.StartProcess(*transform, *interp);
  }

  // The user didn't want to override the program smarts.
  // Handle framing cameras.  Always process using the backward
  // driven system (tfile).
  else if (incam->GetCameraType() == Camera::Framing) {
    transform = new ringscam2mapReverse(icube->sampleCount(),
                                   icube->lineCount(), incam, samples,lines,
                                   outmap, trim);
                                   // (Planar*)outmap, trim);
    p.SetTiling(4, 4);
    p.StartProcess(*transform, *interp);
  }

  // The user didn't want to override the program smarts.
  // Handle linescan cameras.  Always process using the forward
  // driven patch option. Faster and we get better orthorectification
  // 
  // TODO:  For now use the default patch size.  Need to modify
  // to determine patch size based on 1) if the limb is in the file
  // or 2) if the DTM is much coarser than the image
  else if (incam->GetCameraType() == Camera::LineScan) {
    transform = new ringscam2mapForward(icube->sampleCount(),
                                   icube->lineCount(), incam, samples,lines,
                                   outmap, trim);
                                   // (Planar*)outmap, trim);

    p.processPatchTransform(*transform, *interp);
  }

  // The user didn't want to override the program smarts.
  // Handle pushframe cameras.  Always process using the forward driven patch 
  // option.  It is much faster than the tfile method.  We will need to 
  // determine patch sizes based on the size of the push frame.
  // 
  // TODO: What if the user has run crop, enlarge, or shrink on the push
  // frame cube.  Things probably won't work unless they do it just right
  // TODO: What about the THEMIS VIS Camera.  Will tall narrow (128x4) patches
  // work okay?
  else if (incam->GetCameraType() == Camera::PushFrame) {
    transform = new ringscam2mapForward(icube->sampleCount(),
                                   icube->lineCount(), incam, samples,lines,
                                   outmap, trim);
                                   // (Planar*)outmap, trim);

    // Get the frame height
    PushFrameCameraDetectorMap *dmap = (PushFrameCameraDetectorMap *) incam->DetectorMap();
    int frameSize = dmap->frameletHeight() / dmap->LineScaleFactor();

    // Check for even/odd cube to determine starting line
    PvlGroup &instGrp = icube->label()->findGroup("Instrument", Pvl::Traverse);
    int startLine = 1;

    // Get the alpha cube group in case they cropped the image
    AlphaCube acube(*icube);
    double betaLine = acube.AlphaLine(1.0);
    if (fabs(betaLine - 1.0) > 0.0000000001) {
      if (fabs(betaLine - (int) betaLine) > 0.00001) {
        string msg = "Input file is a pushframe camera cropped at a ";
        msg += "fractional pixel.  Can not project"; 
        throw IException(IException::User, msg, _FILEINFO_);
      }
      int offset = (((int) (betaLine + 0.5)) - 1) % frameSize;
      startLine -= offset;
    }

    if (((QString)instGrp["Framelets"]).toUpper() == "EVEN") {
      startLine += frameSize;
    }

    p.setPatchParameters(1, startLine, 5, frameSize,
                         4, frameSize * 2);

    p.processPatchTransform(*transform, *interp);
  }

  // The user didn't want to override the program smarts.  The other camera 
  // types have not be analyized.  This includes Radar and Point.  Continue to
  // use the reverse geom option with the default tiling hints
  else {
    transform = new ringscam2mapReverse(icube->sampleCount(),
                                   icube->lineCount(), incam, samples,lines,
                                   outmap, trim);
                                   // (Planar*)outmap, trim);

    int tileStart, tileEnd;
    incam->GetGeometricTilingHint(tileStart, tileEnd);
    p.SetTiling(tileStart, tileEnd);

    p.StartProcess(*transform, *interp);
  }

  // Wrap up the warping process 
  p.EndProcess();

  // add mapping to print.prt
  Application::Log(cleanMapping);

  // Cleanup
  delete outmap;
  delete transform;
  delete interp;
}
Example #6
0
void IsisMain() {
  //Create a process to create the input cubes
  Process p;
  //Create the input cubes, matching sample/lines
  Cube *inCube = p.SetInputCube ("FROM");
  Cube *latCube = p.SetInputCube("LATCUB", SpatialMatch);
  Cube *lonCube = p.SetInputCube("LONCUB", SpatialMatch);

  //A 1x1 brick to read in the latitude and longitude DN values from
  //the specified cubes
  Brick latBrick(1,1,1, latCube->PixelType());
  Brick lonBrick(1,1,1, lonCube->PixelType());

  UserInterface &ui = Application::GetUserInterface();

  //Set the sample and line increments
  int sinc = (int)(inCube->Samples() * 0.10);
  if(ui.WasEntered("SINC")) {
    sinc = ui.GetInteger("SINC");
  }

  int linc = (int)(inCube->Lines() * 0.10);
  if(ui.WasEntered("LINC")) {
    linc = ui.GetInteger("LINC");
  }

  //Set the degree of the polynomial to use in our functions
  int degree = ui.GetInteger("DEGREE");

  //We are using a polynomial with two variables
  PolynomialBivariate sampFunct(degree); 
  PolynomialBivariate lineFunct(degree);

  //We will be solving the function using the least squares method
  LeastSquares sampSol(sampFunct);
  LeastSquares lineSol(lineFunct);

  //Setup the variables for solving the stereographic projection
  //x = cos(latitude) * sin(longitude - lon_center)
  //y = cos(lat_center) * sin(latitude) - sin(lat_center) * cos(latitude) * cos(longitude - lon_center)

  //Get the center lat and long from the input cubes
  double lat_center = latCube->Statistics()->Average() * PI/180.0;
  double lon_center = lonCube->Statistics()->Average() * PI/180.0;


  /**
   * Loop through lines and samples projecting the latitude and longitude at those
   * points to stereographic x and y and adding these points to the LeastSquares 
   * matrix. 
   */
  for(int i = 1; i <= inCube->Lines(); i+= linc) {
    for(int j = 1; j <= inCube->Samples(); j+= sinc) {
      latBrick.SetBasePosition(j, i, 1);
      latCube->Read(latBrick);
      if(IsSpecial(latBrick.at(0))) continue;
      double lat = latBrick.at(0) * PI/180.0;
      lonBrick.SetBasePosition(j, i, 1);
      lonCube->Read(lonBrick);
      if(IsSpecial(lonBrick.at(0))) continue;
      double lon = lonBrick.at(0) * PI/180.0;

      //Project lat and lon to x and y using a stereographic projection
      double k = 2/(1 + sin(lat_center) * sin(lat) + cos(lat_center)*cos(lat)*cos(lon - lon_center));
      double x = k * cos(lat) * sin(lon - lon_center);
      double y = k * (cos(lat_center) * sin(lat)) - (sin(lat_center) * cos(lat) * cos(lon - lon_center));

      //Add x and y to the least squares matrix
      vector<double> data;
      data.push_back(x);
      data.push_back(y);
      sampSol.AddKnown(data, j);
      lineSol.AddKnown(data, i);

      //If the sample increment goes past the last sample in the line, we want to
      //always read the last sample..
      if(j != inCube->Samples() && j + sinc > inCube->Samples()) {
        j = inCube->Samples() - sinc;
      }
    }
    //If the line increment goes past the last line in the cube, we want to
    //always read the last line..
    if(i != inCube->Lines() && i + linc > inCube->Lines()) {    
      i = inCube->Lines() - linc;
    }
  }

  //Solve the least squares functions using QR Decomposition
  sampSol.Solve(LeastSquares::QRD);
  lineSol.Solve(LeastSquares::QRD);

  //If the user wants to save the residuals to a file, create a file and write
  //the column titles to it.
  TextFile oFile;
  if(ui.WasEntered("RESIDUALS")) {
    oFile.Open(ui.GetFilename("RESIDUALS"), "overwrite");
    oFile.PutLine("Sample,\tLine,\tX,\tY,\tSample Error,\tLine Error\n");
  }

  //Gather the statistics for the residuals from the least squares solutions
  Statistics sampErr;
  Statistics lineErr;
  vector<double> sampResiduals = sampSol.Residuals();
  vector<double> lineResiduals = lineSol.Residuals();
  for(int i = 0; i < (int)sampResiduals.size(); i++) {
    sampErr.AddData(sampResiduals[i]);
    lineErr.AddData(lineResiduals[i]);
  }

  //If a residuals file was specified, write the previous data, and the errors to the file.
  if(ui.WasEntered("RESIDUALS")) {
    for(int i = 0; i < sampSol.Rows(); i++) {
      vector<double> data = sampSol.GetInput(i);
      iString tmp = "";
      tmp += iString(sampSol.GetExpected(i));
      tmp += ",\t";
      tmp += iString(lineSol.GetExpected(i));
      tmp += ",\t";
      tmp += iString(data[0]);
      tmp += ",\t";
      tmp += iString(data[1]);
      tmp += ",\t";
      tmp += iString(sampResiduals[i]);
      tmp += ",\t";
      tmp += iString(lineResiduals[i]);
      oFile.PutLine(tmp + "\n");
    }
  }
  oFile.Close();

  //Records the error to the log
  PvlGroup error( "Error" );
  error += PvlKeyword( "Degree", degree );
  error += PvlKeyword( "NumberOfPoints", (int)sampResiduals.size() );
  error += PvlKeyword( "SampleMinimumError", sampErr.Minimum() );
  error += PvlKeyword( "SampleAverageError", sampErr.Average() );
  error += PvlKeyword( "SampleMaximumError", sampErr.Maximum() );
  error += PvlKeyword( "SampleStdDeviationError", sampErr.StandardDeviation() );
  error += PvlKeyword( "LineMinimumError", lineErr.Minimum() );
  error += PvlKeyword( "LineAverageError", lineErr.Average() );
  error += PvlKeyword( "LineMaximumError", lineErr.Maximum() );
  error += PvlKeyword( "LineStdDeviationError", lineErr.StandardDeviation() );
  Application::Log( error );

  //Close the input cubes for cleanup
  p.EndProcess();

  //If we want to warp the image, then continue, otherwise return
  if(!ui.GetBoolean("NOWARP")) {
    //Creates the mapping group
    Pvl mapFile;
    mapFile.Read(ui.GetFilename("MAP"));
    PvlGroup &mapGrp = mapFile.FindGroup("Mapping",Pvl::Traverse);

    //Reopen the lat and long cubes
    latCube = new Cube();
    latCube->SetVirtualBands(ui.GetInputAttribute("LATCUB").Bands());
    latCube->Open(ui.GetFilename("LATCUB"));

    lonCube = new Cube();
    lonCube->SetVirtualBands(ui.GetInputAttribute("LONCUB").Bands());
    lonCube->Open(ui.GetFilename("LONCUB"));

    PvlKeyword targetName;

    //If the user entered the target name
    if(ui.WasEntered("TARGET")) {
      targetName = PvlKeyword("TargetName", ui.GetString("TARGET"));
    }
    //Else read the target name from the input cube
    else {
      Pvl fromFile;
      fromFile.Read(ui.GetFilename("FROM"));
      targetName = fromFile.FindKeyword("TargetName", Pvl::Traverse);
    }

    mapGrp.AddKeyword(targetName, Pvl::Replace);

    PvlKeyword equRadius;
    PvlKeyword polRadius;


    //If the user entered the equatorial and polar radii
    if(ui.WasEntered("EQURADIUS") && ui.WasEntered("POLRADIUS")) {
      equRadius = PvlKeyword("EquatorialRadius", ui.GetDouble("EQURADIUS"));
      polRadius = PvlKeyword("PolarRadius", ui.GetDouble("POLRADIUS"));
    }
    //Else read them from the pck
    else {
      Filename pckFile("$base/kernels/pck/pck?????.tpc");
      pckFile.HighestVersion();

      string pckFilename = pckFile.Expanded();

      furnsh_c(pckFilename.c_str());

      string target = targetName[0];
      SpiceInt code;
      SpiceBoolean found;

      bodn2c_c (target.c_str(), &code, &found);

      if (!found) {
        string msg = "Could not convert Target [" + target +
                     "] to NAIF code";
        throw Isis::iException::Message(Isis::iException::Io,msg,_FILEINFO_);
      }

      SpiceInt n;
      SpiceDouble radii[3];

      bodvar_c(code,"RADII",&n,radii);

      equRadius = PvlKeyword("EquatorialRadius", radii[0] * 1000);
      polRadius = PvlKeyword("PolarRadius", radii[2] * 1000);
    }

    mapGrp.AddKeyword(equRadius, Pvl::Replace);
    mapGrp.AddKeyword(polRadius, Pvl::Replace);


    //If the latitude type is not in the mapping group, copy it from the input
    if(!mapGrp.HasKeyword("LatitudeType")) {
      if(ui.GetString("LATTYPE") == "PLANETOCENTRIC") {
        mapGrp.AddKeyword(PvlKeyword("LatitudeType","Planetocentric"), Pvl::Replace);
      }
      else {
        mapGrp.AddKeyword(PvlKeyword("LatitudeType","Planetographic"), Pvl::Replace);
      }
    }

    //If the longitude direction is not in the mapping group, copy it from the input
    if(!mapGrp.HasKeyword("LongitudeDirection")) {
      if(ui.GetString("LONDIR") == "POSITIVEEAST") {
        mapGrp.AddKeyword(PvlKeyword("LongitudeDirection","PositiveEast"), Pvl::Replace);
      }
      else {
        mapGrp.AddKeyword(PvlKeyword("LongitudeDirection","PositiveWest"), Pvl::Replace);
      }
    }

    //If the longitude domain is not in the mapping group, assume it is 360
    if(!mapGrp.HasKeyword("LongitudeDomain")) {
      mapGrp.AddKeyword(PvlKeyword("LongitudeDomain","360"), Pvl::Replace);
    }

    //If the default range is to be computed, use the input lat/long cubes to determine the range
    if(ui.GetString("DEFAULTRANGE") == "COMPUTE") {
      //NOTE - When computing the min/max longitude this application does not account for the 
      //longitude seam if it exists. Since the min/max are calculated from the statistics of
      //the input longitude cube and then converted to the mapping group's domain they may be
      //invalid for cubes containing the longitude seam. 
    
      Statistics *latStats = latCube->Statistics();
      Statistics *lonStats = lonCube->Statistics();

      double minLat = latStats->Minimum();
      double maxLat = latStats->Maximum();

      bool isOcentric = ((std::string)mapGrp.FindKeyword("LatitudeType")) == "Planetocentric";
 
      if(isOcentric) {
        if(ui.GetString("LATTYPE") != "PLANETOCENTRIC") {
          minLat = Projection::ToPlanetocentric(minLat, (double)equRadius, (double)polRadius);
          maxLat = Projection::ToPlanetocentric(maxLat, (double)equRadius, (double)polRadius);
        }
      }
      else {
        if(ui.GetString("LATTYPE") == "PLANETOCENTRIC") {
          minLat = Projection::ToPlanetographic(minLat, (double)equRadius, (double)polRadius);
          maxLat = Projection::ToPlanetographic(maxLat, (double)equRadius, (double)polRadius);
        }
      }

      int lonDomain = (int)mapGrp.FindKeyword("LongitudeDomain");
      double minLon = lonDomain == 360 ? Projection::To360Domain(lonStats->Minimum()) : Projection::To180Domain(lonStats->Minimum());
      double maxLon = lonDomain == 360 ? Projection::To360Domain(lonStats->Maximum()) : Projection::To180Domain(lonStats->Maximum());

      bool isPosEast = ((std::string)mapGrp.FindKeyword("LongitudeDirection")) == "PositiveEast";
      
      if(isPosEast) {
        if(ui.GetString("LONDIR") != "POSITIVEEAST") {
          minLon = Projection::ToPositiveEast(minLon, lonDomain);
          maxLon = Projection::ToPositiveEast(maxLon, lonDomain);
        }
      }
      else {
        if(ui.GetString("LONDIR") == "POSITIVEEAST") {
          minLon = Projection::ToPositiveWest(minLon, lonDomain);
          maxLon = Projection::ToPositiveWest(maxLon, lonDomain);
        }
      }

      if(minLon > maxLon) {
        double temp = minLon;
        minLon = maxLon;
        maxLon = temp;
      }

      mapGrp.AddKeyword(PvlKeyword("MinimumLatitude", minLat),Pvl::Replace);
      mapGrp.AddKeyword(PvlKeyword("MaximumLatitude", maxLat),Pvl::Replace);
      mapGrp.AddKeyword(PvlKeyword("MinimumLongitude", minLon),Pvl::Replace);
      mapGrp.AddKeyword(PvlKeyword("MaximumLongitude", maxLon),Pvl::Replace);
    }

    //If the user decided to enter a ground range then override
    if (ui.WasEntered("MINLAT")) {
      mapGrp.AddKeyword(PvlKeyword("MinimumLatitude",
                                        ui.GetDouble("MINLAT")),Pvl::Replace);
    }
  
    if (ui.WasEntered("MAXLAT")) {
      mapGrp.AddKeyword(PvlKeyword("MaximumLatitude",
                                        ui.GetDouble("MAXLAT")),Pvl::Replace);
    }

    if (ui.WasEntered("MINLON")) {
      mapGrp.AddKeyword(PvlKeyword("MinimumLongitude",
                                        ui.GetDouble("MINLON")),Pvl::Replace);
    }
  
    if (ui.WasEntered("MAXLON")) {
      mapGrp.AddKeyword(PvlKeyword("MaximumLongitude",
                                        ui.GetDouble("MAXLON")),Pvl::Replace);
    }
  
    //If the pixel resolution is to be computed, compute the pixels/degree from the input
    if (ui.GetString("PIXRES") == "COMPUTE") {
      latBrick.SetBasePosition(1,1,1);
      latCube->Read(latBrick);

      lonBrick.SetBasePosition(1,1,1);
      lonCube->Read(lonBrick);

      //Read the lat and long at the upper left corner
      double a = latBrick.at(0) * PI/180.0;
      double c = lonBrick.at(0) * PI/180.0;
  
      latBrick.SetBasePosition(latCube->Samples(),latCube->Lines(),1);
      latCube->Read(latBrick);

      lonBrick.SetBasePosition(lonCube->Samples(),lonCube->Lines(),1);     
      lonCube->Read(lonBrick);

      //Read the lat and long at the lower right corner
      double b = latBrick.at(0) * PI/180.0;
      double d = lonBrick.at(0) * PI/180.0;

      //Determine the angle between the two points
      double angle = acos(cos(a) * cos(b) * cos(c - d) + sin(a) * sin(b));
      //double angle = acos((cos(a1) * cos(b1) * cos(b2)) + (cos(a1) * sin(b1) * cos(a2) * sin(b2)) + (sin(a1) * sin(a2)));
      angle *= 180/PI;

      //Determine the number of pixels between the two points
      double pixels = sqrt(pow(latCube->Samples() -1.0, 2.0) + pow(latCube->Lines() -1.0, 2.0));

      //Add the scale in pixels/degree to the mapping group
      mapGrp.AddKeyword(PvlKeyword("Scale",
                                        pixels/angle, "pixels/degree"),
                                        Pvl::Replace);
      if (mapGrp.HasKeyword("PixelResolution")) {
        mapGrp.DeleteKeyword("PixelResolution");
      }
    }


    // If the user decided to enter a resolution then override
    if (ui.GetString("PIXRES") == "MPP") {
      mapGrp.AddKeyword(PvlKeyword("PixelResolution",
                                        ui.GetDouble("RESOLUTION"), "meters/pixel"),
                                        Pvl::Replace);
      if (mapGrp.HasKeyword("Scale")) {
        mapGrp.DeleteKeyword("Scale");
      }
    }
    else if (ui.GetString("PIXRES") == "PPD") {
      mapGrp.AddKeyword(PvlKeyword("Scale",
                                        ui.GetDouble("RESOLUTION"), "pixels/degree"),
                                        Pvl::Replace);
      if (mapGrp.HasKeyword("PixelResolution")) {
        mapGrp.DeleteKeyword("PixelResolution");
      }
    }

    //Create a projection using the map file we created
    int samples,lines;
    Projection *outmap = ProjectionFactory::CreateForCube(mapFile,samples,lines,false);

    //Write the map file to the log
    Application::GuiLog(mapGrp);

    //Create a process rubber sheet
    ProcessRubberSheet r;

    //Set the input cube
    inCube = r.SetInputCube("FROM");

    double tolerance = ui.GetDouble("TOLERANCE") * outmap->Resolution();

    //Create a new transform object
    Transform *transform = new nocam2map (sampSol, lineSol, outmap,
                                          latCube, lonCube,
                                          ui.GetString("LATTYPE") == "PLANETOCENTRIC",
                                          ui.GetString("LONDIR") == "POSITIVEEAST",
                                          tolerance, ui.GetInteger("ITERATIONS"),
                                          inCube->Samples(), inCube->Lines(),
                                          samples, lines);
  
    //Allocate the output cube and add the mapping labels
    Cube *oCube = r.SetOutputCube ("TO", transform->OutputSamples(),
                                              transform->OutputLines(),
                                              inCube->Bands());
    oCube->PutGroup(mapGrp);

    //Determine which interpolation to use
    Interpolator *interp = NULL;
    if (ui.GetString("INTERP") == "NEARESTNEIGHBOR") {
      interp = new Interpolator(Interpolator::NearestNeighborType);
    }
    else if (ui.GetString("INTERP") == "BILINEAR") {
      interp = new Interpolator(Interpolator::BiLinearType);
    }
    else if (ui.GetString("INTERP") == "CUBICCONVOLUTION") {
      interp = new Interpolator(Interpolator::CubicConvolutionType);
    }
  
    //Warp the cube
    r.StartProcess(*transform, *interp);
    r.EndProcess();

    // add mapping to print.prt
    PvlGroup mapping = outmap->Mapping(); 
    Application::Log(mapping); 

    //Clean up
    delete latCube;
    delete lonCube;

    delete outmap;
    delete transform;
    delete interp;
  }
}