Unverified Commit 1a9b9cdc authored by kledmundson's avatar kledmundson Committed by GitHub
Browse files

Mosrange Error Handling Improved to Not Abort on Bad Images (#5292)

* Add detailed error handling for mosrange.

*ONERROR, ERRORLOG, and ERRORLIST parameters added for error handling.
*ONERROR dictates whether mosrange will abort or continue when an error
occurs.
*If ONERROR=FAIL (default behavior), mosrange aborts upon error
without generating a map file.
*If ONERROR=CONTINUE, mosrange produces an output map file with data
collected from all successfully processed images.
*A detailed list of files that fail and their associated errors are
written to ERRORLOG if provided.
*A simple list of failed files is written to ERRORLIST if provided.

* Add old style ISIS tests for mosrange error handling.

*Add test for ONERROR=CONTINUE.
*Add test for ONERROR=FAIL.

* Updates to mosrange error handling; converted mosrange to callable app; added gtests; removed old makefile tests

* Added note to CHANGELOG.md describing changes to mosrange error handling behavior.

* Moved cubeFileList size check from Pvl mosrange(UserInterface &ui) to Pvl mosrange(FileList &cubeFileList, UserInterface &ui) on PR review suggestion.
parent 0761d7e2
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ release.
- Added an optional cubename parameter to tgocassisstitch which lets the user
  override the timestamp style naming convention of the output cube with their
  own name; if not specified retains existing behavior [#5125](https://github.com/USGS-Astrogeology/ISIS3/issues/5162)
- Added new parameters <b>ONERROR</b>, <b>ERRORLOG</b>, and <b>ERRORLIST</b> to <i>mosrange</i> to provide better control over error behavior and provide diagnostics when problems are encountered processing the input file list.[#3606](https://github.com/DOI-USGS/ISIS3/issues/3606)

### Deprecated

+6 −2
Original line number Diff line number Diff line
@@ -9,12 +9,16 @@ find files of those names at the top level of this repository. **/

#include "Application.h"
#include "Pvl.h"
#include "UserInterface.h"
#include "mosrange.h"

using namespace Isis;

void IsisMain() {
  UserInterface &ui = Application::GetUserInterface();
  Pvl appLog;
  mosrange(ui, &appLog); 
  Pvl results = mosrange(ui);

  for (int resultIndex = 0; resultIndex < results.groups(); resultIndex++) {
      Application::Log(results.group(resultIndex));
  }
}
+161 −142
Original line number Diff line number Diff line
@@ -9,10 +9,12 @@ find files of those names at the top level of this repository. **/

#include <cmath>

#include <QPair>
#include <QList>

#include "Camera.h"
#include "Cube.h"
#include "Distance.h"
#include "FileList.h"
#include "Process.h"
#include "Pvl.h"
#include "Statistics.h"
@@ -67,18 +69,42 @@ namespace Isis {
    return (localRadius / pixres * pi_c() / 180.0);
  }

  void mosrange(UserInterface &ui, Pvl *log) {
    Process p;
  /**
   * Compute lat/lon range of a set of camera images for mosaicking
   *
   * @param ui UserInterface object containing parameters
   * @return Pvl results log file
   */
  Pvl mosrange(UserInterface &ui) {

    // Get the list of names of input cubes to stitch together
    FileList cubeFileList;
    cubeFileList.read(ui.GetFileName("FROMLIST"));

    return mosrange(cubeFileList, ui);
  }

    // Get the list of names of input CCD cubes to stitch together
    FileList flist;
    flist.read(ui.GetFileName("FROMLIST"));
    if(flist.size() < 1) {

  /**
   * Compute lat/lon range of a set of camera images for mosaicking
   *
   * @param FileList List of cube filenames
   * @return Pvl results log file
   *
   * @throws IException::User "The list file [FILENAME] does not contain any filenames"
   * @throws IException::User "--> Fatal Errors Encountered <___ [FILENAMES]"
   * @throws IException::User "Unable to open/create error list file [FILENAME]"
   */
  Pvl mosrange(FileList &cubeFileList, UserInterface &ui) {
    if ( cubeFileList.size() < 1)  {
      QString msg = "The list file[" + ui.GetFileName("FROMLIST") +
                    " does not contain any filenames";
      throw IException(IException::User, msg, _FILEINFO_);
    }

    Pvl log;
    Process p;

    QString projection("Equirectangular");
    if(ui.WasEntered("MAP")) {
      Pvl mapfile(ui.GetFileName("MAP"));
@@ -100,38 +126,39 @@ namespace Isis {
    londir = (londir == "POSITIVEEAST") ? "PositiveEast" : "PositiveWest";

    Progress prog;
    prog.SetMaximumSteps(flist.size());
    prog.SetMaximumSteps(cubeFileList.size());
    prog.CheckStatus();

    Statistics scaleStat;

    
    Statistics obliqueScaleStat;

    Statistics longitudeStat;
    Statistics latitudeStat;
    Statistics equiRadStat;
    Statistics poleRadStat;
    PvlObject fileset("FileSet");
    PvlObject errorset("ErrorSet");

    // Save major equitorial and polar radii for last occuring
    // Save major equitorial and polar radii for last occurring
    double eqRad;
    double poleRad;

    QString target("Unknown");
    for(int i = 0 ; i < flist.size() ; i++) {
    QList<QPair<QString, QString> > badfiles;
    for(int i = 0 ; i < cubeFileList.size() ; i++) {

        PvlObject fmap("File");
        fmap += PvlKeyword("Name", cubeFileList[i].toString());

        try {
        // Set the input image, get the camera model, and a basic mapping
        // group
          // Set input image, get camera model, and a basic mapping group
          Cube cube;
        cube.open(flist[i].toString());
          cube.open(cubeFileList[i].toString());

          int lines = cube.lineCount();
          int samples = cube.sampleCount();


          PvlObject fmap("File");
        fmap += PvlKeyword("Name", flist[i].toString());
          fmap += PvlKeyword("Name", cubeFileList[i].toString());
          fmap += PvlKeyword("Lines", toString(lines));
          fmap += PvlKeyword("Samples", toString(samples));

@@ -159,48 +186,22 @@ namespace Isis {
          double lowres = cam->LowestImageResolution();
          double hires = cam->HighestImageResolution();

        
          double lowObliqueRes = cam->LowestObliqueImageResolution();


        
          double hiObliqueRes= cam->HighestObliqueImageResolution();


          scaleStat.AddData(&hires, 1);
          scaleStat.AddData(&lowres, 1);

        
          obliqueScaleStat.AddData(&hiObliqueRes,1);
          obliqueScaleStat.AddData(&lowObliqueRes,1);


          double pixres = (lowres + hires) / 2.0;

        
        //double obliquePixRes = (lowObliqueRes+hiObliqueRes)/2.0;

          double scale = Scale(pixres, poleRad, eqRad);

        
        //double obliqueScale = Scale(obliquePixRes,poleRad,eqRad);


          mapgrp.addKeyword(PvlKeyword("PixelResolution", toString(pixres)), Pvl::Replace);

        
        //mapgrp.addKeyword(PvlKeyword("ObliquePixelResolution", toString(obliquePixRes)),
        //                  Pvl::Replace);

          mapgrp.addKeyword(PvlKeyword("Scale", toString(scale), "pixels/degree"), Pvl::Replace);

        
        //mapgrp.addKeyword(PvlKeyword("ObliqueScale", toString(obliqueScale), "pixels/degree"),
        //                  Pvl::Replace);
          mapgrp += PvlKeyword("MinPixelResolution", toString(lowres), "meters/pixel");
          mapgrp += PvlKeyword("MaxPixelResolution", toString(hires), "meters/pixel");

        
          mapgrp += PvlKeyword("MinObliquePixelResolution", toString(lowObliqueRes), "meters/pixel");
          mapgrp += PvlKeyword("MaxObliquePixelResolution", toString(hiObliqueRes), "meters/pixel");

@@ -221,28 +222,60 @@ namespace Isis {
          latitudeStat.AddData(&maxlat, 1);
        }
        catch(IException &ie) {
        QString mess = "Problems with file " + flist[i].toString() + "\n" +
                       ie.what();
        throw IException(IException::User, mess, _FILEINFO_);
          QString mess = cubeFileList[i].toString() + " - " + ie.what();
          fmap += PvlKeyword("Error", mess);
          errorset.addObject(fmap);

          badfiles.append(qMakePair(cubeFileList[i].toString(), ie.what()));
        }

        p.ClearInputCubes();
        prog.CheckStatus();
    }

  //  Construct the output mapping group with statistics
    PvlGroup mapping("Mapping");
    double avgPixRes( (scaleStat.Minimum() + scaleStat.Maximum() ) / 2.0);
    // Now check for error behavior if a problem was encountered
    if ( badfiles.size() > 0 ) {

        if (  ui.WasEntered("ERRORLOG") ) {
          Pvl temp;
          temp.addObject(errorset);
          temp.write(ui.GetFileName("ERRORLOG", "log"));
        }

    //double avgObliquePixRes( (obliqueScaleStat.Minimum() + obliqueScaleStat.Maximum() ) / 2.0);
        if ( ui.WasEntered("ERRORLIST") ) {
          FileName filename( ui.GetFileName("ERRORLIST") );
          QFile logfile(filename.expanded());
          if ( !logfile.open(QIODevice::WriteOnly | QIODevice::Truncate |
                             QIODevice::Text | QIODevice::Unbuffered) ) {
            QString mess = "Unable to open/create error list file " + filename.name();
            throw IException(IException::User, mess, _FILEINFO_);
          }

          QTextStream lout(&logfile);
          for ( int f = 0  ; f < badfiles.size() ; f++) {
             lout << badfiles[f].first << "\n";
          }
        }

        // Now check onerror status
        if ( ("FAIL" == ui.GetString("ONERROR").toUpper()) ||
             (badfiles.size() == cubeFileList.size()) ) {
          QString errors("--> Fatal Errors Encountered <___\n");
          for (int i = 0 ; i < badfiles.size() ; i++) {
              errors += badfiles[i].first + " - " + badfiles[i].second + "\n";
          }
          throw IException(IException::User, errors, _FILEINFO_);
        }
    }

    // Construct the output mapping group with statistics
    PvlGroup mapping("Mapping");
    double avgPixRes( (scaleStat.Minimum() + scaleStat.Maximum() ) / 2.0);
    double avgLat((latitudeStat.Minimum() + latitudeStat.Maximum()) / 2.0);
    double avgLon((longitudeStat.Minimum() + longitudeStat.Maximum()) / 2.0);
    double avgEqRad((equiRadStat.Minimum() + equiRadStat.Maximum()) / 2.0);
    double avgPoleRad((poleRadStat.Minimum() + poleRadStat.Maximum()) / 2.0);
    double scale  = Scale(avgPixRes, avgPoleRad, avgEqRad);
    //double obliqueScale = Scale(avgObliquePixRes,avgPoleRad,avgEqRad);

    mapping += PvlKeyword("ProjectionName", projection);
    mapping += PvlKeyword("TargetName", target);
@@ -252,27 +285,13 @@ namespace Isis {
    mapping += PvlKeyword("LongitudeDirection", londir);
    mapping += PvlKeyword("LongitudeDomain", londom);
    mapping += PvlKeyword("PixelResolution", toString(SetRound(avgPixRes, digits)), "meters/pixel");

    
    //mapping += PvlKeyword("ObliquePixelResolution", toString(SetRound(avgObliquePixRes, digits)),
    //                      "meters/pixel");


    mapping += PvlKeyword("Scale", toString(SetRound(scale, digits)), "pixels/degree");

    
    //mapping += PvlKeyword("ObliqueScale", toString(SetRound(obliqueScale, digits)), "pixels/degree");


    mapping += PvlKeyword("MinPixelResolution", toString(scaleStat.Minimum()), "meters/pixel");
    mapping += PvlKeyword("MaxPixelResolution", toString(scaleStat.Maximum()), "meters/pixel");

    
    mapping += PvlKeyword("MinObliquePixelResolution", toString(obliqueScaleStat.Minimum()),
                          "meters/pixel");
    mapping += PvlKeyword("MaxObliquePixelResolution", toString(obliqueScaleStat.Maximum()),
                          "meters/pixel");

    mapping += PvlKeyword("CenterLongitude", toString(SetRound(avgLon, digits)));
    mapping += PvlKeyword("CenterLatitude",  toString(SetRound(avgLat, digits)));
    mapping += PvlKeyword("MinimumLatitude", toString(MAX(SetFloor(latitudeStat.Minimum(),
@@ -293,9 +312,7 @@ namespace Isis {
    mapping += PvlKeyword("PreciseMinimumLongitude", toString(longitudeStat.Minimum()));
    mapping += PvlKeyword("PreciseMaximumLongitude", toString(longitudeStat.Maximum()));

    if (log){
      log->addLogGroup(mapping);
    }
    log.addGroup(mapping);

    // Write the output file if requested
    if(ui.WasEntered("TO")) {
@@ -311,6 +328,8 @@ namespace Isis {
    }

    p.EndProcess();

    return log;
  }
}

+3 −1
Original line number Diff line number Diff line
@@ -8,10 +8,12 @@ find files of those names at the top level of this repository. **/
#ifndef mosrange_h
#define mosrange_h

#include "FileList.h"
#include "UserInterface.h"

namespace Isis{
  extern void mosrange(UserInterface &ui, Pvl *log=nullptr);
  extern Pvl mosrange(UserInterface &ui);
  extern Pvl mosrange(FileList &cubeList, UserInterface &ui);
}

#endif
+114 −16
Original line number Diff line number Diff line
@@ -4,36 +4,48 @@
xsi:noNamespaceSchemaLocation=
"http://isis.astrogeology.usgs.gov/Schemas/Application/application.xsd">
  <brief>
    Compute the lat/lon range of a set camera images for mosaicking
    Compute the lat/lon range of a set of camera images for mosaicking
  </brief>

  <description>
    <p>
      This program computes and outputs the 
      <def link="Latitude">latitude</def>/<def link="Longitude">longitude</def>
      range of a set of images in camera space, as well as the <def link="Pixel Resolution">
      pixel resolution</def> and the <def link="Oblique Pixel Resolution"> 
      oblique pixel resolution.</def>.  It creates a <i>cam2map</i> ready map file with 
      the extents of the latitude/longitude ranges of the image set.  
      <i>mosrange</i> computes and outputs the <def link="Latitude">latitude</def>
      and <def link="Longitude">longitude</def> ranges of a set of <def link="Level1">Level1</def>
      images (i.e. non-projected), as well as the <def link="Pixel Resolution"> pixel
      resolution</def> and the <def link="Oblique Pixel Resolution"> oblique pixel resolution</def>.
      It creates a <a href="../cam2map/cam2map.html" target="_blank">cam2map</a> ready map file
      with the extents of the latitude/longitude ranges of the image set.
    </p>

    <p>
      The user can select the type of <def link="Map Projection">map projection</def>
      preferred by two different means. The PROJECTION parameter allows direct 
      preferred by two different ways. The PROJECTION parameter allows direct 
      specification of an ISIS supported projection.  Or, the user can select a 
      map file from the ISIS map template system that contains the projection name.  
      If none of these options are used, then <b>Equirectangular</b> is the default.
    </p>

    <p>
      mosrange provides better control over the values of the latitude/longitude 
      <i>mosrange</i> provides better control over the values of the latitude/longitude 
      ranges by providing a PRECISION parameter.  This parameter specifies the 
      maximum nuber of digits precision for many of the Mapping group parameters 
      maximum number of digits of precision for many of the Mapping group parameters 
      used to project images.
    </p>
    
    <p>
      Its primary use is to provide a quick, simple and batchable means of 
      ONERROR, ERRORLOG, and ERRORLIST parameters offer better error handling and
      diagnostics when problems are encountered processing the input file list.
      ONERROR dictates whether <i>mosrange</i> will abort or continue when an error
      occurs. If ONERROR=FAIL (default behavior), <i>mosrange</i> aborts upon error
      without generating a map file. If ONERROR=CONTINUE, <i>mosrange</i> produces
      an output map file with data collected from all successfully processed images.
      A detailed list of files that fail and their associated errors are written to
      the ERRORLOG file if provided. A simple list of failed files is written to the
      ERRORLIST file if provided.
    </p>
 
    <p>
      The primary use of <i>mosrange</i> is to provide a quick, simple and batchable means of
      creating map files for projections.
    </p>
    
@@ -111,12 +123,20 @@ End
       Updated to use new Target class. References Mantis tickets #775 and #1114.
    </change>
    <change name="Tyler Wilson" date="2016-08-25">
       Updated to use upated Camera/CameraPointInfo classes which include improved approximations
       Updated to use updated Camera/CameraPointInfo classes which include improved approximations
       to Pixel/Detector/Line/Sample resolutions, as well as providing the ability for developers
       to order the fields in CSV/Pvl output.  References #476"
       to order the fields in CSV/Pvl output.  References #476.
    </change>
    <change name="Kris Becker" date="2019-03-11">
        Added ERRORLOG, ERRORLIST and ONERROR flags to provide better control
        error behavior and provide diagnostics when problems are encountered
        processing input file list.
    </change>
    <change name="Ken Edmundson" date="2023-09-07">
        Moved Kris Becker's 2019-03-11 changes from UofA code base to USGS. Updated documentation.
        Cleaned up unnecessary blank lines and commented code in mosrange.cpp. References #3606.
    </change>
 </history>

  <groups>
    <group name="Files">
      <parameter name="FROMLIST">
@@ -367,5 +387,83 @@ End
        </description>
      </parameter>
    </group>
    
    <group name="Errorhandling">
      <parameter name="ONERROR">
        <type>string</type>
        <brief>
          Define behavior when a file error occurs
        </brief>
        <description>
          This flag is provided to specify what action is to be
          taken should an error occur processing the input file
          list. A detailed list of errors for each file will be
          recorded to ERRORLOG if a file name is provided. A list
          containing the file names that failed will be written
          to ERRORLIST if provided. If ONERROR=FAIL, a single
          error will result in an abort. FAIL is the default
          (to preserve existing behavior).
        </description>
        <default>
          <item>FAIL</item>
        </default>
        <list>
          <option value="CONTINUE">
            <brief>
              Continue processing all the input file list if an error occurs
            </brief>
            <description>
              Continue to produce an output map file with data collected from
              any number of successfully processed images when one or more
              errors are encountered with the input processing list. This option
              will cause the application to continue processing all files and
              generate a mapping file if at least one is successful.
            </description>
          </option>
          <option value="FAIL">
            <brief>
              Terminate the application when an error occurs
            </brief>
            <description>
              If any file produces an error when determining mapping statistics,
              ONERROR=FAIL will cause the application to abort with an error
              without generating a map file. The filename and detailed error are
              written to ERRORLOG if provided. The filename alone is written to
              ERRORLIST if provided.
            </description>
          </option>
        </list>
      </parameter>
      <parameter name="ERRORLOG">
        <type>filename</type>
        <fileMode>output</fileMode>
        <internalDefault>None</internalDefault>
        <brief>
          Writes detailed information for each failed file to a PVL file
        </brief>
        <description>
            Each file that encounters an error will produce the issue
            that cased the error. The ERRORLOG file can be specified to
            get a detailed account of why each file failed to successfully
            complete. Data collected from this process will be written
            to the specified ERRORLOG in the form of a PVL object.
        </description>
      </parameter>

      <parameter name="ERRORLIST">
        <type>filename</type>
        <fileMode>output</fileMode>
        <internalDefault>None</internalDefault>
        <brief>
          Record filename of each cube that failed to this file
        </brief>
        <description>
            The filename of each cube that encountered an error will be
            written to this file if provided. The ERRORLIST file can be
            used to difference against the input list to determine all
            the successful files included in the mapping statistics.
        </description>
      </parameter>
    </group>
  </groups>
</application>
Loading