From 93bc3c7441f29e47989b66feaa6270037a5afe1b Mon Sep 17 00:00:00 2001
From: Kristin <kberry@usgs.gov>
Date: Wed, 3 Oct 2018 09:59:54 -0700
Subject: [PATCH] UsgsAstroFramePlugin refactor, part one (#117)

* Initial updates for refactoring the UsgsAstroFramePlugin

* Jacobian1 test now passing

* All coefficients test now passing

* More testing passing

* Down to 6 failing tests

* Fixed failing position-offset tests

* Update to strip out full-paths and also we're down to one failing test

* Fixed last failing test by updating canModelBeConstructedFromIsd to load the json ISD into a csm::Isd object if it has not already been loaded

* Updated based on review comments
---
 include/usgscsm/UsgsAstroFramePlugin.h |   2 +
 src/UsgsAstroFramePlugin.cpp           |  70 ++-
 tests/FrameCameraTests.cpp             | 754 +++++++++----------------
 tests/TestyMcTestFace.cpp              |  23 +-
 4 files changed, 336 insertions(+), 513 deletions(-)

diff --git a/include/usgscsm/UsgsAstroFramePlugin.h b/include/usgscsm/UsgsAstroFramePlugin.h
index e15ee68..8b6f57e 100644
--- a/include/usgscsm/UsgsAstroFramePlugin.h
+++ b/include/usgscsm/UsgsAstroFramePlugin.h
@@ -45,6 +45,8 @@ class UsgsAstroFramePlugin : public csm::Plugin {
     // TODO when implementing, add any other necessary members.
 
 private:
+    csm::Isd loadImageSupportData(const csm::Isd &imageSupportData) const; 
+
     static const UsgsAstroFramePlugin m_registeredPlugin;
     static const std::string _PLUGIN_NAME;
     static const std::string _MANUFACTURER_NAME;
diff --git a/src/UsgsAstroFramePlugin.cpp b/src/UsgsAstroFramePlugin.cpp
index 4641cd2..74cf266 100644
--- a/src/UsgsAstroFramePlugin.cpp
+++ b/src/UsgsAstroFramePlugin.cpp
@@ -3,6 +3,10 @@
 
 #include <cstdlib>
 #include <string>
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <map> 
 
 #include <csm.h>
 #include <Error.h>
@@ -181,7 +185,8 @@ bool UsgsAstroFramePlugin::canModelBeConstructedFromState(const std::string &mod
       modelName != UsgsAstroFrameSensorModel::_SENSOR_MODEL_NAME){
           constructible = false;
       }
-  // Check that the necessary keys are there (this does not chek values at all.)
+
+  // Check that the necessary keys are there (this does not check values at all.)
   auto state = json::parse(modelState);
   for(auto &key : _STATE_KEYWORD){
       if (state.find(key) == state.end()){
@@ -273,18 +278,65 @@ return sensor_model;
 }
 
 
-csm::Model *UsgsAstroFramePlugin::constructModelFromISD(const csm::Isd &imageSupportData,
+// This function takes a csm::Isd which only has the image filename set. It uses this filename to
+// find a metadata json file loacated alongside the image file. It creates and returns new csm::Isd
+// with its parameters populated by the metadata file. 
+csm::Isd UsgsAstroFramePlugin::loadImageSupportData(const csm::Isd &imageSupportDataOriginal) const{
+  // Get image location from the input csm::Isd: 
+  std::string imageFilename = imageSupportDataOriginal.filename(); 
+  
+  // Load 'sidecar' ISD file
+  size_t lastIndex = imageFilename.find_last_of("."); 
+  std::string baseName = imageFilename.substr(0, lastIndex); 
+  std::string isdFilename = baseName.append(".json");
+
+  csm::Isd imageSupportData(isdFilename);
+  imageSupportData.clearAllParams();
+
+  try {
+    std::ifstream isdFile(isdFilename); 
+    json jsonIsd = json::parse(isdFile);
+     for (json::iterator it = jsonIsd.begin(); it != jsonIsd.end(); ++it) {
+      if (it.value().size() >1 ) {
+        std::vector<double> v = it.value();
+        for (int j=0;j < v.size(); j++) {
+          std::ostringstream val;
+          val << std::setprecision(15) << v[j];
+          imageSupportData.addParam(it.key(),val.str());
+        }
+      }
+      else {
+        imageSupportData.addParam(it.key(), it.value().dump());
+      }
+    }
+    isdFile.close(); 
+  } catch (...) {
+    std::string errorMessage = "Could not read metadata file associated with image: ";
+    errorMessage.append(isdFilename);
+    throw csm::Error(csm::Error::FILE_READ, errorMessage, 
+                     "UsgsAstroFramePlugin::loadImageSupportData"); 
+  }
+
+  return imageSupportData; 
+}
+
+csm::Model *UsgsAstroFramePlugin::constructModelFromISD(const csm::Isd &imageSupportDataOriginal,
                                               const std::string &modelName,
                                               csm::WarningList *warnings) const {
 
+  csm::Isd imageSupportData = loadImageSupportData(imageSupportDataOriginal); 
+
+  // FIXME: Check needs to be updated to use new JSON isd spec
   // Check if the sensor model can be constructed from ISD given the model name
   if (!canModelBeConstructedFromISD(imageSupportData, modelName)) {
-    throw csm::Error(csm::Error::ISD_NOT_SUPPORTED,
+      throw csm::Error(csm::Error::ISD_NOT_SUPPORTED,
                      "Sensor model support data provided is not supported by this plugin",
-                     "UsgsAstroFramePlugin::constructModelFromISD");
+                       "UsgsAstroFramePlugin::constructModelFromISD");
   }
-  UsgsAstroFrameSensorModel *sensorModel = new UsgsAstroFrameSensorModel();
 
+  // Create the empty sensorModel
+  UsgsAstroFrameSensorModel *sensorModel = new UsgsAstroFrameSensorModel();
+  
   // Keep track of necessary keywords that are missing from the ISD.
   std::vector<std::string> missingKeywords;
 
@@ -375,7 +427,6 @@ csm::Model *UsgsAstroFramePlugin::constructModelFromISD(const csm::Isd &imageSup
   sensorModel->m_odtY[8] = atof(imageSupportData.param("odt_y", 8).c_str());
   sensorModel->m_odtY[9] = atof(imageSupportData.param("odt_y", 9).c_str());
 
-
   sensorModel->m_ccdCenter[0] = atof(imageSupportData.param("ccd_center", 0).c_str());
   sensorModel->m_ccdCenter[1] = atof(imageSupportData.param("ccd_center", 1).c_str());
 
@@ -537,9 +588,14 @@ bool UsgsAstroFramePlugin::canISDBeConvertedToModelState(const csm::Isd &imageSu
       convertible = false;
   }
 
+  csm::Isd localImageSupportData = imageSupportData; 
+  if (imageSupportData.parameters().empty()) {
+    localImageSupportData = loadImageSupportData(imageSupportData); 
+  }
+
   std::string value;
   for(auto &key : _ISD_KEYWORD){
-      value = imageSupportData.param(key);
+      value = localImageSupportData.param(key);
       if (value.empty()){
           convertible = false;
       }
diff --git a/tests/FrameCameraTests.cpp b/tests/FrameCameraTests.cpp
index 454d663..27bdb6e 100644
--- a/tests/FrameCameraTests.cpp
+++ b/tests/FrameCameraTests.cpp
@@ -19,14 +19,13 @@ class FramerParameterizedTest : public ::testing::TestWithParam<csm::ImageCoord>
 protected:
   csm::Isd isd;
 
-
       void printIsd(csm::Isd &localIsd) {
            multimap<string,string> isdmap= localIsd.parameters();
            for (auto it = isdmap.begin(); it != isdmap.end();++it){
 
                       cout << it->first << " : " << it->second << endl;
            }
-       }
+      }
       UsgsAstroFrameSensorModel* createModel(csm::Isd &modifiedIsd) {
 
         UsgsAstroFramePlugin frameCameraPlugin;
@@ -39,118 +38,56 @@ protected:
           return sensorModel;
         else
           return nullptr;
-
-
       }
 
 
-
    virtual void SetUp() {
-
-
-      std::ifstream isdFile("data/simpleFramerISD.json");
-      json jsonIsd = json::parse(isdFile);
-      for (json::iterator it = jsonIsd.begin(); it != jsonIsd.end(); ++it) {
-         json jsonValue = it.value();
-         if (jsonValue.size() > 1) {
-            for (int i = 0; i < jsonValue.size(); i++) {
-               isd.addParam(it.key(), jsonValue[i].dump());
-            }
-         }
-         else {
-            isd.addParam(it.key(), jsonValue.dump());
-         }
-      }
-      isdFile.close();
-   }
+     isd.setFilename("data/simpleFramerISD.img");
+   };
 };
 
 
-
-
 class FrameIsdTest : public ::testing::Test {
-   protected:
-
-      csm::Isd isd;
-
-      void printIsd(csm::Isd &localIsd) {
-           multimap<string,string> isdmap= localIsd.parameters();
-           for (auto it = isdmap.begin(); it != isdmap.end();++it){
-
-                      cout << it->first << " : " << it->second << endl;
-           }
-       }
-      UsgsAstroFrameSensorModel* createModel(csm::Isd &modifiedIsd) {
-
-        UsgsAstroFramePlugin frameCameraPlugin;
-        csm::Model *model = frameCameraPlugin.constructModelFromISD(
+  protected:
+    csm::Isd isd;
+    void printIsd(csm::Isd &localIsd) {
+      multimap<string,string> isdmap= localIsd.parameters();
+      for (auto it = isdmap.begin(); it != isdmap.end();++it){
+        cout << it->first << " : " << it->second << endl;
+      }
+    }
+    UsgsAstroFrameSensorModel* createModel(csm::Isd &modifiedIsd) {
+      UsgsAstroFramePlugin frameCameraPlugin;
+      csm::Model *model = frameCameraPlugin.constructModelFromISD(
               modifiedIsd,"USGS_ASTRO_FRAME_SENSOR_MODEL");
-
-        UsgsAstroFrameSensorModel* sensorModel = dynamic_cast<UsgsAstroFrameSensorModel *>(model);
-
-        if (sensorModel)
-          return sensorModel;
-        else
-          return nullptr;
-
+      UsgsAstroFrameSensorModel* sensorModel = dynamic_cast<UsgsAstroFrameSensorModel *>(model);
+      if (sensorModel)
+        return sensorModel;
+      else
+        return nullptr;
       }
 
 
-
-   virtual void SetUp() {
-      std::ifstream isdFile("data/simpleFramerISD.json");
-      json jsonIsd = json::parse(isdFile);
-      for (json::iterator it = jsonIsd.begin(); it != jsonIsd.end(); ++it) {
-         json jsonValue = it.value();
-         if (jsonValue.size() > 1) {
-            for (int i = 0; i < jsonValue.size(); i++) {
-               isd.addParam(it.key(), jsonValue[i].dump());
-            }
-         }
-         else {
-            isd.addParam(it.key(), jsonValue.dump());
-         }
-      }
-      isdFile.close();
+    virtual void SetUp() {
+      isd.setFilename("data/simpleFramerISD.img");
    }
 };
 
 class FrameSensorModel : public ::testing::Test {
    protected:
-
-
       protected :
+      csm::Isd isd;
       UsgsAstroFrameSensorModel *sensorModel;
 
-
-
       void SetUp() override {
          sensorModel = NULL;
-         std::ifstream isdFile("data/simpleFramerISD.json");
-         json jsonIsd = json::parse(isdFile);
-         csm::Isd isd;
-         for (json::iterator it = jsonIsd.begin(); it != jsonIsd.end(); ++it) {
-            json jsonValue = it.value();
-            if (jsonValue.size() > 1) {
-              for (int i = 0; i < jsonValue.size(); i++){
-                    isd.addParam(it.key(), jsonValue[i].dump());
-               }
-            }
-            else {
-               isd.addParam(it.key(), jsonValue.dump());
-            }
-         }
-
-         isdFile.close();
 
+         isd.setFilename("data/simpleFramerISD.img");
          UsgsAstroFramePlugin frameCameraPlugin;
-
          csm::Model *model = frameCameraPlugin.constructModelFromISD(
                isd,
                "USGS_ASTRO_FRAME_SENSOR_MODEL");
-
          sensorModel = dynamic_cast<UsgsAstroFrameSensorModel *>(model);
-
          ASSERT_NE(sensorModel, nullptr);
       }
 
@@ -159,64 +96,39 @@ class FrameSensorModel : public ::testing::Test {
             delete sensorModel;
             sensorModel = NULL;
          }
-
       }
 };
 
-
-
 INSTANTIATE_TEST_CASE_P(JacobianTest,FramerParameterizedTest,
                         ::testing::Values(csm::ImageCoord(2.5,2.5),csm::ImageCoord(7.5,7.5)));
 
 TEST_P(FramerParameterizedTest,JacobianTest) {
 
-  int precision  = 20;
-  std:string odtx_key= "odt_x";
-  std::string odty_key="odt_y";
-
-  isd.clearParams(odty_key);
-  isd.clearParams(odtx_key);
-
-
-  vector<double> odtx{1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0};
-  vector<double> odty{0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0};
-
-
-   for (auto & val: odtx){
-      ostringstream strval;
-      strval << setprecision(precision) << val;
-      isd.addParam("odt_x", strval.str());
-   }
-   for (auto & val: odty){
-      ostringstream strval;
-      strval << setprecision(precision) << val;
-      isd.addParam("odt_y", strval.str());
-   }
-
    UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+   std::string modelState = sensorModel->getModelState(); 
+   auto state = json::parse(modelState);
 
+   state["m_odtX"] = {1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0};
+   state["m_odtY"] = {0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0};
+   sensorModel->replaceModelState(state.dump()); 
+   
    double Jxx,Jxy,Jyx,Jyy;
-
    ASSERT_NE(sensorModel, nullptr);
 
    csm::ImageCoord imagePt1 = GetParam();
    cout << "[" << imagePt1.samp << "," << imagePt1.line << "]"<< endl;
    sensorModel->distortionJacobian(imagePt1.samp, imagePt1.line, Jxx, Jxy,Jyx,Jyy);
 
-
    double determinant = fabs(Jxx*Jyy - Jxy*Jyx);
-
    EXPECT_GT(determinant,1e-3);
 
    delete sensorModel;
    sensorModel=NULL;
-
-
 }
 
-//NOTE: The imagePt format is (Lines,Samples)
+// NOTE: The imagePt format is (Lines,Samples)
 
-//centered and slightly off-center:
+// centered and slightly off-center:
 TEST_F(FrameSensorModel, Center) {
    csm::ImageCoord imagePt(7.5, 7.5);
    csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
@@ -232,7 +144,7 @@ TEST_F(FrameSensorModel, SlightlyOffCenter) {
    EXPECT_NEAR(groundPt.z, 1.98039612, 1e-8);
 }
 
-//Test all four corners:
+// Test all four corners:
 TEST_F(FrameSensorModel, OffBody1) {
    csm::ImageCoord imagePt(15.0, 0.0);
    csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
@@ -265,343 +177,236 @@ TEST_F(FrameSensorModel, OffBody4) {
 
 
 TEST_F(FrameIsdTest, setFocalPlane1) {
-  int precision  = 20;
-  std:string odtx_key= "odt_x";
-  std::string odty_key="odt_y";
-
-  isd.clearParams(odty_key);
-  isd.clearParams(odtx_key);
-
   csm::ImageCoord imagePt(7.5, 7.5);
   double ux,uy;
 
-  vector<double> odtx{0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
-  vector<double> odty{0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
+  UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
 
+  std::string modelState = sensorModel->getModelState(); 
+  auto state = json::parse(modelState);
 
-   for (auto & val: odtx){
-      ostringstream strval;
-      strval << setprecision(precision) << val;
-      isd.addParam("odt_x", strval.str());
-   }
-   for (auto & val: odty){
-      ostringstream strval;
-      strval << setprecision(precision) << val;
-      isd.addParam("odt_y", strval.str());
-   }
-
-   UsgsAstroFrameSensorModel* sensorModel =createModel(isd);
-   ASSERT_NE(sensorModel, nullptr);
-   sensorModel->setFocalPlane(imagePt.samp, imagePt.line, ux, uy);
-   EXPECT_NEAR(imagePt.samp,7.5,1e-8 );
-   EXPECT_NEAR(imagePt.line,7.5,1e-8);   
-   delete sensorModel;
-   sensorModel = NULL;
-
+  state["m_odtX"] = {0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
+  state["m_odtY"] = {0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
+  sensorModel->replaceModelState(state.dump()); 
 
+  ASSERT_NE(sensorModel, nullptr);
+  sensorModel->setFocalPlane(imagePt.samp, imagePt.line, ux, uy);
+  EXPECT_NEAR(imagePt.samp,7.5,1e-8 );
+  EXPECT_NEAR(imagePt.line,7.5,1e-8);   
+  delete sensorModel;
+  sensorModel = NULL;
 }
 
 
-
 TEST_F(FrameIsdTest, Jacobian1) {
+  csm::ImageCoord imagePt(7.5, 7.5);
 
-  int precision  = 20;
-  std:string odtx_key= "odt_x";
-  std::string odty_key="odt_y";
-
-  isd.clearParams(odty_key);
-  isd.clearParams(odtx_key);
-
-   csm::ImageCoord imagePt(7.5, 7.5);
-
-   vector<double> odtx{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0};
-   vector<double> odty{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,1.0};
-
-   for (auto & val: odtx){
-      ostringstream strval;
-      strval << setprecision(precision) << val;
-      isd.addParam("odt_x", strval.str());
-   }
-   for (auto & val: odty){
-      ostringstream strval;
-      strval << setprecision(precision) << val;
-      isd.addParam("odt_y", strval.str());
-   }
-
-   UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
-
-
-   double Jxx,Jxy,Jyx,Jyy;
-
-   ASSERT_NE(sensorModel, nullptr);
-   sensorModel->distortionJacobian(imagePt.samp, imagePt.line, Jxx, Jxy,Jyx,Jyy);
-
-   EXPECT_NEAR(Jxx,56.25,1e-8 );
-   EXPECT_NEAR(Jxy,112.5,1e-8);
-   EXPECT_NEAR(Jyx,56.25,1e-8);
-   EXPECT_NEAR(Jyy,281.25,1e-8);
-
-   delete sensorModel;
-   sensorModel = NULL;
-
+  UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+  std::string modelState = sensorModel->getModelState(); 
+  auto state = json::parse(modelState);
+  state["m_odtX"] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0};
+  state["m_odtY"] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,1.0};
+  sensorModel->replaceModelState(state.dump()); 
+
+  double Jxx,Jxy,Jyx,Jyy;
+
+  ASSERT_NE(sensorModel, nullptr);
+  sensorModel->distortionJacobian(imagePt.samp, imagePt.line, Jxx, Jxy,Jyx,Jyy);
+
+  EXPECT_NEAR(Jxx,56.25,1e-8 );
+  EXPECT_NEAR(Jxy,112.5,1e-8);
+  EXPECT_NEAR(Jyx,56.25,1e-8);
+  EXPECT_NEAR(Jyy,281.25,1e-8);
+  
+  delete sensorModel;
+  sensorModel = NULL;
 }
 
 
 TEST_F(FrameIsdTest, distortMe_AllCoefficientsOne) {
+  csm::ImageCoord imagePt(7.5, 7.5);
 
-  int precision  = 20;
-
-  std:string odtx_key= "odt_x";
-  std::string odty_key="odt_y";
-
-  isd.clearParams(odty_key);
-  isd.clearParams(odtx_key);
-
-   csm::ImageCoord imagePt(7.5, 7.5);
-
-   vector<double> odtx{1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
-   vector<double> odty{1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
-
-   for (auto & val: odtx){
-      ostringstream strval;
-      strval << setprecision(precision) << val;
-      isd.addParam("odt_x", strval.str());
-   }
-   for (auto & val: odty){
-      ostringstream strval;
-      strval << setprecision(precision) << val;
-      isd.addParam("odt_y", strval.str());
-   }
-
-
-   UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
-
-   double dx,dy;
-   ASSERT_NE(sensorModel, nullptr);
-   sensorModel->distortionFunction(imagePt.samp, imagePt.line,dx,dy );
-
-   EXPECT_NEAR(dx,1872.25,1e-8 );
-   EXPECT_NEAR(dy,1872.25,1e-8);
-
-   delete sensorModel;
-   sensorModel = NULL;
-
+  UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+  std::string modelState = sensorModel->getModelState(); 
+  auto state = json::parse(modelState);
+  state["m_odtX"] = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
+  state["m_odtY"] = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
+  sensorModel->replaceModelState(state.dump()); 
+
+  double dx,dy;
+  ASSERT_NE(sensorModel, nullptr);
+  sensorModel->distortionFunction(imagePt.samp, imagePt.line,dx,dy );
+  
+  EXPECT_NEAR(dx,1872.25,1e-8 );
+  EXPECT_NEAR(dy,1872.25,1e-8);
+  
+  delete sensorModel;
+  sensorModel = NULL;
 }
 
 TEST_F(FrameIsdTest, setFocalPlane_AllCoefficientsOne) {
+  csm::ImageCoord imagePt(1872.25, 1872.25);
 
-  int precision  = 20;
-  std:string odtx_key= "odt_x";
-  std::string odty_key="odt_y";
-
-  isd.clearParams(odty_key);
-  isd.clearParams(odtx_key);
-
-   csm::ImageCoord imagePt(1872.25, 1872.25);
-
-   vector<double> odtx{1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
-   vector<double> odty{1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
-
-   for (auto & val: odtx){
-      ostringstream strval;
-      strval << setprecision(precision) << val;
-      isd.addParam("odt_x", strval.str());
-   }
-   for (auto & val: odty){
-      ostringstream strval;
-      strval << setprecision(precision) << val;
-      isd.addParam("odt_y", strval.str());
-   }
-
-
-
-   UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
-
-
-   double ux,uy;
-   ASSERT_NE(sensorModel, nullptr);
-   sensorModel->setFocalPlane(imagePt.samp, imagePt.line,ux,uy );
-
+  UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+  std::string modelState = sensorModel->getModelState(); 
+  auto state = json::parse(modelState);
+  state["m_odtX"] = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
+  state["m_odtY"] = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
+  sensorModel->replaceModelState(state.dump()); 
 
-   //The Jacobian is singular, so the setFocalPlane should break out of it's iteration and
-   //returns the same distorted coordinates that were passed in.
-   EXPECT_NEAR(ux,imagePt.samp,1e-8 );
-   EXPECT_NEAR(uy,imagePt.line,1e-8);
+  double ux,uy;
+  ASSERT_NE(sensorModel, nullptr);
+  sensorModel->setFocalPlane(imagePt.samp, imagePt.line,ux,uy );
 
-   delete sensorModel;
-   sensorModel = NULL;
+  // The Jacobian is singular, so the setFocalPlane should break out of it's iteration and
+  // returns the same distorted coordinates that were passed in.
+  EXPECT_NEAR(ux,imagePt.samp,1e-8 );
+  EXPECT_NEAR(uy,imagePt.line,1e-8);
 
+  delete sensorModel;
+  sensorModel = NULL;
 }
 
 
 TEST_F(FrameIsdTest, distortMe_AlternatingOnes) {
+  csm::ImageCoord imagePt(7.5, 7.5);
 
-  int precision  = 20;
-  std:string odtx_key= "odt_x";
-  std::string odty_key="odt_y";
-
-  isd.clearParams(odty_key);
-  isd.clearParams(odtx_key);
-
-   csm::ImageCoord imagePt(7.5, 7.5);
-
-   vector<double> odtx{1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0};
-   vector<double> odty{0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0};
-
-   for (auto & val: odtx){
-      ostringstream strval;
-      strval << setprecision(precision) << val;
-      isd.addParam("odt_x", strval.str());
-   }
-   for (auto & val: odty){
-      ostringstream strval;
-      strval << setprecision(precision) << val;
-      isd.addParam("odt_y", strval.str());
-   }
-
-   UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
-
-   double dx,dy;
-   ASSERT_NE(sensorModel, nullptr);
-   sensorModel->distortionFunction(imagePt.samp, imagePt.line,dx,dy );
-
-   EXPECT_NEAR(dx,908.5,1e-8 );
-   EXPECT_NEAR(dy,963.75,1e-8);
-
-   delete sensorModel;
-   sensorModel = NULL;
-
+  UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+  std::string modelState = sensorModel->getModelState(); 
+  auto state = json::parse(modelState);
+  state["m_odtX"] = {1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0};
+  state["m_odtY"] = {0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0};
+  sensorModel->replaceModelState(state.dump()); 
+
+  double dx,dy;
+  ASSERT_NE(sensorModel, nullptr);
+  sensorModel->distortionFunction(imagePt.samp, imagePt.line,dx,dy );
+  
+  EXPECT_NEAR(dx,908.5,1e-8 );
+  EXPECT_NEAR(dy,963.75,1e-8);
+  
+  delete sensorModel;
+  sensorModel = NULL;
 }
 
 
-
-
 TEST_F(FrameIsdTest, setFocalPlane_AlternatingOnes) {
+  csm::ImageCoord imagePt(963.75, 908.5);
 
-  int precision  = 20;
-  std:string odtx_key= "odt_x";
-  std::string odty_key="odt_y";
-
-  isd.clearParams(odty_key);
-  isd.clearParams(odtx_key);
+  UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+  std::string modelState = sensorModel->getModelState(); 
+  auto state = json::parse(modelState);
+  state["m_odtX"] = {1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0};
+  state["m_odtY"] = {0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0};
+  sensorModel->replaceModelState(state.dump()); 
 
-   csm::ImageCoord imagePt(963.75, 908.5);
-
-   vector<double> odtx{1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0};
-   vector<double> odty{0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0};
-
-   for (auto & val: odtx){
-      ostringstream strval;
-      strval << setprecision(precision) << val;
-      isd.addParam("odt_x", strval.str());
-   }
-   for (auto & val: odty){
-      ostringstream strval;
-      strval << setprecision(precision) << val;
-      isd.addParam("odt_y", strval.str());
-   }
-
-   UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
-
-   double ux,uy;
-   ASSERT_NE(sensorModel, nullptr);
-
-   sensorModel->setFocalPlane(imagePt.samp, imagePt.line,ux,uy );
+  double ux,uy;
+  ASSERT_NE(sensorModel, nullptr);
 
-   EXPECT_NEAR(ux,7.5,1e-8 );
-   EXPECT_NEAR(uy,7.5,1e-8);
+  sensorModel->setFocalPlane(imagePt.samp, imagePt.line,ux,uy );
 
-   delete sensorModel;
-   sensorModel = NULL;
+  EXPECT_NEAR(ux,7.5,1e-8 );
+  EXPECT_NEAR(uy,7.5,1e-8);
 
+  delete sensorModel;
+  sensorModel = NULL;
 }
 
 
 
 // Focal Length Tests:
 TEST_F(FrameIsdTest, FL500_OffBody4) {
-   std::string key = "focal_length";
-   std::string newValue = "500.0";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
-
-
-   UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+  std::string key = "m_focalLength";
+  double newValue = 500.0;
+
+  UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+  std::string modelState = sensorModel->getModelState(); 
+  auto state = json::parse(modelState);
+  state[key] = newValue; 
+  sensorModel->replaceModelState(state.dump()); 
+  
+  ASSERT_NE(sensorModel, nullptr);
+  csm::ImageCoord imagePt(15.0, 15.0);
+  csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
+  EXPECT_NEAR(groundPt.x, 9.77688917, 1e-8);
+  EXPECT_NEAR(groundPt.y, -1.48533467, 1e-8);
+  EXPECT_NEAR(groundPt.z, -1.48533467, 1e-8);
+  
+  delete sensorModel;
+  sensorModel = NULL;
+}
 
-   ASSERT_NE(sensorModel, nullptr);
-   csm::ImageCoord imagePt(15.0, 15.0);
-   csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
-   EXPECT_NEAR(groundPt.x, 9.77688917, 1e-8);
-   EXPECT_NEAR(groundPt.y, -1.48533467, 1e-8);
-   EXPECT_NEAR(groundPt.z, -1.48533467, 1e-8);
 
-   delete sensorModel;
-   sensorModel = NULL;
-}
 TEST_F(FrameIsdTest, FL500_OffBody3) {
-   std::string key = "focal_length";
-   std::string newValue = "500.0";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
-
-   UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+  std::string key = "m_focalLength";
+  double newValue = 500.0;
+
+  UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+  std::string modelState = sensorModel->getModelState(); 
+  auto state = json::parse(modelState);
+  state[key] = newValue; 
+  sensorModel->replaceModelState(state.dump()); 
+  
+  ASSERT_NE(sensorModel, nullptr);
+  csm::ImageCoord imagePt(0.0, 0.0);
+  csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
+  EXPECT_NEAR(groundPt.x, 9.77688917, 1e-8);
+  EXPECT_NEAR(groundPt.y, 1.48533467, 1e-8);
+  EXPECT_NEAR(groundPt.z, 1.48533467, 1e-8);
+  
+  delete sensorModel;
+  sensorModel = NULL;
+}
 
-   ASSERT_NE(sensorModel, nullptr);
-   csm::ImageCoord imagePt(0.0, 0.0);
-   csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
-   EXPECT_NEAR(groundPt.x, 9.77688917, 1e-8);
-   EXPECT_NEAR(groundPt.y, 1.48533467, 1e-8);
-   EXPECT_NEAR(groundPt.z, 1.48533467, 1e-8);
 
-   delete sensorModel;
-   sensorModel = NULL;
-}
 TEST_F(FrameIsdTest, FL500_Center) {
-   std::string key = "focal_length";
-   std::string newValue = "500.0";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
-
-   UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+  std::string key = "m_focalLength";
+  double newValue = 500.0;
 
-   ASSERT_NE(sensorModel, nullptr);
-   csm::ImageCoord imagePt(7.5, 7.5);
-   csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
-   EXPECT_NEAR(groundPt.x, 10.0, 1e-8);
-   EXPECT_NEAR(groundPt.y, 0.0, 1e-8);
-   EXPECT_NEAR(groundPt.z, 0.0, 1e-8);
+  UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+  std::string modelState = sensorModel->getModelState(); 
+  auto state = json::parse(modelState);
+  state[key] = newValue; 
+  sensorModel->replaceModelState(state.dump()); 
 
-   delete sensorModel;
-   sensorModel = NULL;
+  ASSERT_NE(sensorModel, nullptr);
+  csm::ImageCoord imagePt(7.5, 7.5);
+  csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
+  EXPECT_NEAR(groundPt.x, 10.0, 1e-8);
+  EXPECT_NEAR(groundPt.y, 0.0, 1e-8);
+  EXPECT_NEAR(groundPt.z, 0.0, 1e-8);
+  
+  delete sensorModel;
+  sensorModel = NULL;
 }
-TEST_F(FrameIsdTest, FL500_SlightlyOffCenter) {
-   std::string key = "focal_length";
-   std::string newValue = "500.0";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
 
-   UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
-
-   ASSERT_NE(sensorModel, nullptr);
-   csm::ImageCoord imagePt(7.5, 6.5);
-   csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
-   EXPECT_NEAR(groundPt.x, 9.99803960, 1e-8);
-   EXPECT_NEAR(groundPt.y, 0.0, 1e-8);
-   EXPECT_NEAR(groundPt.z, 1.98000392e-01, 1e-8);
-
-   delete sensorModel;
-   sensorModel = NULL;
 
+TEST_F(FrameIsdTest, FL500_SlightlyOffCenter) {
+  std::string key = "m_focalLength";
+  double newValue = 500.0;
+
+  UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+  std::string modelState = sensorModel->getModelState(); 
+  auto state = json::parse(modelState);
+  state[key] = newValue; 
+  sensorModel->replaceModelState(state.dump()); 
+
+  ASSERT_NE(sensorModel, nullptr);
+  csm::ImageCoord imagePt(7.5, 6.5);
+  csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
+  EXPECT_NEAR(groundPt.x, 9.99803960, 1e-8);
+  EXPECT_NEAR(groundPt.y, 0.0, 1e-8);
+  EXPECT_NEAR(groundPt.z, 1.98000392e-01, 1e-8);
+
+  delete sensorModel;
+  sensorModel = NULL;
 }
 
-//Observer x position:
+// Observer x position:
 TEST_F(FrameIsdTest, X10_SlightlyOffCenter) {
-   std::string key = "x_sensor_origin";
-   std::string newValue = "10.0";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
-
+   double newValue = 10.0;
    UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+   sensorModel->setParameterValue(0, newValue); // X
 
    ASSERT_NE(sensorModel, nullptr);
    csm::ImageCoord imagePt(7.5, 6.5);
@@ -612,38 +417,32 @@ TEST_F(FrameIsdTest, X10_SlightlyOffCenter) {
 
    delete sensorModel;
    sensorModel = NULL;
-
 }
+
+
 TEST_F(FrameIsdTest, X1e9_SlightlyOffCenter) {
-   std::string key = "x_sensor_origin";
-   std::string newValue = "1000000000.0";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
+   double newValue = 1000000000.0;
 
    UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+   sensorModel->setParameterValue(0, newValue); // X
 
    ASSERT_NE(sensorModel, nullptr);
    csm::ImageCoord imagePt(7.5, 6.5);
    csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
-   //Note: In the following, the tolerance was increased due to the very large distance being tested (~6.68 AU).
+   // Note: In the following, the tolerance was increased due to the very large distance being tested (~6.68 AU).
    EXPECT_NEAR(groundPt.x, 3.99998400e+03, 1e-4);
    EXPECT_NEAR(groundPt.y, 0.0, 1e-4);
    EXPECT_NEAR(groundPt.z, 1.99999200e+06, 1e-4);
 
    delete sensorModel;
    sensorModel = NULL;
-
 }
 
-//Angle rotations:
-TEST_F(FrameIsdTest, Rotation_omegaPi_Center) {
-   std::string key = "omega";
-   std::ostringstream strval;
-   strval << std::setprecision(20) << M_PI;
-   isd.clearParams(key);
-   isd.addParam(key,strval.str());
 
+// Angle rotations:
+TEST_F(FrameIsdTest, Rotation_omegaPi_Center) {
    UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+   sensorModel->setParameterValue(3, M_PI); // omega
 
    ASSERT_NE(sensorModel, nullptr);
    csm::ImageCoord imagePt(7.5, 7.5);
@@ -654,28 +453,15 @@ TEST_F(FrameIsdTest, Rotation_omegaPi_Center) {
 
    delete sensorModel;
    sensorModel = NULL;
-
 }
-TEST_F(FrameIsdTest, Rotation_NPole_Center) {
-   std::string key = "phi";
-   std::ostringstream strval;
-   strval << std::setprecision(20) << -M_PI;
-   isd.clearParams(key);
-   isd.addParam(key,strval.str());
-   key = "x_sensor_origin";
-   std::string newValue = "0.0";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
-   key = "y_sensor_origin";
-   newValue = "0.0";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
-   key = "z_sensor_origin";
-   newValue = "1000.0";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
 
+
+TEST_F(FrameIsdTest, Rotation_NPole_Center) {
    UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+   sensorModel->setParameterValue(4, -M_PI); // phi
+   sensorModel->setParameterValue(0, 0.0); // X
+   sensorModel->setParameterValue(1, 0.0); // Y 
+   sensorModel->setParameterValue(2, 1000.0); // Z
 
    ASSERT_NE(sensorModel, nullptr);
    csm::ImageCoord imagePt(7.5, 7.5);
@@ -686,27 +472,15 @@ TEST_F(FrameIsdTest, Rotation_NPole_Center) {
 
    delete sensorModel;
    sensorModel = NULL;
-
 }
-TEST_F(FrameIsdTest, Rotation_SPole_Center) {
-   std::string key = "phi";
-   std::string newValue = "0.0";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
-   key = "x_sensor_origin";
-   newValue = "0.0";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
-   key = "y_sensor_origin";
-   newValue = "0.0";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
-   key = "z_sensor_origin";
-   newValue = "-1000.0";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
 
+
+TEST_F(FrameIsdTest, Rotation_SPole_Center) {
    UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+   sensorModel->setParameterValue(4, 0.0); // phi
+   sensorModel->setParameterValue(0, 0.0); // X
+   sensorModel->setParameterValue(1, 0.0); // Y 
+   sensorModel->setParameterValue(2, -1000.0); // Z
 
    ASSERT_NE(sensorModel, nullptr);
    csm::ImageCoord imagePt(7.5, 7.5);
@@ -717,60 +491,67 @@ TEST_F(FrameIsdTest, Rotation_SPole_Center) {
 
    delete sensorModel;
    sensorModel = NULL;
-
 }
 
 
 // Ellipsoid axis tests:
 TEST_F(FrameIsdTest, SemiMajorAxis100x_Center) {
-   std::string key = "semi_major_axis";
-   std::string newValue = "1.0";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
+  std::string key = "m_majorAxis";
+  double newValue = 1000.0;
 
+  UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+  std::string modelState = sensorModel->getModelState(); 
+  auto state = json::parse(modelState);
+  state[key] = newValue; 
+  sensorModel->replaceModelState(state.dump()); 
 
-   UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+  ASSERT_NE(sensorModel, nullptr);
+  csm::ImageCoord imagePt(7.5, 7.5);
+  csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
+  EXPECT_NEAR(groundPt.x, 1000.0, 1e-8);
+  EXPECT_NEAR(groundPt.y, 0.0, 1e-8);
+  EXPECT_NEAR(groundPt.z, 0.0, 1e-8);
 
-   ASSERT_NE(sensorModel, nullptr);
-   csm::ImageCoord imagePt(7.5, 7.5);
-   csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
-   EXPECT_NEAR(groundPt.x, 1000.0, 1e-8);
-   EXPECT_NEAR(groundPt.y, 0.0, 1e-8);
-   EXPECT_NEAR(groundPt.z, 0.0, 1e-8);
+  delete sensorModel;
+  sensorModel = NULL;
+}
 
-   delete sensorModel;
-   sensorModel = NULL;
 
-}
 TEST_F(FrameIsdTest, SemiMajorAxis10x_SlightlyOffCenter) {
-   std::string key = "semi_major_axis";
-   std::string newValue = "0.10";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
-
-   UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
-   ASSERT_NE(sensorModel, nullptr);
-   csm::ImageCoord imagePt(7.5, 6.5);
-   csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
-   //Note: In the following, the tolerance was increased due to the combination of an offset image point and
-   //      a very large deviation from sphericity.
-   EXPECT_NEAR(groundPt.x, 9.83606557e+01, 1e-7);
-   EXPECT_NEAR(groundPt.y, 0.0, 1e-7);
-   EXPECT_NEAR(groundPt.z, 1.80327869, 1e-7);
+  std::string key = "m_majorAxis";
+  double newValue = 100.0;
+
+  UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+  std::string modelState = sensorModel->getModelState(); 
+  auto state = json::parse(modelState);
+  state[key] = newValue; 
+  sensorModel->replaceModelState(state.dump()); 
+
+  ASSERT_NE(sensorModel, nullptr);
+  csm::ImageCoord imagePt(7.5, 6.5);
+  csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
+  // Note: In the following, the tolerance was increased due to the combination of an offset image point and
+  //       a very large deviation from sphericity.
+  EXPECT_NEAR(groundPt.x, 9.83606557e+01, 1e-7);
+  EXPECT_NEAR(groundPt.y, 0.0, 1e-7);
+  EXPECT_NEAR(groundPt.z, 1.80327869, 1e-7);
+  
+  delete sensorModel;
+  sensorModel = NULL;
+}
 
-   delete sensorModel;
-   sensorModel = NULL;
 
-}
 // The following test is for the scenario where the semi_minor_axis is actually larger
 // than the semi_major_axis:
 TEST_F(FrameIsdTest, SemiMinorAxis10x_SlightlyOffCenter) {
-   std::string key = "semi_minor_axis";
-   std::string newValue = "0.10";
-   isd.clearParams(key);
-   isd.addParam(key,newValue);
+   std::string key = "m_minorAxis";
+   double newValue = 100.0;
 
    UsgsAstroFrameSensorModel* sensorModel = createModel(isd);
+   std::string modelState = sensorModel->getModelState(); 
+   auto state = json::parse(modelState);
+   state[key] = newValue;
+   sensorModel->replaceModelState(state.dump()); 
 
    ASSERT_NE(sensorModel, nullptr);
    csm::ImageCoord imagePt(7.5, 6.5);
@@ -781,5 +562,4 @@ TEST_F(FrameIsdTest, SemiMinorAxis10x_SlightlyOffCenter) {
 
    delete sensorModel;
    sensorModel = NULL;
-
 }
diff --git a/tests/TestyMcTestFace.cpp b/tests/TestyMcTestFace.cpp
index 20c569e..7b0696c 100644
--- a/tests/TestyMcTestFace.cpp
+++ b/tests/TestyMcTestFace.cpp
@@ -12,25 +12,10 @@ using json = nlohmann::json;
 
 class FrameIsdTest : public ::testing::Test {
    protected:
-
-      csm::Isd isd;
-
-   virtual void SetUp() {
-      std::ifstream isdFile("data/simpleFramerISD.json");
-      json jsonIsd = json::parse(isdFile);
-      for (json::iterator it = jsonIsd.begin(); it != jsonIsd.end(); ++it) {
-         json jsonValue = it.value();
-         if (jsonValue.size() > 1) {
-            for (int i = 0; i < jsonValue.size(); i++) {
-               isd.addParam(it.key(), jsonValue[i].dump());
-            }
-         }
-         else {
-            isd.addParam(it.key(), jsonValue.dump());
-         }
-      }
-      isdFile.close();
-   }
+     csm::Isd isd;
+     virtual void SetUp() {
+       isd.setFilename("data/simpleFramerISD.img");
+     };
 };
 
 TEST(FramePluginTests, PluginName) {
-- 
GitLab