Unverified Commit 0bc9a43f authored by Kelvin Rodriguez's avatar Kelvin Rodriguez Committed by GitHub
Browse files

added mappt coordlist functionality (#3905)

* added mappt coordlist functionality

* include guards

* more copy paste errors

* uuuuuuuuuggggghhhh

* reverted campt.xml

* updated docs

* added flatfile output and associated tests

* addressing Kristin's comment

* left in group selection

* removed couts
parent a1e724c9
Loading
Loading
Loading
Loading
+150 −84
Original line number Diff line number Diff line
@@ -9,12 +9,18 @@
#include "TProjection.h"
#include "ProjectionFactory.h"
#include "SpecialPixel.h"
#include "CSVReader.h"

#include "mappt.h"

using namespace std;


namespace Isis {

QList< QPair<double, double> > getMapPoints(const UserInterface &ui, bool usePointList);
PvlGroup getProjPointInfo(Cube *icube, QPair<double, double> point, UserInterface &ui, Pvl *log);
  
void mappt(UserInterface &ui, Pvl *log) {
  Cube *cube = new Cube();
  CubeAttributeInput inAtt = ui.GetInputAttribute("FROM");
@@ -29,18 +35,92 @@ void mappt(UserInterface &ui, Pvl *log) {

void mappt(Cube *icube, UserInterface &ui, Pvl *log, CubeAttributeInput* inAtt) {
  // Open the input cube and initialize the projection
  TProjection *proj = (TProjection *) icube->projection();
  
  QList<QPair<double, double>> points = getMapPoints(ui, ui.WasEntered("COORDLIST"));
   
  if(log) {
    for(int i = 0; i < points.size(); i++) {
      log->addGroup(getProjPointInfo(icube, points[i], ui, log));
    } 
  }

  // Write an output label file if necessary
  if(ui.WasEntered("TO")) {
    // Get user params from ui
    QString outFile = FileName(ui.GetFileName("TO")).expanded();
    bool exists = FileName(outFile).fileExists();
    bool append = ui.GetBoolean("APPEND");

    // Write the pvl group out to the file
    if(ui.GetString("FORMAT") == "PVL") {
      if(append) {
        log->append(outFile);
      }
      else {
        log->write(outFile);
      }
    }

    // Create a flatfile of the same data
    // The flatfile is comma delimited and can be imported into Excel
    else {
      ofstream os;
      bool writeHeader = false;
      if(append) {
        os.open(outFile.toLatin1().data(), ios::app);
        if(!exists) {
          writeHeader = true;
        }
      }
      else {
        os.open(outFile.toLatin1().data(), ios::out);
        writeHeader = true;
      }
      
      PvlGroup fResult = log->group(0); 

      if(writeHeader) {
        for(int i = 0; i < fResult.keywords(); i++) {
          os << fResult[i].name();

          if(i < fResult.keywords() - 1) {
            os << ",";
          }
        }
        os << endl;
      }
      
      for(int i = 0; i < log->groups(); i++) {
        PvlGroup group = log->group(i);
        for(int j = 0; j < group.keywords(); j++) {
          os << (QString)group[j];
          if(j < group.keywords() - 1) {
            os << ",";
          }
        }
        os << endl;    
      } // end of keyword loop
    } // end of group loop 
  
  }
  else if(ui.GetString("FORMAT") == "FLAT") {
    QString msg = "Flat file must have a name.";
    throw IException(IException::User, msg, _FILEINFO_);
  }
}


PvlGroup getProjPointInfo(Cube *icube, QPair<double, double> point, UserInterface &ui, Pvl *log) { 
  // Get the coordinate
  bool outsideAllowed = ui.GetBoolean("ALLOWOUTSIDE");
  int cubeLineLimit = icube->lineCount() + .5;
  int cubeSampleLimit = icube->sampleCount() + .5;

  TProjection *proj = (TProjection *) icube->projection();
  // Get the sample/line position if we have an image point
  if(ui.GetString("TYPE") == "IMAGE") {
    double samp = ui.GetDouble("SAMPLE");
    double line = ui.GetDouble("LINE");
    double samp = point.first;
    double line = point.second;

    if (!outsideAllowed) {
      if (samp < .5 || line < .5 || samp > cubeSampleLimit || line > cubeLineLimit) {
@@ -53,8 +133,8 @@ void mappt(Cube *icube, UserInterface &ui, Pvl *log, CubeAttributeInput* inAtt)

  // Get the lat/lon position if we have a ground point
  else if(ui.GetString("TYPE") == "GROUND") {
    double lat = ui.GetDouble("LATITUDE");
    double lon = ui.GetDouble("LONGITUDE");
    double lat = point.first;
    double lon = point.second;

    // Make sure we have a valid latitude value
    if(fabs(lat) > 90.0) {
@@ -135,8 +215,8 @@ void mappt(Cube *icube, UserInterface &ui, Pvl *log, CubeAttributeInput* inAtt)

  // Get the x/y position if we have a projection point
  else {
    double x = ui.GetDouble("X");
    double y = ui.GetDouble("Y");
    double x = point.first;
    double y = point.second;
    proj->SetCoordinate(x, y);
  }

@@ -161,10 +241,13 @@ void mappt(Cube *icube, UserInterface &ui, Pvl *log, CubeAttributeInput* inAtt)
  icube->read(b);
  
  QString filterName = "Null";

  if ( icube->label()->findObject("IsisCube").hasGroup("BandBin")) {
    PvlGroup bandBin = icube->label()->findObject("IsisCube").findGroup("BandBin");
    if (bandBin.hasKeyword("FilterName")) {
        filterName = bandBin.findKeyword("FilterName")[0];
    }
  }

  // Log the position
  if(proj->IsGood()) {
@@ -202,6 +285,7 @@ void mappt(Cube *icube, UserInterface &ui, Pvl *log, CubeAttributeInput* inAtt)
                 toString(proj->To180Domain(proj->ToPositiveEast(
                            proj->UniversalLongitude(), 360))));


    // Input map coordinate system location
    // Latitude
    if(proj->IsPlanetocentric()) {
@@ -268,45 +352,7 @@ void mappt(Cube *icube, UserInterface &ui, Pvl *log, CubeAttributeInput* inAtt)
      }
    }
    
    if (log) {
      log->addGroup(results);
    }

    // Write an output label file if necessary
    if(ui.WasEntered("TO")) {
      // Get user params from ui
      QString outFile = FileName(ui.GetFileName("TO")).expanded();
      bool exists = FileName(outFile).fileExists();
      bool append = ui.GetBoolean("APPEND");

      // Write the pvl group out to the file
      if(ui.GetString("FORMAT") == "PVL") {
        Pvl temp;
        temp.addGroup(results);
        if(append) {
          temp.append(outFile);
        }
        else {
          temp.write(outFile);
        }
      }

      // Create a flatfile of the same data
      // The flatfile is comma delimited and can be imported into Excel
      else {
        ofstream os;
        bool writeHeader = false;
        if(append) {
          os.open(outFile.toLatin1().data(), ios::app);
          if(!exists) {
            writeHeader = true;
          }
        }
        else {
          os.open(outFile.toLatin1().data(), ios::out);
          writeHeader = true;
        }

    if (ui.GetString("FORMAT") == "FLAT") {
      // Rearrange the order of the lat/lons for the csv
      results.deleteKeyword( pE360.name() );
      results.deleteKeyword( pE180.name() );
@@ -321,36 +367,56 @@ void mappt(Cube *icube, UserInterface &ui, Pvl *log, CubeAttributeInput* inAtt)
      results += pE180;
      results += pW360;
      results += pW180;

        if(writeHeader) {
          for(int i = 0; i < results.keywords(); i++) {
            os << results[i].name();

            if(i < results.keywords() - 1) {
              os << ",";
    }
  }
          os << endl;

  return results; 
}

        for(int i = 0; i < results.keywords(); i++) {
          os << (QString)results[i];

          if(i < results.keywords() - 1) {
            os << ",";
          }
QList< QPair<double, double> > getMapPoints(const UserInterface &ui, bool usePointList) {
    double point1 = 0.0;
    double point2 = 0.0;
    QList< QPair<double, double> > points;
    QString pointType = ui.GetString("TYPE");

    // Check if the provided coordinate list is valid, i.e. a Samp/Line or Lat/Long coordinate per row
    if (usePointList) {

      CSVReader reader;
      reader.read(FileName(ui.GetFileName("COORDLIST")).expanded());

      if (!reader.isTableValid(reader.getTable()) || reader.columns() != 2) {
        QString msg = "Coordinate file formatted incorrectly.\n"
                      "Each row must have two columns: a sample,line or a latitude,longitude pair.";
        throw IException(IException::User, msg, _FILEINFO_);
      }
        os << endl;
      for (int row = 0; row < reader.rows(); row++) {
        point1 = toDouble(reader.getRow(row)[0]);
        point2 = toDouble(reader.getRow(row)[1]);
        points.append(QPair<double, double>(point1, point2));
      }
    }
    else if(ui.GetString("FORMAT") == "FLAT") {
      QString msg = "Flat file must have a name.";
      throw IException(IException::User, msg, _FILEINFO_);
    // Grab the coordinate from the ui position parameters if no coordinate list is provided
    else {
      if (pointType == "IMAGE") {
        if (ui.WasEntered("SAMPLE"))
          point1 = ui.GetDouble("SAMPLE");
        if (ui.WasEntered("LINE"))
          point2 = ui.GetDouble("LINE");
      }
      else if (pointType == "GROUND") {
        point1 = ui.GetDouble("LATITUDE");
        point2 = ui.GetDouble("LONGITUDE");
      }
      else {
    QString msg = "Could not project requested position";
    throw IException(IException::Unknown, msg, _FILEINFO_);
        // Projection type selected
        point1 = ui.GetDouble("X");
        point2 = ui.GetDouble("Y");
      }
      points.append(QPair<double, double>(point1, point2));
    }
    
    return points;
}
}
+2 −2
Original line number Diff line number Diff line
#ifndef campt_h
#define campt_h
#ifndef mappt_h
#define mappt_h

#include "Pvl.h"
#include "UserInterface.h"
+69 −13
Original line number Diff line number Diff line
@@ -178,10 +178,6 @@
              This option interprets the coordinate as sample/line and will
              compute latitude/longitude and x/y
            </description>
            <inclusions>
              <item>SAMPLE</item>
              <item>LINE</item>
            </inclusions>
            <exclusions>
              <item>LATITUDE</item>
              <item>LONGITUDE</item>
@@ -200,11 +196,6 @@
              This option interprets the coordinate as latitude/longitude and will
              compute sample/line and x/y
            </description>
            <inclusions>
              <item>LATITUDE</item>
              <item>LONGITUDE</item>
              <item>COORDSYS</item>
            </inclusions>
            <exclusions>
              <item>SAMPLE</item>
              <item>LINE</item>
@@ -218,10 +209,6 @@
              This option interprets the coordinate as x/y and will
              compute sample/line and latitude/longitude
            </description>
            <inclusions>
              <item>X</item>
              <item>Y</item>
            </inclusions>
            <exclusions>
              <item>SAMPLE</item>
              <item>LINE</item>
@@ -308,6 +295,75 @@
        </description>
      </parameter>

      <parameter name = "USECOORDLIST">
        <type>boolean</type>
        <default><item>FALSE</item></default>
        <brief>
          Enable coordinate list parameters in the GUI
        </brief>
        <description>
          Selecting this parameter will enable the 'COORDLIST' parameter in the
          GUI.
          <p>
            Note that this parameter is optional when running <i>mappt</i> on the
            command line.
          </p>
        </description>
        <inclusions>
          <item>COORDLIST</item>
          <item>TYPE</item>
        </inclusions>
        <exclusions>
          <item>TYPE</item>
          <item>SAMPLE</item>
          <item>X</item>
          <item>Y</item>
          <item>LINE</item>
          <item>LATITUDE</item>
          <item>LONGITUDE</item>
        </exclusions>
      </parameter>

      <parameter name="COORDLIST">
        <type>filename</type>
        <fileMode>input</fileMode>
        <brief>
          Input comma-delimited file of image or ground coordinates
        </brief>
        <internalDefault>None</internalDefault>
        <description>
          An input comma-delimited flatfile of image, ground or projection coordinates.
          This allows the program to process multiple coordinates in a single run of <i>mappt</i>,
          and output the results to the specified output file.
          <p>
            Expected order for image coordinates: sample, line<br/>
            Expected order for ground coordinates: latitude, longitude
            Expected order for projection coordinates: X, Y
          </p>
          <p>
            The "Error" keyword will only appear when using a coordinate list. The value of "Error"
            will then be NULL unless an error occurs during the processing of a coordinate in the
            coordinate list. Then, the value of this keyword will be the error message. This allows
            for <i>mappt</i> to continue processing the remaining coordinates in the coordinate
            list.
          </p>
          <p>
            For the input of Latitude and Longitude, mappt expects
            <def>Planetocentric Latitude</def> (within -90 to 90 boundary) and
            <def>Positive East Longitude</def> (with 0-360 boundary) to find the location in the
            input camera image.
          </p>
        </description>
        <exclusions>
          <item>SAMPLE</item>
          <item>LINE</item>
          <item>X</item>
          <item>Y</item>
          <item>LATITUDE</item>
          <item>LONGITUDE</item>
        </exclusions>
      </parameter>

      <parameter name="ALLOWOUTSIDE">
        <type>boolean</type>
        <brief>
+168 −1
Original line number Diff line number Diff line
@@ -121,6 +121,7 @@ TEST_F(DefaultCube, FunctionalTestMapptFlatFileTest) {
  UserInterface options(APP_XML, args);
  Pvl appLog;
  mappt(projTestCube, options, &appLog);

  PvlGroup mapPoint = appLog.findGroup("Results");
  
  int lineNumber = 0;
@@ -166,6 +167,9 @@ TEST_F(DefaultCube, FunctionalTestMapptFlatFileTest) {
      lineNumber++;
    }
  }
  else {
    FAIL() << "FAILED TO OPEN FLATFILE";
  }
}

TEST_F(DefaultCube, FunctionalTestMapptAllowOutside) {
@@ -196,3 +200,166 @@ TEST_F(DefaultCube, FunctionalTestMapptBandTest) {
  EXPECT_PRED_FORMAT2(AssertQStringsEqual, mapPoint.findKeyword("FilterName"), "NIR");
  EXPECT_EQ( (double) mapPoint.findKeyword("Band"), 2);
}


TEST_F(DefaultCube, FunctionalTestMapptImageCoordList) {
  std::ofstream of;
  of.open(tempDir.path().toStdString()+"/coords.txt");
  of << "1, 1\n2, 2\n 3, 3";
  of.close();
  
  QVector<QString> args = {"coordlist="+tempDir.path()+"/coords.txt",
                           "UseCoordList=True", 
                           "append=false",
                           "type=image"};
  UserInterface options(APP_XML, args);

  Pvl appLog; 
  mappt(projTestCube, options, &appLog);
  
  PvlGroup mapPoint = appLog.group(0);

  EXPECT_PRED_FORMAT2(AssertQStringsEqual, mapPoint.findKeyword("FileName"), projTestCube->fileName());
  EXPECT_PRED_FORMAT2(AssertQStringsEqual, mapPoint.findKeyword("FilterName"), "CLEAR");
  EXPECT_EQ( (double) mapPoint.findKeyword("Band"), 1);
  EXPECT_NEAR( (double) mapPoint.findKeyword("Sample"), 1, 1e-8);
  EXPECT_NEAR( (double) mapPoint.findKeyword("Line"), 1, 1e-8);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PlanetographicLatitude"), 9.3870849567571);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PlanetocentricLatitude"), 9.2788326719634);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PositiveWest360Longitude"), 359.14528612684);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PositiveEast360Longitude"), 0.85471387315749);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PositiveEast180Longitude"), 0.85471387315749);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PositiveWest180Longitude"), -0.85471387315751); 
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("x"), 50000);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("y"), 550000); 
  
  mapPoint = appLog.group(1);

  EXPECT_PRED_FORMAT2(AssertQStringsEqual, mapPoint.findKeyword("FileName"), projTestCube->fileName());
  EXPECT_PRED_FORMAT2(AssertQStringsEqual, mapPoint.findKeyword("FilterName"), "CLEAR");
  EXPECT_EQ( (double) mapPoint.findKeyword("Band"), 1);
  EXPECT_NEAR( (double) mapPoint.findKeyword("Sample"), 2, 1e-8);
  EXPECT_NEAR( (double) mapPoint.findKeyword("Line"), 2, 1e-8);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PlanetographicLatitude"), 7.6808677548562);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PlanetocentricLatitude"), 7.5917721861518);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PositiveWest360Longitude"), 357.44703128109);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PositiveEast360Longitude"), 2.5529687189083);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PositiveEast180Longitude"), 2.5529687189083);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PositiveWest180Longitude"), -2.5529687189083); 
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("x"), 150000);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("y"), 450000);
  
  mapPoint = appLog.group(2);

  EXPECT_PRED_FORMAT2(AssertQStringsEqual, mapPoint.findKeyword("FileName"), projTestCube->fileName());
  EXPECT_PRED_FORMAT2(AssertQStringsEqual, mapPoint.findKeyword("FilterName"), "CLEAR");
  EXPECT_EQ( (double) mapPoint.findKeyword("Band"), 1);
  EXPECT_NEAR( (double) mapPoint.findKeyword("Sample"), 3, 1e-8);
  EXPECT_NEAR( (double) mapPoint.findKeyword("Line"), 3, 1e-8);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PlanetographicLatitude"), 5.9743363392284);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PlanetocentricLatitude"), 5.9047117003403);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PositiveWest360Longitude"), 355.75985208984);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PositiveEast360Longitude"), 4.2401479101647);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PositiveEast180Longitude"), 4.2401479101647);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("PositiveWest180Longitude"), -4.2401479101646); 
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("x"), 250000);
  EXPECT_DOUBLE_EQ( (double) mapPoint.findKeyword("y"), 350000.0);

}

TEST_F(DefaultCube, FunctionalTestMapptCoordListFlatFile) {
  std::ofstream of;
  of.open(tempDir.path().toStdString()+"/coords.txt");
  of << "1, 1\n2, 2\n 3, 3";
  of.close();
   
  QFile flatFile(tempDir.path() + "/testOut.txt");
  QVector<QString> args = {"coordlist="+tempDir.path()+"/coords.txt","to=" + flatFile.fileName(), 
                           "UseCoordList=True", 
                           "append=false", "format=flat",
                           "type=image"};
  UserInterface options(APP_XML, args);

  Pvl appLog; 
  mappt(projTestCube, options, &appLog);
  
  int lineNumber = 0;
  QTextStream flatStream(&flatFile);
  
  PvlGroup mapPoint = appLog.group(0);

  if (flatFile.open(QIODevice::ReadOnly)) {
    while(!flatStream.atEnd()) {
      QString line = flatStream.readLine();
      QStringList fields = line.split(",");
      
      if(lineNumber == 0) {
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(0), "Filename");
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(1), "Sample");
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(2), "Line");
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(3), "Band");
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(4), "FilterName");
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(5), "PixelValue");
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(6), "X");
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(7), "Y");
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(8), "PlanetocentricLatitude");
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(9), "PlanetographicLatitude");
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(10), "PositiveEast360Longitude");
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(11), "PositiveEast180Longitude");
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(12), "PositiveWest360Longitude");
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(13), "PositiveWest180Longitude");
      }
      else {
        mapPoint = appLog.group(lineNumber-1); 
        
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(0), mapPoint.findKeyword("FileName"));
        EXPECT_DOUBLE_EQ(fields.value(1).toDouble(), mapPoint.findKeyword("Sample"));
        EXPECT_DOUBLE_EQ(fields.value(2).toDouble(), mapPoint.findKeyword("Line"));
        EXPECT_DOUBLE_EQ(fields.value(3).toDouble(), mapPoint.findKeyword("Band"));
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(4), mapPoint.findKeyword("FilterName"));
        EXPECT_PRED_FORMAT2(AssertQStringsEqual, fields.value(5), mapPoint.findKeyword("PixelValue"));
        EXPECT_DOUBLE_EQ(fields.value(6).toDouble(), mapPoint.findKeyword("X"));
        EXPECT_DOUBLE_EQ(fields.value(7).toDouble(), mapPoint.findKeyword("Y"));
        EXPECT_DOUBLE_EQ(fields.value(8).toDouble(), mapPoint.findKeyword("PlanetocentricLatitude"));
        EXPECT_DOUBLE_EQ(fields.value(9).toDouble(), mapPoint.findKeyword("PlanetographicLatitude") );
        EXPECT_DOUBLE_EQ(fields.value(10).toDouble(), mapPoint.findKeyword("PositiveEast360Longitude"));
        EXPECT_DOUBLE_EQ(fields.value(11).toDouble(), mapPoint.findKeyword("PositiveEast180Longitude"));
        EXPECT_DOUBLE_EQ(fields.value(12).toDouble(), mapPoint.findKeyword("PositiveWest360Longitude"));
        EXPECT_DOUBLE_EQ(fields.value(13).toDouble(), mapPoint.findKeyword("PositiveWest180Longitude"));
      }

      lineNumber++;
    }
  }
  else {
    FAIL() << "FAILED TO OPEN FLATFILE";
  }
}

TEST_F(DefaultCube, FunctionalTestMapptBadColumnError) {
  std::ofstream of;
  of.open(tempDir.path().toStdString()+"/coords.txt");
  of << "1, 1\n2\n 3, 3";
  of.close();
  
  QVector<QString> args = {"coordlist="+tempDir.path()+"/coords.txt",
                           "UseCoordList=True", 
                           "append=false",
                           "type=image"};

  UserInterface options(APP_XML, args);
  Pvl appLog;
  try {
    mappt(projTestCube, options, &appLog);
    FAIL() << "Expected an exception to be thrown";
  }
  catch(IException &e) {
    EXPECT_TRUE(e.toString().toLatin1().contains("Coordinate file formatted incorrectly."))
      << e.toString().toStdString();
  }
  catch(...) {
    FAIL() << "Expected an IException with message: \"Coordinate file formatted incorrectly.\n"
              "Each row must have two columns: a sample,line or a latitude,longitude pair.\"";
  }

}