diff --git a/include/usgscsm/UsgsAstroProjectedLsSensorModel.h b/include/usgscsm/UsgsAstroProjectedLsSensorModel.h index 9b907c67846350e1ed1f447aeb7d3b589b0f69d8..d8d9ba0f87a75b950dafc1df75c3b9b371f05ba8 100644 --- a/include/usgscsm/UsgsAstroProjectedLsSensorModel.h +++ b/include/usgscsm/UsgsAstroProjectedLsSensorModel.h @@ -70,61 +70,8 @@ class UsgsAstroProjectedLsSensorModel : public UsgsAstroLsSensorModel { csm::WarningList* list); // State data elements; - std::string m_imageIdentifier; - std::string m_sensorName; - int m_nLines; - int m_nSamples; - int m_platformFlag; - std::vector<double> m_intTimeLines; - std::vector<double> m_intTimeStartTimes; - std::vector<double> m_intTimes; - double m_startingEphemerisTime; - double m_centerEphemerisTime; - double m_detectorSampleSumming; - double m_detectorLineSumming; - double m_startingDetectorSample; - double m_startingDetectorLine; - int m_ikCode; - double m_focalLength; - double m_zDirection; - DistortionType m_distortionType; - std::vector<double> m_opticalDistCoeffs; - double m_iTransS[3]; - double m_iTransL[3]; - double m_detectorSampleOrigin; - double m_detectorLineOrigin; - double m_mountingMatrix[9]; - double m_majorAxis; - double m_minorAxis; - std::string m_referenceDateAndTime; - std::string m_platformIdentifier; - std::string m_sensorIdentifier; - std::string m_trajectoryIdentifier; - std::string m_collectionIdentifier; - double m_refElevation; - double m_minElevation; - double m_maxElevation; - double m_dtEphem; - double m_t0Ephem; - double m_dtQuat; - double m_t0Quat; - int m_numPositions; - int m_numQuaternions; - std::vector<double> m_positions; - std::vector<double> m_velocities; - std::vector<double> m_quaternions; - std::vector<double> m_currentParameterValue; - std::vector<csm::param::Type> m_parameterType; - csm::EcefCoord m_referencePointXyz; - double m_gsd; - double m_flyingHeight; - double m_halfSwath; - double m_halfTime; - std::vector<double> m_covariance; - int m_imageFlipFlag; - - std::vector<double> m_sunPosition; - std::vector<double> m_sunVelocity; + std::vector<double> m_geoTransform; + std::string m_projString; // Define logging pointer and file content std::shared_ptr<spdlog::logger> m_logger = spdlog::get("usgscsm_logger"); @@ -720,13 +667,6 @@ class UsgsAstroProjectedLsSensorModel : public UsgsAstroLsSensorModel { const std::vector<double>& adj, double attCorr[9]) const; - virtual csm::EcefVector getSunPosition(const double imageTime) const; - //> This method returns the position of the sun at the time the image point - // was recorded. If multiple sun positions are available, the method uses - // lagrange interpolation. If one sun position and at least one sun velocity - // are available, then the position is calculated using linear extrapolation. - // If only one sun position is available, then that value is returned. - private: // Some state data values not found in the support data require a @@ -790,4 +730,4 @@ class UsgsAstroProjectedLsSensorModel : public UsgsAstroLsSensorModel { bool m_useApproxInitTrans; }; -#endif // INCLUDE_USGSCSM_UsgsAstroProjectedLsSensorModel_H_ +#endif // INCLUDE_USGSCSM_USGSASTROPROJECTEDLSSENSORMODEL_H_ diff --git a/include/usgscsm/Utilities.h b/include/usgscsm/Utilities.h index d847e1e1d960979db643134165538c2025ad10dd..a661a354a130336dae895ff90f7ad409e136fd30 100644 --- a/include/usgscsm/Utilities.h +++ b/include/usgscsm/Utilities.h @@ -199,4 +199,12 @@ void applyRotationTranslationToXyzVec(ale::Rotation const& r, ale::Vec3d const& // to a calendar time string, such as 2000-01-01T00:16:40Z. std::string ephemTimeToCalendarTime(double ephemTime); +std::vector<double> getGeoTransform(nlohmann::json isd); + +std::string getProjectionString(nlohmann::json isd); + +std::vector<double> pixelToMeter(double line, double sample, std::vector<double> geoTransform); + +std::vector<double> meterToPixel(double meter_x, double meter_y, std::vector<double> geoTransform); + #endif // INCLUDE_USGSCSM_UTILITIES_H_ diff --git a/src/UsgsAstroLsSensorModel.cpp b/src/UsgsAstroLsSensorModel.cpp index e5d3b2dcf8c009d421b225d84d258346d8292593..351815a4fb46d01ca5eb44dc3527be70d117e7e3 100644 --- a/src/UsgsAstroLsSensorModel.cpp +++ b/src/UsgsAstroLsSensorModel.cpp @@ -2735,7 +2735,7 @@ std::string UsgsAstroLsSensorModel::constructStateFromIsd( } throw csm::Error(csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE, "ISD is invalid for creating the sensor model.", - "UsgsAstroFrameSensorModel::constructStateFromIsd"); + "UsgsAstroLsSensorModel::constructStateFromIsd"); } // The state data will still be updated when a sensor model is created since diff --git a/src/UsgsAstroProjectedLsSensorModel.cpp b/src/UsgsAstroProjectedLsSensorModel.cpp index 6c47b72777318b7a943de78eeee213d5615f73f2..91d4d2560f748d5d4dae099b8ba84bca76c44988 100644 --- a/src/UsgsAstroProjectedLsSensorModel.cpp +++ b/src/UsgsAstroProjectedLsSensorModel.cpp @@ -33,6 +33,8 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <iostream> #include <sstream> +#include <proj.h> + #include <Error.h> #include <nlohmann/json.hpp> @@ -117,6 +119,8 @@ const std::string UsgsAstroProjectedLsSensorModel::_STATE_KEYWORD[] = { "m_halfSwath", "m_halfTime", "m_covariance", + "m_geoTransform", + "m_projString", }; const int UsgsAstroProjectedLsSensorModel::NUM_PARAM_TYPES = 4; @@ -130,9 +134,17 @@ const csm::param::Type UsgsAstroProjectedLsSensorModel::PARAM_CHAR_ALL[] = { // UsgsAstroLineScannerSensorModel::replaceModelState //*************************************************************************** void UsgsAstroProjectedLsSensorModel::replaceModelState(const std::string& stateString) { - UsgsAstroLsSensorModel::replaceModelState(stateString); + reset(); - // Pull proj string from ISD + auto j = stateAsJson(stateString); + m_geoTransform = j["m_geoTransform"].get<std::vector<double>>(); + m_projString = j["m_projString"]; + MESSAGE_LOG( + spdlog::level::trace, + "m_geoTransform: {} " + "m_projString: {} ", + j["m_geoTransform"].dump(), j["m_projString"].dump()); + UsgsAstroLsSensorModel::replaceModelState(stateString); } //*************************************************************************** @@ -168,144 +180,20 @@ std::string UsgsAstroProjectedLsSensorModel::getModelNameFromModelState( // UsgsAstroLineScannerSensorModel::getModelState //*************************************************************************** std::string UsgsAstroProjectedLsSensorModel::getModelState() const { - MESSAGE_LOG(spdlog::level::info, "Running getModelState") - - json state; - state["m_modelName"] = _SENSOR_MODEL_NAME; - state["m_startingDetectorSample"] = m_startingDetectorSample; - state["m_startingDetectorLine"] = m_startingDetectorLine; - state["m_imageIdentifier"] = m_imageIdentifier; - state["m_sensorName"] = m_sensorName; - state["m_nLines"] = m_nLines; - state["m_nSamples"] = m_nSamples; - state["m_platformFlag"] = m_platformFlag; - MESSAGE_LOG( - spdlog::level::trace, - "m_imageIdentifier: {} " - "m_sensorName: {} " - "m_nLines: {} " - "m_nSamples: {} " - "m_platformFlag: {} ", - m_imageIdentifier, m_sensorName, m_nLines, m_nSamples, m_platformFlag) - - state["m_intTimeLines"] = m_intTimeLines; - state["m_intTimeStartTimes"] = m_intTimeStartTimes; - state["m_intTimes"] = m_intTimes; - state["m_startingEphemerisTime"] = m_startingEphemerisTime; - state["m_centerEphemerisTime"] = m_centerEphemerisTime; - MESSAGE_LOG( - spdlog::level::trace, - "m_startingEphemerisTime: {} " - "m_centerEphemerisTime: {} ", - m_startingEphemerisTime, m_centerEphemerisTime) - - state["m_detectorSampleSumming"] = m_detectorSampleSumming; - state["m_detectorLineSumming"] = m_detectorLineSumming; - state["m_startingDetectorSample"] = m_startingDetectorSample; - state["m_ikCode"] = m_ikCode; - MESSAGE_LOG( - spdlog::level::trace, - "m_detectorSampleSumming: {} " - "m_detectorLineSumming: {} " - "m_startingDetectorSample: {} " - "m_ikCode: {} ", - m_detectorSampleSumming, m_detectorLineSumming, m_startingDetectorSample, - m_ikCode) - - state["m_focalLength"] = m_focalLength; - state["m_zDirection"] = m_zDirection; - state["m_distortionType"] = m_distortionType; - state["m_opticalDistCoeffs"] = m_opticalDistCoeffs; - MESSAGE_LOG( - spdlog::level::trace, - "m_focalLength: {} " - "m_zDirection: {} " - "m_distortionType (0-Radial, 1-Transverse): {} ", - m_focalLength, m_zDirection, m_distortionType) - - state["m_iTransS"] = std::vector<double>(m_iTransS, m_iTransS + 3); - state["m_iTransL"] = std::vector<double>(m_iTransL, m_iTransL + 3); - - state["m_detectorSampleOrigin"] = m_detectorSampleOrigin; - state["m_detectorLineOrigin"] = m_detectorLineOrigin; - state["m_majorAxis"] = m_majorAxis; - state["m_minorAxis"] = m_minorAxis; - MESSAGE_LOG( - spdlog::level::trace, - "m_detectorSampleOrigin: {} " - "m_detectorLineOrigin: {} " - "m_majorAxis: {} " - "m_minorAxis: {} ", - m_detectorSampleOrigin, m_detectorLineOrigin, m_majorAxis, m_minorAxis) - - state["m_platformIdentifier"] = m_platformIdentifier; - state["m_sensorIdentifier"] = m_sensorIdentifier; - state["m_minElevation"] = m_minElevation; - state["m_maxElevation"] = m_maxElevation; - MESSAGE_LOG( - spdlog::level::trace, - "m_platformIdentifier: {} " - "m_sensorIdentifier: {} " - "m_minElevation: {} " - "m_maxElevation: {} ", - m_platformIdentifier, m_sensorIdentifier, m_minElevation, m_maxElevation) - - state["m_dtEphem"] = m_dtEphem; - state["m_t0Ephem"] = m_t0Ephem; - state["m_dtQuat"] = m_dtQuat; - state["m_t0Quat"] = m_t0Quat; - MESSAGE_LOG( - spdlog::level::trace, - "m_dtEphem: {} " - "m_t0Ephem: {} " - "m_dtQuat: {} " - "m_t0Quat: {} ", - m_dtEphem, m_t0Ephem, m_dtQuat, m_t0Quat) - - state["m_numPositions"] = m_numPositions; - state["m_numQuaternions"] = m_numQuaternions; - state["m_positions"] = m_positions; - state["m_velocities"] = m_velocities; - state["m_quaternions"] = m_quaternions; + auto state = stateAsJson(UsgsAstroLsSensorModel::getModelState()); + state["m_geoTransform"] = m_geoTransform; + state["m_projString"] = m_projString; MESSAGE_LOG( spdlog::level::trace, - "m_numPositions: {} " - "m_numQuaternions: {} ", - m_numPositions, m_numQuaternions) - - state["m_currentParameterValue"] = m_currentParameterValue; - state["m_parameterType"] = m_parameterType; - - state["m_gsd"] = m_gsd; - state["m_flyingHeight"] = m_flyingHeight; - state["m_halfSwath"] = m_halfSwath; - state["m_halfTime"] = m_halfTime; - state["m_covariance"] = m_covariance; - MESSAGE_LOG( - spdlog::level::trace, - "m_gsd: {} " - "m_flyingHeight: {} " - "m_halfSwath: {} " - "m_halfTime: {} ", - m_gsd, m_flyingHeight, m_halfSwath, m_halfTime) - - state["m_referencePointXyz"] = json(); - state["m_referencePointXyz"][0] = m_referencePointXyz.x; - state["m_referencePointXyz"][1] = m_referencePointXyz.y; - state["m_referencePointXyz"][2] = m_referencePointXyz.z; - MESSAGE_LOG( - spdlog::level::trace, - "m_referencePointXyz: {} " - "m_referencePointXyz: {} " - "m_referencePointXyz: {} ", - m_referencePointXyz.x, m_referencePointXyz.y, m_referencePointXyz.z) - - state["m_sunPosition"] = m_sunPosition; - MESSAGE_LOG(spdlog::level::trace, "num sun positions: {} ", m_sunPosition.size()) - - state["m_sunVelocity"] = m_sunVelocity; - MESSAGE_LOG(spdlog::level::trace, "num sun velocities: {} ", m_sunVelocity.size()); - + "m_geoTransform: {}, {}, {}, {}, {}, {} " + "m_projString: {} ", + m_geoTransform[0], + m_geoTransform[1], + m_geoTransform[2], + m_geoTransform[3], + m_geoTransform[4], + m_geoTransform[5], + m_projString); // Use dump(2) to avoid creating the model string as a single long line std::string stateString = getModelName() + "\n" + state.dump(2); return stateString; @@ -315,10 +203,10 @@ std::string UsgsAstroProjectedLsSensorModel::getModelState() const { // UsgsAstroLineScannerSensorModel::reset //*************************************************************************** void UsgsAstroProjectedLsSensorModel::reset() { - MESSAGE_LOG(spdlog::level::debug, "Running reset()") - UsgsAstroLsSensorModel::reset(); + MESSAGE_LOG(spdlog::level::debug, "Running reset()"); - // Reset the proj string + m_geoTransform = std::vector<double>(6, 0.0); + m_projString = ""; } //***************************************************************************** @@ -347,7 +235,47 @@ UsgsAstroProjectedLsSensorModel::~UsgsAstroProjectedLsSensorModel() { csm::ImageCoord UsgsAstroProjectedLsSensorModel::groundToImage( const csm::EcefCoord &ground_pt, double desired_precision, double *achieved_precision, csm::WarningList *warnings) const { - csm::ImageCoord imagePt = UsgsAstroLsSensorModel::groundToImage(ground_pt, desired_precision, achieved_precision, warnings); + + PJ_CONTEXT *C = proj_context_create(); + + /* Create a projection. */ + PJ *isdProj = proj_create(C, m_projString.c_str()); + if (0 == isdProj) { + MESSAGE_LOG( + spdlog::level::debug, + "Failed to create isd transformation object"); + return csm::ImageCoord(0, 0); + } + + /* Create the geocentric projection for our target */ + std::string radius_a = "+a=" + std::to_string(m_majorAxis); + std::string radius_b = "+b=" + std::to_string(m_minorAxis); + std::string projString = "+proj=geocent " + radius_a + " " + radius_b + " +type=crs"; + PJ *ecefProj = proj_create(C, projString.c_str()); + if (0 == ecefProj) { + MESSAGE_LOG( + spdlog::level::debug, + "Failed to create geocent transformation object"); + return csm::ImageCoord(0, 0); + } + + // Compute the transformation from our ISIS projection to ecef + PJ *isdProj2ecefProj = proj_create_crs_to_crs_from_pj(C, isdProj, ecefProj, 0, 0); + PJ_COORD c_in; + c_in.xyz.x = ground_pt.x; + c_in.xyz.y = ground_pt.y; + c_in.xyz.z = ground_pt.z; + MESSAGE_LOG( + spdlog::level::info, + "Ground point {}, {}, {}", + c_in.xyz.x, c_in.xyz.y, c_in.xyz.z); + PJ_COORD c_out = proj_trans(isdProj2ecefProj, PJ_INV, c_in); + MESSAGE_LOG( + spdlog::level::info, + "Meters {}, {}", + c_out.xyz.x, c_out.xyz.y); + std::vector<double> lineSampleCoord = meterToPixel(c_out.xyz.x, c_out.xyz.y, m_geoTransform); + csm::ImageCoord imagePt(lineSampleCoord[0], lineSampleCoord[1]); MESSAGE_LOG( spdlog::level::info, "groundToImage result of ({}, {})", @@ -421,33 +349,50 @@ csm::ImageCoordCovar UsgsAstroProjectedLsSensorModel::groundToImage( csm::EcefCoord UsgsAstroProjectedLsSensorModel::imageToGround( const csm::ImageCoord& image_pt, double height, double desired_precision, double* achieved_precision, csm::WarningList* warnings) const { - // MESSAGE_LOG( - // spdlog::level::info, - // "Computing imageToGround for {}, {}, {}, with desired precision {}", - // image_pt.line, image_pt.samp, height, desired_precision); - // double xc, yc, zc; - // double vx, vy, vz; - // double xl, yl, zl; - // double dxl, dyl, dzl; - // losToEcf(image_pt.line, image_pt.samp, _no_adjustment, xc, yc, zc, vx, vy, vz, - // xl, yl, zl); - - // double aPrec; + MESSAGE_LOG( + spdlog::level::info, + "Computing imageToGround for {}, {}, {}, with desired precision {}", + image_pt.line, image_pt.samp, height, desired_precision); + double x = 0, y = 0, z = 0; - // losEllipsoidIntersect(height, xc, yc, zc, xl, yl, zl, x, y, z, aPrec, - // desired_precision, warnings); - - // if (achieved_precision) *achieved_precision = aPrec; - - // if (warnings && (desired_precision > 0.0) && (aPrec > desired_precision)) { - // warnings->push_back(csm::Warning( - // csm::Warning::PRECISION_NOT_MET, "Desired precision not achieved.", - // "UsgsAstroProjectedLsSensorModel::imageToGround()")); - // } - // MESSAGE_LOG( - // spdlog::level::info, - // "imageToGround result {} {} {}", - // x, y, z); + double meterLine, meterSamp; + std::vector<double> meterCoord = pixelToMeter(image_pt.line, image_pt.samp, m_geoTransform); + meterLine = meterCoord[0]; + meterSamp = meterCoord[1]; + PJ_CONTEXT *C = proj_context_create(); + + /* Create a projection. */ + PJ *isdProj = proj_create(C, m_projString.c_str()); + if (0 == isdProj) { + MESSAGE_LOG( + spdlog::level::debug, + "Failed to create isd transformation object"); + return csm::EcefCoord(x, y, z); + } + + /* Create the geocentric projection for our target */ + std::string radius_a = "+a=" + std::to_string(m_majorAxis); + std::string radius_b = "+b=" + std::to_string(m_minorAxis); + std::string projString = "+proj=geocent " + radius_a + " " + radius_b + " +type=crs"; + PJ *ecefProj = proj_create(C, projString.c_str()); + if (0 == ecefProj) { + MESSAGE_LOG( + spdlog::level::debug, + "Failed to create geocent transformation object"); + return csm::EcefCoord(x, y, z); + } + + // Compute the transformation from our ISIS projection to ecef + PJ *isdProj2ecefProj = proj_create_crs_to_crs_from_pj(C, isdProj, ecefProj, 0, 0); + PJ_COORD c_in; + c_in.xy.x = meterSamp; + c_in.xy.y = meterLine; + PJ_COORD c_out = proj_trans(isdProj2ecefProj, PJ_FWD, c_in); + x = c_out.xyz.x, y = c_out.xyz.y, z = c_out.xyz.z; + MESSAGE_LOG( + spdlog::level::info, + "imageToGround result {} {} {}", + x, y, z); return csm::EcefCoord(x, y, z); } @@ -458,86 +403,11 @@ csm::EcefCoordCovar UsgsAstroProjectedLsSensorModel::imageToGround( const csm::ImageCoordCovar& image_pt, double height, double heightVariance, double desired_precision, double* achieved_precision, csm::WarningList* warnings) const { - MESSAGE_LOG( - spdlog::level::debug, - "Calculating imageToGround (covar) for {}, {}, {} with " - "height varinace {} and desired precision {}", - image_pt.line, image_pt.samp, height, heightVariance, desired_precision) - // Image to ground with error propagation - // Use numerical partials - // Convert imagept to camera imagept - // proj -> ecef -> groundToImage - const double DELTA_IMAGE = 1.0; - const double DELTA_GROUND = m_gsd; - csm::ImageCoord ip(image_pt.line, image_pt.samp); - - csm::EcefCoord gp = imageToGround(ip, height, desired_precision, - achieved_precision, warnings); - - // Compute numerical partials xyz wrt to lsh - ip.line = image_pt.line + DELTA_IMAGE; - ip.samp = image_pt.samp; - csm::EcefCoord gpl = imageToGround(ip, height, desired_precision); - double xpl = (gpl.x - gp.x) / DELTA_IMAGE; - double ypl = (gpl.y - gp.y) / DELTA_IMAGE; - double zpl = (gpl.z - gp.z) / DELTA_IMAGE; - - ip.line = image_pt.line; - ip.samp = image_pt.samp + DELTA_IMAGE; - csm::EcefCoord gps = imageToGround(ip, height, desired_precision); - double xps = (gps.x - gp.x) / DELTA_IMAGE; - double yps = (gps.y - gp.y) / DELTA_IMAGE; - double zps = (gps.z - gp.z) / DELTA_IMAGE; - - ip.line = image_pt.line; - ip.samp = image_pt.samp; - csm::EcefCoord gph = - imageToGround(ip, height + DELTA_GROUND, desired_precision); - double xph = (gph.x - gp.x) / DELTA_GROUND; - double yph = (gph.y - gp.y) / DELTA_GROUND; - double zph = (gph.z - gp.z) / DELTA_GROUND; - - // Convert sensor covariance to image space - // double sCov[4]; - // determineSensorCovarianceInImageSpace(gp, sCov); - - std::vector<double> unmod = getUnmodeledError(image_pt); - - double iCov[4]; - iCov[0] = 0; - iCov[1] = 0; - iCov[2] = 0; - iCov[3] = 0; - - // Temporary matrix product - double t00, t01, t02, t10, t11, t12, t20, t21, t22; - t00 = xpl * iCov[0] + xps * iCov[2]; - t01 = xpl * iCov[1] + xps * iCov[3]; - t02 = xph * heightVariance; - t10 = ypl * iCov[0] + yps * iCov[2]; - t11 = ypl * iCov[1] + yps * iCov[3]; - t12 = yph * heightVariance; - t20 = zpl * iCov[0] + zps * iCov[2]; - t21 = zpl * iCov[1] + zps * iCov[3]; - t22 = zph * heightVariance; - - // Ground covariance - csm::EcefCoordCovar result; - result.x = gp.x; - result.y = gp.y; - result.z = gp.z; - - result.covariance[0] = t00 * xpl + t01 * xps + t02 * xph; - result.covariance[1] = t00 * ypl + t01 * yps + t02 * yph; - result.covariance[2] = t00 * zpl + t01 * zps + t02 * zph; - result.covariance[3] = t10 * xpl + t11 * xps + t12 * xph; - result.covariance[4] = t10 * ypl + t11 * yps + t12 * yph; - result.covariance[5] = t10 * zpl + t11 * zps + t12 * zph; - result.covariance[6] = t20 * xpl + t21 * xps + t22 * xph; - result.covariance[7] = t20 * ypl + t21 * yps + t22 * yph; - result.covariance[8] = t20 * zpl + t21 * zps + t22 * zph; + csm::EcefCoord groundCoord = imageToGround(image_pt, height); + csm::ImageCoord cameraImagePt = UsgsAstroLsSensorModel::groundToImage(groundCoord); + csm::ImageCoordCovar cameraImagePtCovar(cameraImagePt.line, cameraImagePt.samp); - return result; + return UsgsAstroLsSensorModel::imageToGround(cameraImagePtCovar, height, heightVariance, desired_precision, achieved_precision, warnings); } //*************************************************************************** @@ -547,65 +417,9 @@ csm::EcefLocus UsgsAstroProjectedLsSensorModel::imageToProximateImagingLocus( const csm::ImageCoord& image_pt, const csm::EcefCoord& ground_pt, double desired_precision, double* achieved_precision, csm::WarningList* warnings) const { - // Convert imagept to camera imagept - // proj -> ecef -> groundToImage - MESSAGE_LOG( - spdlog::level::info, - "Computing imageToProximateImagingLocus (ground {}, {}, {}) for image " - "point ({}, {}) with desired precision {}", - ground_pt.x, ground_pt.y, ground_pt.z, image_pt.line, image_pt.samp, - desired_precision); - - // Object ray unit direction near given ground location - const double DELTA_GROUND = m_gsd; - - double x = ground_pt.x; - double y = ground_pt.y; - double z = ground_pt.z; - - // Elevation at input ground point - double height = computeEllipsoidElevation(x, y, z, m_majorAxis, m_minorAxis, - desired_precision); - - // Ground point on object ray with the same elevation - csm::EcefCoord gp1 = - imageToGround(image_pt, height, desired_precision, achieved_precision); - - // Vector between 2 ground points above - double dx1 = x - gp1.x; - double dy1 = y - gp1.y; - double dz1 = z - gp1.z; - - // Unit vector along object ray - csm::EcefCoord gp2 = imageToGround(image_pt, height - DELTA_GROUND, - desired_precision, achieved_precision); - double dx2 = gp2.x - gp1.x; - double dy2 = gp2.y - gp1.y; - double dz2 = gp2.z - gp1.z; - double mag2 = sqrt(dx2 * dx2 + dy2 * dy2 + dz2 * dz2); - dx2 /= mag2; - dy2 /= mag2; - dz2 /= mag2; - - // Point on object ray perpendicular to input point - - // Locus - csm::EcefLocus locus; - double scale = dx1 * dx2 + dy1 * dy2 + dz1 * dz2; - gp2.x = gp1.x + scale * dx2; - gp2.y = gp1.y + scale * dy2; - gp2.z = gp1.z + scale * dz2; - - double hLocus = computeEllipsoidElevation(gp2.x, gp2.y, gp2.z, m_majorAxis, - m_minorAxis, desired_precision); - locus.point = imageToGround(image_pt, hLocus, desired_precision, - achieved_precision, warnings); - - locus.direction.x = dx2; - locus.direction.y = dy2; - locus.direction.z = dz2; - - return locus; + csm::ImageCoord cameraImagePt = UsgsAstroLsSensorModel::groundToImage(ground_pt); + + return UsgsAstroLsSensorModel::imageToProximateImagingLocus(cameraImagePt, ground_pt, desired_precision, achieved_precision, warnings); } //*************************************************************************** @@ -616,8 +430,10 @@ csm::EcefLocus UsgsAstroProjectedLsSensorModel::imageToRemoteImagingLocus( double* achieved_precision, csm::WarningList* warnings) const { // Go from proj x, y to latlon then ground to image // Convert imagept to camera imagept - // proj -> ecef -> groundToImage - return UsgsAstroLsSensorModel::imageToRemoteImagingLocus(image_pt, desired_precision, achieved_precision, warnings); + csm::EcefCoord groundCoord = imageToGround(image_pt, 0); + csm::ImageCoord cameraImagePt = UsgsAstroLsSensorModel::groundToImage(groundCoord); + + return UsgsAstroLsSensorModel::imageToRemoteImagingLocus(cameraImagePt, desired_precision, achieved_precision, warnings); } //--------------------------------------------------------------------------- @@ -1283,360 +1099,20 @@ void UsgsAstroProjectedLsSensorModel::createProjectiveApproximation() { //*************************************************************************** std::string UsgsAstroProjectedLsSensorModel::constructStateFromIsd( const std::string imageSupportData, csm::WarningList* warnings) { - json state = {}; - MESSAGE_LOG( - spdlog::level::debug, - "Constructing state from Isd") - // Instantiate UsgsAstroLineScanner sensor model - json jsonIsd = json::parse(imageSupportData); - std::shared_ptr<csm::WarningList> parsingWarnings(new csm::WarningList); - - state["m_modelName"] = ale::getSensorModelName(jsonIsd); - state["m_imageIdentifier"] = ale::getImageId(jsonIsd); - state["m_sensorName"] = ale::getSensorName(jsonIsd); - state["m_platformName"] = ale::getPlatformName(jsonIsd); - MESSAGE_LOG( - spdlog::level::trace, - "m_modelName: {} " - "m_imageIdentifier: {} " - "m_sensorName: {} " - "m_platformName: {} ", - state["m_modelName"].dump(), state["m_imageIdentifier"].dump(), - state["m_sensorName"].dump(), state["m_platformName"].dump()) - - state["m_focalLength"] = ale::getFocalLength(jsonIsd); - MESSAGE_LOG( - spdlog::level::trace, - "m_focalLength: {} ", - state["m_focalLength"].dump()) - - state["m_nLines"] = ale::getTotalLines(jsonIsd); - state["m_nSamples"] = ale::getTotalSamples(jsonIsd); - MESSAGE_LOG( - spdlog::level::trace, - "m_nLines: {} " - "m_nSamples: {} ", - state["m_nLines"].dump(), state["m_nSamples"].dump()) + // return UsgsAstroLsSensorModel::constructStateFromIsd(imageSupportData, warnings); + json lsState = json::parse(UsgsAstroLsSensorModel::constructStateFromIsd(imageSupportData, warnings)); + json state = json::parse(imageSupportData); - state["m_iTransS"] = ale::getFocal2PixelSamples(jsonIsd); - state["m_iTransL"] = ale::getFocal2PixelLines(jsonIsd); + lsState["m_geoTransform"] = getGeoTransform(state); + lsState["m_projString"] = getProjectionString(state); MESSAGE_LOG( spdlog::level::trace, - "m_iTransS: {} " - "m_iTransL: {} ", - state["m_iTransS"].dump(), state["m_iTransL"].dump()) - - state["m_platformFlag"] = 1; - state["m_ikCode"] = 0; - state["m_zDirection"] = 1; - MESSAGE_LOG( - spdlog::level::trace, - "m_platformFlag: {} " - "m_ikCode: {} " - "m_zDirection: {} ", - state["m_platformFlag"].dump(), state["m_ikCode"].dump(), - state["m_zDirection"].dump()) - - state["m_distortionType"] = - getDistortionModel(ale::getDistortionModel(jsonIsd)); - state["m_opticalDistCoeffs"] = ale::getDistortionCoeffs(jsonIsd); - MESSAGE_LOG( - spdlog::level::trace, - "m_distortionType: {} " - "m_opticalDistCoeffs: {} ", - state["m_distortionType"].dump(), state["m_opticalDistCoeffs"].dump()) - - // Zero computed state values - state["m_referencePointXyz"] = std::vector<double>(3, 0.0); - MESSAGE_LOG( - spdlog::level::trace, - "m_referencePointXyz: {} ", - state["m_referencePointXyz"].dump()) - - // Sun position and sensor position use the same times so compute that now - ale::States inst_state = ale::getInstrumentPosition(jsonIsd); - std::vector<double> ephemTime = inst_state.getTimes(); - double startTime = ephemTime.front(); - double stopTime = ephemTime.back(); - // Force at least 25 nodes to help with lagrange interpolation - // These also have to be equally spaced for lagrange interpolation - if (ephemTime.size() < 25) { - ephemTime.resize(25); - } - double timeStep = (stopTime - startTime) / (ephemTime.size() - 1); - for (size_t i = 0; i < ephemTime.size(); i++) { - ephemTime[i] = startTime + i * timeStep; - } - - try { - state["m_dtEphem"] = timeStep; - MESSAGE_LOG( - spdlog::level::trace, - "m_dtEphem: {} ", - state["m_dtEphem"].dump()) - } catch (...) { - parsingWarnings->push_back(csm::Warning( - csm::Warning::DATA_NOT_AVAILABLE, "dt_ephemeris not in ISD", - "UsgsAstroFrameSensorModel::constructStateFromIsd()")); - MESSAGE_LOG(spdlog::level::warn, "m_dtEphem not in ISD") - } - - try { - state["m_t0Ephem"] = startTime - ale::getCenterTime(jsonIsd); - MESSAGE_LOG( - spdlog::level::trace, - "t0_ephemeris: {}", - state["m_t0Ephem"].dump()) - } catch (...) { - parsingWarnings->push_back(csm::Warning( - csm::Warning::DATA_NOT_AVAILABLE, "t0_ephemeris not in ISD", - "UsgsAstroFrameSensorModel::constructStateFromIsd()")); - MESSAGE_LOG(spdlog::level::warn, "t0_ephemeris not in ISD") - } - - ale::States sunState = ale::getSunPosition(jsonIsd); - ale::Orientations j2000_to_target = ale::getBodyRotation(jsonIsd); - ale::State interpSunState, rotatedSunState; - std::vector<double> sunPositions = {}; - std::vector<double> sunVelocities = {}; - - for (int i = 0; i < ephemTime.size(); i++) { - interpSunState = sunState.getState(ephemTime[i], ale::SPLINE); - rotatedSunState = j2000_to_target.rotateStateAt(ephemTime[i], interpSunState); - // ALE operates in km and we want m - sunPositions.push_back(rotatedSunState.position.x * 1000); - sunPositions.push_back(rotatedSunState.position.y * 1000); - sunPositions.push_back(rotatedSunState.position.z * 1000); - sunVelocities.push_back(rotatedSunState.velocity.x * 1000); - sunVelocities.push_back(rotatedSunState.velocity.y * 1000); - sunVelocities.push_back(rotatedSunState.velocity.z * 1000); - } - - state["m_sunPosition"] = sunPositions; - state["m_sunVelocity"] = sunVelocities; - - // leave these be for now. - state["m_gsd"] = 1.0; - state["m_flyingHeight"] = 1000.0; - state["m_halfSwath"] = 1000.0; - state["m_halfTime"] = 10.0; - MESSAGE_LOG( - spdlog::level::trace, - "m_gsd: {} " - "m_flyingHeight: {} " - "m_halfSwath: {} " - "m_halfTime: {} ", - state["m_gsd"].dump(), state["m_flyingHeight"].dump(), - state["m_halfSwath"].dump(), state["m_halfTime"].dump()) - - state["m_centerEphemerisTime"] = ale::getCenterTime(jsonIsd); - state["m_startingEphemerisTime"] = ale::getStartingTime(jsonIsd); - MESSAGE_LOG( - spdlog::level::trace, - "m_centerEphemerisTime: {} " - "m_startingEphemerisTime: {} ", - state["m_centerEphemerisTime"].dump(), - state["m_startingEphemerisTime"].dump()) - - std::vector<std::vector<double>> lineScanRate = ale::getLineScanRate(jsonIsd); - state["m_intTimeLines"] = - getIntegrationStartLines(lineScanRate, parsingWarnings.get()); - state["m_intTimeStartTimes"] = - getIntegrationStartTimes(lineScanRate, parsingWarnings.get()); - state["m_intTimes"] = getIntegrationTimes(lineScanRate, parsingWarnings.get()); - MESSAGE_LOG( - spdlog::level::trace, - "m_intTimeLines: {} " - "m_intTimeStartTimes: {} " - "m_intTimes: {} ", - state["m_intTimeLines"].dump(), state["m_intTimeStartTimes"].dump(), - state["m_intTimes"].dump()) - - state["m_detectorSampleSumming"] = ale::getSampleSumming(jsonIsd); - state["m_detectorLineSumming"] = ale::getLineSumming(jsonIsd); - state["m_startingDetectorSample"] = ale::getDetectorStartingSample(jsonIsd); - state["m_startingDetectorLine"] = ale::getDetectorStartingLine(jsonIsd); - state["m_detectorSampleOrigin"] = ale::getDetectorCenterSample(jsonIsd); - state["m_detectorLineOrigin"] = ale::getDetectorCenterLine(jsonIsd); - MESSAGE_LOG( - spdlog::level::trace, - "m_detectorSampleSumming: {} " - "m_detectorLineSumming: {}" - "m_startingDetectorSample: {} " - "m_startingDetectorLine: {} " - "m_detectorSampleOrigin: {} " - "m_detectorLineOrigin: {} ", - state["m_detectorSampleSumming"].dump(), - state["m_detectorLineSumming"].dump(), - state["m_startingDetectorSample"].dump(), - state["m_startingDetectorLine"].dump(), - state["m_detectorSampleOrigin"].dump(), - state["m_detectorLineOrigin"].dump()) - - ale::Orientations j2000_to_sensor = ale::getInstrumentPointing(jsonIsd); - ale::State interpInstState, rotatedInstState; - std::vector<double> positions = {}; - std::vector<double> velocities = {}; - - for (int i = 0; i < ephemTime.size(); i++) { - interpInstState = inst_state.getState(ephemTime[i], ale::SPLINE); - rotatedInstState = - j2000_to_target.rotateStateAt(ephemTime[i], interpInstState, ale::SLERP); - // ALE operates in km and we want m - positions.push_back(rotatedInstState.position.x * 1000); - positions.push_back(rotatedInstState.position.y * 1000); - positions.push_back(rotatedInstState.position.z * 1000); - velocities.push_back(rotatedInstState.velocity.x * 1000); - velocities.push_back(rotatedInstState.velocity.y * 1000); - velocities.push_back(rotatedInstState.velocity.z * 1000); - } - - state["m_positions"] = positions; - state["m_numPositions"] = positions.size(); - MESSAGE_LOG( - spdlog::level::trace, - "m_positions: {}" - "m_numPositions: {}", - state["m_positions"].dump(), state["m_numPositions"].dump()) - - state["m_velocities"] = velocities; - MESSAGE_LOG( - spdlog::level::trace, - "m_velocities: {}", - state["m_velocities"].dump()) - - // Work-around for issues in ALE <=0.8.5 where Orientations without angular - // velocities seg fault when you multiply them. This will make the angular - // velocities in the final Orientations dubious but they are not used - // in any calculations so this is okay. - if (j2000_to_sensor.getAngularVelocities().empty()) { - j2000_to_sensor = ale::Orientations( - j2000_to_sensor.getRotations(), - j2000_to_sensor.getTimes(), - std::vector<ale::Vec3d>(j2000_to_sensor.getTimes().size()), - j2000_to_sensor.getConstantRotation(), - j2000_to_sensor.getConstantFrames(), - j2000_to_sensor.getTimeDependentFrames()); - } - if (j2000_to_target.getAngularVelocities().empty()) { - j2000_to_target = ale::Orientations( - j2000_to_target.getRotations(), - j2000_to_target.getTimes(), - std::vector<ale::Vec3d>(j2000_to_target.getTimes().size()), - j2000_to_target.getConstantRotation(), - j2000_to_target.getConstantFrames(), - j2000_to_target.getTimeDependentFrames()); - } - - ale::Orientations sensor_to_j2000 = j2000_to_sensor.inverse(); - ale::Orientations sensor_to_target = j2000_to_target * sensor_to_j2000; - ephemTime = sensor_to_target.getTimes(); - double quatStep = - (ephemTime.back() - ephemTime.front()) / (ephemTime.size() - 1); - try { - state["m_dtQuat"] = quatStep; - MESSAGE_LOG( - spdlog::level::trace, - "dt_quaternion: {}", - state["m_dtQuat"].dump()) - } catch (...) { - parsingWarnings->push_back(csm::Warning( - csm::Warning::DATA_NOT_AVAILABLE, "dt_quaternion not in ISD", - "UsgsAstroFrameSensorModel::constructStateFromIsd()")); - MESSAGE_LOG( - spdlog::level::warn, - "dt_quaternion not in ISD") - } - - try { - state["m_t0Quat"] = ephemTime[0] - ale::getCenterTime(jsonIsd); - MESSAGE_LOG( - spdlog::level::trace, - "m_t0Quat: {}", - state["m_t0Quat"].dump()) - } catch (...) { - parsingWarnings->push_back(csm::Warning( - csm::Warning::DATA_NOT_AVAILABLE, "t0_quaternion not in ISD", - "UsgsAstroFrameSensorModel::constructStateFromIsd()")); - MESSAGE_LOG( - spdlog::level::warn, - "t0_quaternion not in ISD") - } - std::vector<double> quaternion; - std::vector<double> quaternions; - - for (size_t i = 0; i < ephemTime.size(); i++) { - ale::Rotation rotation = sensor_to_target.interpolate( - ephemTime.front() + quatStep * i, ale::SLERP); - quaternion = rotation.toQuaternion(); - quaternions.push_back(quaternion[1]); - quaternions.push_back(quaternion[2]); - quaternions.push_back(quaternion[3]); - quaternions.push_back(quaternion[0]); - } - - state["m_quaternions"] = quaternions; - state["m_numQuaternions"] = quaternions.size(); - MESSAGE_LOG( - spdlog::level::trace, - "m_quaternions: {}" - "m_numQuaternions: {}", - state["m_quaternions"].dump(), state["m_numQuaternions"].dump()) - - state["m_currentParameterValue"] = std::vector<double>(NUM_PARAMETERS, 0.0); - MESSAGE_LOG( - spdlog::level::trace, - "m_currentParameterValue: {}", - state["m_currentParameterValue"].dump()) - - // Get radii - // ALE operates in km and we want m - state["m_minorAxis"] = ale::getSemiMinorRadius(jsonIsd) * 1000; - state["m_majorAxis"] = ale::getSemiMajorRadius(jsonIsd) * 1000; - MESSAGE_LOG( - spdlog::level::trace, - "m_minorAxis: {}" - "m_majorAxis: {}", - state["m_minorAxis"].dump(), state["m_majorAxis"].dump()) - - // set identifiers - state["m_platformIdentifier"] = ale::getPlatformName(jsonIsd); - state["m_sensorIdentifier"] = ale::getSensorName(jsonIsd); - MESSAGE_LOG( - spdlog::level::trace, - "m_platformIdentifier: {}" - "m_sensorIdentifier: {}", - state["m_platformIdentifier"].dump(), state["m_sensorIdentifier"].dump()) - - // get reference_height - state["m_minElevation"] = ale::getMinHeight(jsonIsd); - state["m_maxElevation"] = ale::getMaxHeight(jsonIsd); - MESSAGE_LOG( - spdlog::level::trace, - "m_minElevation: {}" - "m_maxElevation: {}", - state["m_minElevation"].dump(), state["m_maxElevation"].dump()) - - // Default to identity covariance - state["m_covariance"] = - std::vector<double>(NUM_PARAMETERS * NUM_PARAMETERS, 0.0); - for (int i = 0; i < NUM_PARAMETERS; i++) { - state["m_covariance"][i * NUM_PARAMETERS + i] = 1.0; - } - - if (!parsingWarnings->empty()) { - if (warnings) { - warnings->insert(warnings->end(), parsingWarnings->begin(), - parsingWarnings->end()); - } - throw csm::Error(csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE, - "ISD is invalid for creating the sensor model.", - "UsgsAstroFrameSensorModel::constructStateFromIsd"); - } - + "m_geoTransform: {} " + "m_projString: {} ", + lsState["m_geoTransform"].dump(), lsState["m_projString"].dump()); // The state data will still be updated when a sensor model is created since // some state data is not in the ISD and requires a SM to compute them. - return state.dump(); + return lsState.dump(); } //*************************************************************************** @@ -1649,33 +1125,3 @@ std::shared_ptr<spdlog::logger> UsgsAstroProjectedLsSensorModel::getLogger() { void UsgsAstroProjectedLsSensorModel::setLogger(std::string logName) { m_logger = spdlog::get(logName); } - -csm::EcefVector UsgsAstroProjectedLsSensorModel::getSunPosition( - const double imageTime) const { - int numSunPositions = m_sunPosition.size(); - int numSunVelocities = m_sunVelocity.size(); - csm::EcefVector sunPosition = csm::EcefVector(); - - // If there are multiple positions, use Lagrange interpolation - if ((numSunPositions / 3) > 1) { - double sunPos[3]; - lagrangeInterp(numSunPositions / 3, &m_sunPosition[0], m_t0Ephem, - m_dtEphem, imageTime, 3, 8, sunPos); - sunPosition.x = sunPos[0]; - sunPosition.y = sunPos[1]; - sunPosition.z = sunPos[2]; - } else if ((numSunVelocities / 3) >= 1) { - // If there is one position triple with at least one velocity triple - // then the illumination direction is calculated via linear extrapolation. - sunPosition.x = (imageTime * m_sunVelocity[0] + m_sunPosition[0]); - sunPosition.y = (imageTime * m_sunVelocity[1] + m_sunPosition[1]); - sunPosition.z = (imageTime * m_sunVelocity[2] + m_sunPosition[2]); - } else { - // If there is one position triple with no velocity triple, then the - // illumination direction is the difference of the original vectors. - sunPosition.x = m_sunPosition[0]; - sunPosition.y = m_sunPosition[1]; - sunPosition.z = m_sunPosition[2]; - } - return sunPosition; -} diff --git a/src/Utilities.cpp b/src/Utilities.cpp index 14ed72ee316e19b8583db27d2de3826557616ce2..039885d29fbedfbe0072970e6e5dc2269b6ae0f9 100644 --- a/src/Utilities.cpp +++ b/src/Utilities.cpp @@ -1528,3 +1528,45 @@ std::string ephemTimeToCalendarTime(double ephemTime) { buffer[21] = '\0'; return buffer; } + +std::vector<double> getGeoTransform(json isd) { + std::vector<double> transform = {}; + try { + transform = isd.at("geo_transform").get<std::vector<double>>(); + } catch (std::exception &e) { + std::string originalError = e.what(); + std::string msg = "Could not parse the geo_transform. ERROR: " + originalError + isd.dump(); + throw std::runtime_error(msg); + } + return transform; +} + +std::string getProjectionString(json isd) { + std::string projection_string = ""; + try { + projection_string = isd.at("proj_string"); + } catch (...) { + throw std::runtime_error("Could not parse the projection string."); + } + return projection_string; +} + +std::vector<double> pixelToMeter(double line, double sample, std::vector<double> geoTransform) { + double meter_x = (sample * geoTransform[1]) + geoTransform[0]; + double meter_y = (line * geoTransform[5]) + geoTransform[3]; + + meter_x += geoTransform[1] * 0.5; + meter_y += geoTransform[5] * 0.5; + + return {meter_y, meter_x}; +} + +std::vector<double> meterToPixel(double meter_x, double meter_y, std::vector<double> geoTransform) { + meter_x -= geoTransform[1] * 0.5; + meter_y -= geoTransform[5] * 0.5; + + double sample = (meter_x - geoTransform[0]) / geoTransform[1]; + double line = (meter_y - geoTransform[3]) / geoTransform[5]; + + return {line, sample}; +} \ No newline at end of file diff --git a/tests/UtilitiesTests.cpp b/tests/UtilitiesTests.cpp index 75797876143af358019e68117374b62434dc96bb..36a2294b6d3a0b29b2f95f63b26f7dcdc374ed2a 100644 --- a/tests/UtilitiesTests.cpp +++ b/tests/UtilitiesTests.cpp @@ -636,3 +636,17 @@ TEST(UtilitiesTests, sanitizeTest) { sanitize(input); EXPECT_STREQ(input.c_str(), "\nHello World\n"); } + +TEST(UtilitiesTests, pixelToMeterTest) { + std::vector<double> geoTransform = {-104607.78948592, 4.980137561815, 0.0, -570564.40018202, 0.0, -4.980137561815}; + std::vector<double> ret = pixelToMeter(13.1191, 4981.08, geoTransform); + EXPECT_NEAR(ret[0], -570632.225173488, 1e-4); + EXPECT_NEAR(ret[1], -79798.83581073358, 1e-4); +} + +TEST(UtilitiesTests, meterToPixelTest) { + std::vector<double> geoTransform = {-104607.78948592, 4.980137561815, 0.0, -570564.40018202, 0.0, -4.980137561815}; + std::vector<double> ret = meterToPixel(-79798.83581073358, -570632.225173488, geoTransform); + EXPECT_NEAR(ret[0], 13.1191, 1e-4); + EXPECT_NEAR(ret[1], 4981.0800000000099, 1e-4); +} \ No newline at end of file