Unverified Commit f8d013a9 authored by Kristin's avatar Kristin Committed by GitHub
Browse files

Update to CaSSIS Exported PDS4 images and Mosaicking program (#3402)

* Update date and other metadata

* Added changes to tgo-related applications and PEP4 to update PDS4 exported labels for CaSSIS based on requests

* Updated tgocassisrdrgen to add PSA schema

* Add function on cube to return actual min/max lat/lons based on DNs and updated PEP4 to format a couple of specific output map values with a trailing .0. Also update PEP4 to use the new function on Cube to calculate min/max lat/lons

* Fixed description order, updated geom schema details to pass validation, propagate Archive group to mosaic output needed for PDS4 export

* Update to PEP4

* Update tgocassisrdrgen

* Cleanup before commit

* More cleanup add header file history entries

* Clean up PEP4 before commit

* Updates to address review comments
parent fae3b9e1
Loading
Loading
Loading
Loading
+93 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@
#include "Projection.h"
#include "SpecialPixel.h"
#include "Statistics.h"
#include "TProjection.h"

using namespace std;

@@ -2133,6 +2134,98 @@ namespace Isis {
  }


/**
 * Returns the latitude and longitude range for the Cube. More accurate than the minimum and 
 * maximum latitude and longitude from the mapping group. 
 * 
 * @param[out] minLatitude minimum latitude present in the cube
 * @param[out] maxLatitude maximum latitude present in the cube
 * @param[out] minLongitude minimum longitude present in the cube
 * @param[out] maxLongitude maximum longitude present in the cube
 */
  void Cube::latLonRange(double &minLatitude, double &maxLatitude, double &minLongitude, double &
                         maxLongitude) {
    Camera *cam;
    TProjection *proj;

    bool isGood = false;
    bool useProj = true;

    if (hasGroup("Instrument")) {
      useProj = false;
    }

    // setup camera or projection
    if (useProj) {
     try {
       proj = (TProjection *) projection();
     }
     catch(IException &e) {
       QString msg = "Cannot calculate lat/lon range without a camera or projection";
       throw IException(e, IException::User, msg, _FILEINFO_);
     }
    }
    else {
      try {
        cam = camera();
      }
      catch(IException &e) {
        QString msg = "Unable to create camera when calculating a lat/lon range.";
        throw IException(e, IException::User, msg, _FILEINFO_);
      }
    }

    // Iterate over all samp/line combos in cube
    minLatitude = 99999;
    minLongitude = 99999;
    maxLatitude = -99999;
    maxLongitude = -99999;

    for (int sample = 0.5; sample < sampleCount() + 0.5; sample++) {
    // Checks to see if the point is in outer space
      for (int line = 0.5; line < lineCount() + 0.5; line++) {
        if (useProj) {
          isGood = proj->SetWorld(sample, line);
        }
        else {
          isGood = cam->SetImage(sample, line);
        }
      
        double lat, lon;
        if (isGood) {
          if (useProj) {
            lat = proj->UniversalLatitude();
            lon = proj->UniversalLongitude();
          }
          else {
            lat = cam->UniversalLatitude(); 
            lon = cam->UniversalLongitude(); 
          }
        
          // update mix/max lat/lons
          if (lat < minLatitude) {
            minLatitude = lat;
          }
          else if (lat > maxLatitude) {
            maxLatitude = lat;
          }
          
          if (lon < minLongitude) {
            minLongitude = lon;
          }
          else if (lon > maxLongitude) {
            maxLongitude = lon; 
          }
        }
      }
    }
    if ( (minLatitude == 99999) || (minLongitude == 99999) || (maxLatitude == -99999) || 
    (maxLongitude == -99999) ) {
      QString msg = "Unable to calculate a minimum or maximum latitutde or longitude.";
        throw IException(IException::Unknown, msg, _FILEINFO_);
    }
  }
  
  /**
   * Write the Pvl labels to the cube's label file. Excess data in the attached
   *   labels is set to 0.
+3 −0
Original line number Diff line number Diff line
@@ -165,6 +165,7 @@ namespace Isis {
   *   @history 2018-01-18 Summer Stapleton - Updated error message in ::create() to address when
   *                           an IsisPreference file cannot be found. Fixes #5145.
   *   @history 2018-11-16 Jesse Mapel - Made several methods virtual for mocking.
   *   @history 2019-06-15 Kristin Berry - Added latLonRange method to return the valid lat/lon rage of the cube. The values in the mapping group are not sufficiently accurate for some purposes. 
   */
  class Cube {
    public:
@@ -301,6 +302,8 @@ namespace Isis {
      bool hasGroup(const QString &group) const;
      bool hasTable(const QString &name);
      void putGroup(const PvlGroup &group);
      void latLonRange(double &minLatitude, double &maxLatitude, double &minLongitude, 
                       double &maxLongitude); 

    private:
      void applyVirtualBandsToLabel();
+207 −57
Original line number Diff line number Diff line
@@ -273,7 +273,7 @@ namespace Isis {
      PvlToXmlTranslationManager targXlator(*inputLabel, translationFileName.expanded());
      targXlator.Auto(*m_domDoc);

      // move target to just below Observing_System. 
      // Move target to just below Observing_System. 
      QDomElement targetIdNode = obsAreaNode.firstChildElement("Target_Identification");
      obsAreaNode.insertAfter(targetIdNode, obsAreaNode.firstChildElement("Observing_System"));
    }
@@ -304,12 +304,19 @@ namespace Isis {
          if (startTime.text() == "") {
            startTime.setAttribute("xsi:nil", "true");
          }
          else {
            QString timeValue = startTime.text();
            PvlToXmlTranslationManager::resetElementValue(startTime, timeValue + "Z");
          }

          QDomElement stopTime  = timeNode.firstChildElement("stop_date_time"); 
          if (stopTime.text() == "") {
            stopTime.setAttribute("xsi:nil", "true");
          }

          else {
            QString timeValue = stopTime.text();
            PvlToXmlTranslationManager::resetElementValue(stopTime, timeValue + "Z");
          }
          QStringList xmlPath;
          xmlPath << "Product_Observational"
            << "Observation_Area"
@@ -779,6 +786,15 @@ namespace Isis {
    
  }

 /**
  * Sets the description string which describes the pixel vales in 
  * File_Area_Observational 
  *  
  * @param description Description of pixel values to use.
  */
  void ProcessExportPds4::setPixelDescription(QString description) {
    m_pixelDescription = description;
  }

  /**
   * Create and internalize an image output label from the input 
@@ -882,36 +898,139 @@ namespace Isis {
        elementArrayElement.appendChild(offsetElement);
      }

      // Add the Special_Constants class to define ISIS special pixel values: 

      // Assume 32-bit/Real for CaSSIS
      // Add the Special_Constants class to define ISIS special pixel values if pixel type is
      QDomElement specialConstantElement = m_domDoc->createElement("Special_Constants"); 
      arrayImageElement.insertAfter(specialConstantElement,
                                    arrayImageElement.lastChildElement("Axis_Array"));

      QDomElement nullElement = m_domDoc->createElement("missing_constant");
      PvlToXmlTranslationManager::setElementValue(nullElement, QString::number(NULL8, 'g', 18)); //toString(NULL8));
      switch (p_pixelType) {
        case Real: 
          { QDomElement nullElement = m_domDoc->createElement("missing_constant");
          PvlToXmlTranslationManager::setElementValue(nullElement, QString::number(NULL4, 'g', 18));
          specialConstantElement.appendChild(nullElement);
        
          QDomElement highInstrumentSatElement = m_domDoc->createElement("high_instrument_saturation");
          PvlToXmlTranslationManager::setElementValue(highInstrumentSatElement, QString::number(HIGH_INSTR_SAT4, 'g', 18));
          specialConstantElement.appendChild(highInstrumentSatElement);

          QDomElement highRepresentationSatElement = m_domDoc->createElement("high_representation_saturation");
          PvlToXmlTranslationManager::setElementValue(highRepresentationSatElement, QString::number(HIGH_REPR_SAT4, 'g', 18));
          specialConstantElement.appendChild(highRepresentationSatElement);

          QDomElement lowInstrumentSatElement = m_domDoc->createElement("low_instrument_saturation");
          PvlToXmlTranslationManager::setElementValue(lowInstrumentSatElement, QString::number(LOW_INSTR_SAT4, 'g', 18));
          specialConstantElement.appendChild(lowInstrumentSatElement);

          QDomElement lowRepresentationSatElement = m_domDoc->createElement("low_representation_saturation");
          PvlToXmlTranslationManager::setElementValue(lowRepresentationSatElement, QString::number(LOW_REPR_SAT4, 'g', 18));
          specialConstantElement.appendChild(lowRepresentationSatElement); 
          break;}

        case UnsignedByte:
          { QDomElement nullElement = m_domDoc->createElement("missing_constant");
          PvlToXmlTranslationManager::setElementValue(nullElement, QString::number(NULL1, 'g', 18));
          specialConstantElement.appendChild(nullElement);
        
          QDomElement highInstrumentSatElement = m_domDoc->createElement("high_instrument_saturation");
          PvlToXmlTranslationManager::setElementValue(highInstrumentSatElement, QString::number(HIGH_INSTR_SAT1, 'g', 18));
          specialConstantElement.appendChild(highInstrumentSatElement);

          QDomElement highRepresentationSatElement = m_domDoc->createElement("high_representation_saturation");
          PvlToXmlTranslationManager::setElementValue(highRepresentationSatElement, QString::number(HIGH_REPR_SAT1, 'g', 18));
          specialConstantElement.appendChild(highRepresentationSatElement);

          QDomElement lowInstrumentSatElement = m_domDoc->createElement("low_instrument_saturation");
          PvlToXmlTranslationManager::setElementValue(lowInstrumentSatElement, QString::number(LOW_INSTR_SAT1, 'g', 18));
          specialConstantElement.appendChild(lowInstrumentSatElement);

          QDomElement lowRepresentationSatElement = m_domDoc->createElement("low_representation_saturation");
          PvlToXmlTranslationManager::setElementValue(lowRepresentationSatElement, QString::number(LOW_REPR_SAT1, 'g', 18));
          specialConstantElement.appendChild(lowRepresentationSatElement);
          break; }

        case SignedWord:
          { QDomElement nullElement = m_domDoc->createElement("missing_constant");
          PvlToXmlTranslationManager::setElementValue(nullElement, QString::number(NULL2, 'g', 18));
          specialConstantElement.appendChild(nullElement);
        
          QDomElement highInstrumentSatElement = m_domDoc->createElement("high_instrument_saturation");
      PvlToXmlTranslationManager::setElementValue(highInstrumentSatElement, QString::number(HIGH_INSTR_SAT8, 'g', 18));
          PvlToXmlTranslationManager::setElementValue(highInstrumentSatElement, QString::number(HIGH_INSTR_SAT2, 'g', 18));
          specialConstantElement.appendChild(highInstrumentSatElement);

          QDomElement highRepresentationSatElement = m_domDoc->createElement("high_representation_saturation");
      PvlToXmlTranslationManager::setElementValue(highRepresentationSatElement, QString::number(HIGH_REPR_SAT8, 'g', 18));
          PvlToXmlTranslationManager::setElementValue(highRepresentationSatElement, QString::number(HIGH_REPR_SAT2, 'g', 18));
          specialConstantElement.appendChild(highRepresentationSatElement);

          QDomElement lowInstrumentSatElement = m_domDoc->createElement("low_instrument_saturation");
      PvlToXmlTranslationManager::setElementValue(lowInstrumentSatElement, QString::number(LOW_INSTR_SAT8, 'g', 18));
          PvlToXmlTranslationManager::setElementValue(lowInstrumentSatElement, QString::number(LOW_INSTR_SAT2, 'g', 18));
          specialConstantElement.appendChild(lowInstrumentSatElement);

          QDomElement lowRepresentationSatElement = m_domDoc->createElement("low_representation_saturation");
      PvlToXmlTranslationManager::setElementValue(lowRepresentationSatElement, QString::number(LOW_REPR_SAT8, 'g', 18));
          PvlToXmlTranslationManager::setElementValue(lowRepresentationSatElement, QString::number(LOW_REPR_SAT2, 'g', 18));
          specialConstantElement.appendChild(lowRepresentationSatElement); 
          break; }

        case UnsignedWord:
          { QDomElement nullElement = m_domDoc->createElement("missing_constant");
          PvlToXmlTranslationManager::setElementValue(nullElement, QString::number(NULLU2, 'g', 18));
          specialConstantElement.appendChild(nullElement);
        
          QDomElement highInstrumentSatElement = m_domDoc->createElement("high_instrument_saturation");
          PvlToXmlTranslationManager::setElementValue(highInstrumentSatElement, QString::number(HIGH_INSTR_SATU2, 'g', 18));
          specialConstantElement.appendChild(highInstrumentSatElement);

          QDomElement highRepresentationSatElement = m_domDoc->createElement("high_representation_saturation");
          PvlToXmlTranslationManager::setElementValue(highRepresentationSatElement, QString::number(HIGH_REPR_SATU2, 'g', 18));
          specialConstantElement.appendChild(highRepresentationSatElement);

          QDomElement lowInstrumentSatElement = m_domDoc->createElement("low_instrument_saturation");
          PvlToXmlTranslationManager::setElementValue(lowInstrumentSatElement, QString::number(LOW_INSTR_SATU2, 'g', 18));
          specialConstantElement.appendChild(lowInstrumentSatElement);

          QDomElement lowRepresentationSatElement = m_domDoc->createElement("low_representation_saturation");
          PvlToXmlTranslationManager::setElementValue(lowRepresentationSatElement, QString::number(LOW_REPR_SATU2, 'g', 18));
          specialConstantElement.appendChild(lowRepresentationSatElement); 
          break; }

        case None: break;
      }



      if (!m_pixelDescription.isEmpty()) {
        QDomElement descriptionElement = m_domDoc->createElement("description"); 
        PvlToXmlTranslationManager::setElementValue(descriptionElement,
                                                    m_pixelDescription);
        arrayImageElement.insertAfter(descriptionElement, arrayImageElement.lastChildElement());
      }
    }
  }


  /**
   * Adds necessary information to the xml header for a pds4 class for schema which lack 
   * schematron files (.sch) 
   * 
   * @param xsd Schema filename without path
   * @param xmlns The xml namespace used 
   * @param xmlnsURI Full URL to the xml namespace URI. Also used as the location of the sch and xsd
   */
  void ProcessExportPds4::addSchema(QString xsd, QString xmlns, QString xmlnsURI) {
    // Add xmlns
    QDomElement root = m_domDoc->documentElement();
    root.setAttribute(xmlns, xmlnsURI);

    // Add to xsi:schemaLocation
    m_schemaLocation += " "; 
    m_schemaLocation += xmlnsURI;
    m_schemaLocation += " ";
    m_schemaLocation += xmlnsURI;
    m_schemaLocation += "/";
    m_schemaLocation += xsd;
    root.setAttribute("xsi:schemaLocation", m_schemaLocation);
  }


  /**
   * Adds necessary information to the xml header for a pds4 class. 
   * 
@@ -933,17 +1052,7 @@ namespace Isis {
    m_domDoc->insertAfter(header, m_domDoc->firstChild());

    // Add xmlns
    QDomElement root = m_domDoc->documentElement();
    root.setAttribute(xmlns, xmlnsURI);

    // Add to xsi:schemaLocation
    m_schemaLocation += " "; 
    m_schemaLocation += xmlnsURI;
    m_schemaLocation += " ";
    m_schemaLocation += xmlnsURI;
    m_schemaLocation += "/";
    m_schemaLocation += xsd;
    root.setAttribute("xsi:schemaLocation", m_schemaLocation);
    addSchema(xsd, xmlns, xmlnsURI);
  }


@@ -1126,13 +1235,14 @@ namespace Isis {
      }
    }

    // Add the EASTERNMOST AND WESTERNMOST LONGITUDE keywords
    PvlKeyword &isisLonDir = inputMapping.findKeyword("LongitudeDirection");
    QString lonDir = isisLonDir[0];
    lonDir = lonDir.toUpper();
    if (inputMapping.hasKeyword("MaximumLongitude") && inputMapping.hasKeyword("MinimumLongitude")) {
      double maxLon = inputMapping.findKeyword("MaximumLongitude");
      double minLon = inputMapping.findKeyword("MinimumLongitude");
    
    // Add Lat/Lon range
    double maxLon, minLon, maxLat, minLat; 
    InputCubes[0]->latLonRange(minLat, maxLat, minLon, maxLon); 
    
    xmlPath.clear();
    xmlPath << "Product_Observational"
            << "Observation_Area" 
@@ -1143,14 +1253,55 @@ namespace Isis {
    QDomElement boundingCoordElement = getElement(xmlPath, baseElement);
    QDomElement eastElement = boundingCoordElement.firstChildElement("cart:east_bounding_coordinate");
    QDomElement westElement = boundingCoordElement.firstChildElement("cart:west_bounding_coordinate");
    QDomElement northElement = boundingCoordElement.firstChildElement("cart:north_bounding_coordinate");
    QDomElement southElement = boundingCoordElement.firstChildElement("cart:south_bounding_coordinate");

      // translation files currently handles Positive West case where east = min, west = max
    // Translation files currently handles Positive West case where east = min, west = max
    // so if positive east, swap min/max
    if(QString::compare(lonDir, "PositiveEast", Qt::CaseInsensitive) == 0) {
      // west min, east max
      PvlToXmlTranslationManager::resetElementValue(eastElement, toString(maxLon), "deg");
      PvlToXmlTranslationManager::resetElementValue(westElement, toString(minLon), "deg");
    }
    else {
      PvlToXmlTranslationManager::resetElementValue(eastElement, toString(minLon), "deg");
      PvlToXmlTranslationManager::resetElementValue(westElement, toString(maxLon), "deg");
    }

    PvlToXmlTranslationManager::resetElementValue(northElement, toString(maxLat), "deg");
    PvlToXmlTranslationManager::resetElementValue(southElement, toString(minLat), "deg");

    // longitude_of_central_meridian and latitude_of_projection_origin need to be converted to floats.
    xmlPath.clear();
    xmlPath << "Product_Observational"
            << "Observation_Area" 
            << "Discipline_Area"
            << "cart:Cartography" 
            << "cart:Spatial_Reference_Information"
            << "cart:Horizontal_Coordinate_System_Definition"
            << "cart:Planar"
            << "cart:Map_Projection";

    // The following is necessary because the full xmlPath differs depending on the projection used.
    QDomElement projectionElement = getElement(xmlPath, baseElement);
    QDomElement tempElement = projectionElement.firstChildElement();
    QDomElement nameElement = tempElement.nextSiblingElement();

    QDomElement longitudeElement = nameElement.firstChildElement("cart:longitude_of_central_meridian"); 
    QDomElement originElement = nameElement.firstChildElement("cart:latitude_of_projection_origin");

    double longitudeElementValue = longitudeElement.text().toDouble(); 
    double originElementValue = originElement.text().toDouble(); 

    // Only update the ouput formatting if there are no digits after the decimal point. 
    if (!longitudeElement.text().contains('.')) {
      QString toset1 = QString::number(longitudeElementValue, 'f', 1); 
      PvlToXmlTranslationManager::resetElementValue(longitudeElement, toset1, "deg"); 
    }

    if (!originElement.text().contains('.')) {
      QString toset2 = QString::number(originElementValue, 'f', 1); 
      PvlToXmlTranslationManager::resetElementValue(originElement, toset2, "deg");
    }
  }

@@ -1373,7 +1524,6 @@ namespace Isis {
        }
      }
    }

    return transMap; 
  }

+8 −2
Original line number Diff line number Diff line
@@ -78,6 +78,9 @@ namespace Isis {
   *   @history 2019-03-01 Kristin Berry - Added ability to set version_id and title, added
   *                           Special_Constants to define ISIS special pixel values, fixed east/west
   *                           bounding coordinates swap bug. Fixes git issue #2635.
   *   @history 2019-06-15 Kristin Berry - Added a new addSchema() function for cases in which a
   *                           schematron file is not available and added setPixelDescription to
   *                           set a pixel description for the output image. 
   */

  class ProcessExportPds4: public Isis::ProcessExport {
@@ -115,11 +118,13 @@ namespace Isis {
      void setTitle(QString title);
      void setSchemaLocation(QString schema);
      void setImageType(ImageType imageType);

      void setPixelDescription(QString description); 
      static void translateUnits(QDomDocument &label,
                                 QString transMapFile = "$base/translations/pds4ExportUnits.pvl");
      void reorder();
      void addSchema(QString sch, QString xsd, QString xmlns, QString xmlnsURI);
      void addSchema(QString xsd, QString xmlns, QString xmlnsURI);

    protected:
      void identificationArea();
      void standardInstrument();
@@ -139,6 +144,7 @@ namespace Isis {
      QString m_versionId;                  //!< QString with specified version id.
      QString m_title;                      //!< QString with specified title. 
      ImageType m_imageType;                //!< Type of image data to be written.
      QString m_pixelDescription;           //!< Description of pixel values.

  };
}
+6 −7
Original line number Diff line number Diff line
@@ -350,15 +350,14 @@ namespace Isis {
   *
   * @return string
   */
  QString iTime::SecondString() const {
  QString iTime::SecondString(int precision) const {
    ostringstream osec;
    osec.setf(ios::fixed);
    osec << setprecision(8) << Second();
    osec << setprecision(precision) << Second();
    QString sSeconds(osec.str().c_str());
    sSeconds = sSeconds.remove(QRegExp("(\\.0*|0*)$"));

    if(sSeconds.isEmpty()) sSeconds = "0";

    return sSeconds;
  }

@@ -416,7 +415,7 @@ namespace Isis {
   *
   * @return string The internalized time, in UTC format
   */
  QString iTime::UTC() const {
  QString iTime::UTC(int precision) const {
    QString utc = YearString() + "-" ;
    if(Month() < 10) utc += "0" + MonthString() + "-";
    else utc += MonthString() + "-";
@@ -430,8 +429,8 @@ namespace Isis {
    if(Minute() < 10) utc += "0" + MinuteString() + ":";
    else utc += MinuteString() + ":";

    if(Second() < 10) utc += "0" + SecondString();
    else utc += SecondString();
    if(Second() < 10) utc += "0" + SecondString(precision);
    else utc += SecondString(precision);
    
    return utc;
  }
Loading