diff --git a/include/usgscsm/UsgsAstroLsSensorModel.h b/include/usgscsm/UsgsAstroLsSensorModel.h
index 0a3b721b41ca213ac0dbc08c9cccf557949df292..3ce3880f1f380109a6b7c843b51736d03e0571d0 100644
--- a/include/usgscsm/UsgsAstroLsSensorModel.h
+++ b/include/usgscsm/UsgsAstroLsSensorModel.h
@@ -154,6 +154,12 @@ class UsgsAstroLsSensorModel : public csm::RasterGM,
 
   virtual std::string getModelState() const;
 
+  // Apply a rotation and translation to a state string. The effect is
+  // to transform the position and orientation of the camera in ECEF
+  // coordinates.
+  static void applyTransformToState(ale::Rotation const& r, ale::Vec3d const& t,
+                                    std::string& stateString);
+  
   // Set the sensor model based on the input state data
   void set(const std::string& state_data);
 
diff --git a/include/usgscsm/Utilities.h b/include/usgscsm/Utilities.h
index 2e41bfda26835801e2703db1328412b1dde2eeaf..84fabec5bfe9b5dd682fdb6e0198d63808db8a3b 100644
--- a/include/usgscsm/Utilities.h
+++ b/include/usgscsm/Utilities.h
@@ -10,6 +10,9 @@
 
 #include <nlohmann/json.hpp>
 
+#include <ale/Rotation.h>
+#include <ale/Vectors.h>
+
 #include <Warning.h>
 #include <csm.h>
 
@@ -168,4 +171,9 @@ std::vector<double> getSensorOrientations(nlohmann::json isd,
 double getWavelength(nlohmann::json isd, csm::WarningList *list = nullptr);
 nlohmann::json stateAsJson(std::string modelState);
 
+// Apply transforms to orientations and vectors
+void applyRotationToQuatVec(ale::Rotation const& r, std::vector<double> & quaternions);
+void applyRotationTranslationToXyzVec(ale::Rotation const& r, ale::Vec3d const& t,
+                                      std::vector<double> & xyz);
+
 #endif  // INCLUDE_USGSCSM_UTILITIES_H_
diff --git a/src/UsgsAstroLsSensorModel.cpp b/src/UsgsAstroLsSensorModel.cpp
index a8a87f690ffebcc3410c84e8a000ddac2e80e6d5..b329d344c36b159d321b1bf5f7ebed42f04b94a7 100644
--- a/src/UsgsAstroLsSensorModel.cpp
+++ b/src/UsgsAstroLsSensorModel.cpp
@@ -441,6 +441,37 @@ std::string UsgsAstroLsSensorModel::getModelState() const {
   return stateString;
 }
 
+//***************************************************************************
+// UsgsAstroLineScannerSensorModel::applyTransformToState
+//***************************************************************************
+void UsgsAstroLsSensorModel::applyTransformToState(ale::Rotation const& r, ale::Vec3d const& t,
+                                                   std::string& stateString) {
+
+  nlohmann::json j = stateAsJson(stateString);
+
+  // Apply rotation to quaternions
+  std::vector<double> quaternions = j["m_quaternions"].get<std::vector<double>>();
+  applyRotationToQuatVec(r, quaternions);
+  j["m_quaternions"] = quaternions;
+  
+  // Apply rotation and translation to positions
+  std::vector<double> positions = j["m_positions"].get<std::vector<double>>();;
+  applyRotationTranslationToXyzVec(r, t, positions);
+  j["m_positions"] = positions;
+  
+  // Apply rotation to velocities. The translation does not get applied. 
+  ale::Vec3d zero_t(0, 0, 0);
+  std::vector<double> velocities = j["m_velocities"].get<std::vector<double>>();;
+  applyRotationTranslationToXyzVec(r, zero_t, velocities);
+  j["m_velocities"] = velocities;
+  
+  // We do not change the Sun position or velocity. The idea is that
+  // the Sun is so far, that minor camera adjustments won't affect
+  // where the Sun is.
+
+  // Update the state string
+  stateString = getModelNameFromModelState(stateString) + "\n" + j.dump();
+}
 //***************************************************************************
 // UsgsAstroLineScannerSensorModel::reset
 //***************************************************************************
@@ -2233,8 +2264,6 @@ std::string UsgsAstroLsSensorModel::constructStateFromIsd(
   json jsonIsd = json::parse(imageSupportData);
   csm::WarningList* parsingWarnings = new csm::WarningList;
 
-  int num_params = NUM_PARAMETERS;
-
   state["m_modelName"] = ale::getSensorModelName(jsonIsd);
   state["m_imageIdentifier"] = ale::getImageId(jsonIsd);
   state["m_sensorName"] = ale::getSensorName(jsonIsd);
diff --git a/src/Utilities.cpp b/src/Utilities.cpp
index 279c46dbf721cae6e270eb846dac6edf1ca4e389..df3e5d2ea50fce11281a50466a45e2ed1142c7b3 100644
--- a/src/Utilities.cpp
+++ b/src/Utilities.cpp
@@ -1323,3 +1323,49 @@ json stateAsJson(std::string modelState) {
   }
   return json::parse(modelState.begin() + found, modelState.end());
 }
+
+// Apply a rotation to a vector of quaternions.
+void applyRotationToQuatVec(ale::Rotation const& r, std::vector<double> & quaternions) {
+  int num_quat = quaternions.size();
+  
+  for (int it = 0; it < num_quat/4; it++) {
+
+    double * q = &quaternions[4*it];
+
+    // Note that quaternion in q is stored in order x, y, z, w, while
+    // the rotation matrix wants it as w, x, y, z.
+    
+    std::vector<double> trans_q = (r * ale::Rotation(q[3], q[0], q[1], q[2])).toQuaternion();
+
+    // Modify in-place
+    q[0] = trans_q[1];
+    q[1] = trans_q[2];
+    q[2] = trans_q[3];
+    q[3] = trans_q[0];
+  }
+}
+
+// Apply a rotation and translation to a vector of xyz vectors.
+void applyRotationTranslationToXyzVec(ale::Rotation const& r, ale::Vec3d const& t,
+                                      std::vector<double> & xyz) {
+  
+  int num_xyz = xyz.size();
+  for (int it = 0; it < num_xyz/3; it++) {
+    
+    double * p = &xyz[3*it];
+
+    ale::Vec3d p_vec(p[0], p[1], p[2]);
+    
+    // Apply the rotation
+    p_vec = r(p_vec);
+
+    // Apply the translation
+    p_vec += t;
+
+    // Modify in-place
+    p[0] = p_vec.x;
+    p[1] = p_vec.y;
+    p[2] = p_vec.z;
+    
+  }
+}
diff --git a/tests/LineScanCameraTests.cpp b/tests/LineScanCameraTests.cpp
index a36be717904cc7a7521bf5eb8ad8fd324fcb252a..8ae9bd8dec2c9dd17fb1893f6bb110b234db42c0 100644
--- a/tests/LineScanCameraTests.cpp
+++ b/tests/LineScanCameraTests.cpp
@@ -22,6 +22,29 @@ TEST_F(ConstVelocityLineScanSensorModel, State) {
   EXPECT_EQ(sensorModel->getModelState(), modelState);
 }
 
+// Apply the identity transform to a state
+TEST_F(ConstVelocityLineScanSensorModel, ApplyTransformToState) {
+  std::string modelState = sensorModel->getModelState();
+
+  ale::Rotation r; // identity rotation
+  ale::Vec3d t;    // zero translation
+
+  // The input state has some "-0" values which get transformed by
+  // applying the identity transform into "0", which results in the
+  // state string changing. Hence, for this test, compare the results
+  // of applying the identity transform once vs twice, which are the
+  // same.
+
+  // First application
+  UsgsAstroLsSensorModel::applyTransformToState(r, t, modelState);
+
+  // Second application
+  std::string modelState2 = modelState;
+  UsgsAstroLsSensorModel::applyTransformToState(r, t, modelState2);
+
+  EXPECT_EQ(modelState, modelState2);
+}
+
 // Fly by tests
 
 TEST_F(ConstVelocityLineScanSensorModel, Center) {