diff --git a/include/usgscsm/Utilities.h b/include/usgscsm/Utilities.h index a7b1b7606506fec497df472358e9705ee187b6e8..4776edf480d3b74a76cfa80a117ed595a219afb5 100644 --- a/include/usgscsm/Utilities.h +++ b/include/usgscsm/Utilities.h @@ -4,23 +4,28 @@ #include <vector> #include <math.h> #include <tuple> +#include <string> + +#include <json.hpp> + +#include <Warning.h> // methods pulled out of los2ecf and computeViewingPixel -// for now, put everything in here. +// for now, put everything in here. // TODO: later, consider if it makes sense to pull sample/line offsets out // Compute distorted focalPlane coordinates in mm std::tuple<double, double> computeDistortedFocalPlaneCoordinates( const double& line, const double& sample, const double& sampleOrigin, - const double& lineOrigin, + const double& lineOrigin, const double& sampleSumming, const double& startingSample, const double& lineOffset, const double iTransS[], const double iTransL[]); - + void calculateRotationMatrixFromQuaternions( double quaternions[4], double cameraToBody[9]); @@ -40,9 +45,44 @@ void createCameraLookVector( //void calculateAttitudeCorrection( // const double& time, -// +// // double attCorr[9]); // -#endif +// Methods for checking/accessing the ISD + +double metric_conversion(double val, std::string from, std::string to="m"); +std::string getSensorModelName(nlohmann::json isd, csm::WarningList *list=nullptr); +std::string getImageId(nlohmann::json isd, csm::WarningList *list=nullptr); +std::string getSensorName(nlohmann::json isd, csm::WarningList *list=nullptr); +std::string getPlatformName(nlohmann::json isd, csm::WarningList *list=nullptr); +int getTotalLines(nlohmann::json isd, csm::WarningList *list=nullptr); +int getTotalSamples(nlohmann::json isd, csm::WarningList *list=nullptr); +double getStartingTime(nlohmann::json isd, csm::WarningList *list=nullptr); +double getCenterTime(nlohmann::json isd, csm::WarningList *list=nullptr); +std::vector<double> getIntegrationStartLines(nlohmann::json isd, csm::WarningList *list=nullptr); +std::vector<double> getIntegrationStartTimes(nlohmann::json isd, csm::WarningList *list=nullptr); +std::vector<double> getIntegrationTimes(nlohmann::json isd, csm::WarningList *list=nullptr); +int getSampleSumming(nlohmann::json isd, csm::WarningList *list=nullptr); +int getLineSumming(nlohmann::json isd, csm::WarningList *list=nullptr); +double getFocalLength(nlohmann::json isd, csm::WarningList *list=nullptr); +double getFocalLengthEpsilon(nlohmann::json isd, csm::WarningList *list=nullptr); +std::vector<double> getFocal2PixelLines(nlohmann::json isd, csm::WarningList *list=nullptr); +std::vector<double> getFocal2PixelSamples(nlohmann::json isd, csm::WarningList *list=nullptr); +double getDetectorCenterLine(nlohmann::json isd, csm::WarningList *list=nullptr); +double getDetectorCenterSample(nlohmann::json isd, csm::WarningList *list=nullptr); +double getDetectorStartingLine(nlohmann::json isd, csm::WarningList *list=nullptr); +double getDetectorStartingSample(nlohmann::json isd, csm::WarningList *list=nullptr); +double getMinHeight(nlohmann::json isd, csm::WarningList *list=nullptr); +double getMaxHeight(nlohmann::json isd, csm::WarningList *list=nullptr); +double getSemiMajorRadius(nlohmann::json isd, csm::WarningList *list=nullptr); +double getSemiMinorRadius(nlohmann::json isd, csm::WarningList *list=nullptr); +std::vector<double> getTransverseDistortionX(nlohmann::json isd, csm::WarningList *list=nullptr); +std::vector<double> getTransverseDistortionY(nlohmann::json isd, csm::WarningList *list=nullptr); +std::vector<double> getRadialDistortion(nlohmann::json isd, csm::WarningList *list=nullptr); +std::vector<double> getSunPositions(nlohmann::json isd, csm::WarningList *list=nullptr); +std::vector<double> getSensorPositions(nlohmann::json isd, csm::WarningList *list=nullptr); +std::vector<double> getSensorVelocities(nlohmann::json isd, csm::WarningList *list=nullptr); +std::vector<double> getSensorOrientations(nlohmann::json isd, csm::WarningList *list=nullptr); +#endif diff --git a/src/UsgsAstroFrameSensorModel.cpp b/src/UsgsAstroFrameSensorModel.cpp index ca233c8be1d386da66118c5ae98fcf00ce25f248..71176369c3d202bb1246bd90a03b540dee333ea9 100644 --- a/src/UsgsAstroFrameSensorModel.cpp +++ b/src/UsgsAstroFrameSensorModel.cpp @@ -1,5 +1,4 @@ #include "UsgsAstroFrameSensorModel.h" -#include "Distortion.h" #include <iomanip> #include <iostream> @@ -10,6 +9,9 @@ #include <Error.h> #include <Version.h> +#include "Distortion.h" +#include "Utilities.h" + using json = nlohmann::json; using namespace std; @@ -791,156 +793,120 @@ void UsgsAstroFrameSensorModel::replaceModelState(const std::string& stringState std::string UsgsAstroFrameSensorModel::constructStateFromIsd(const std::string& jsonIsd, csm::WarningList* warnings) { - auto metric_conversion = [](double val, std::string from, std::string to="m") { - json typemap = { - {"m", 0}, - {"km", 3} - }; - - // everything to lowercase - std::transform(from.begin(), from.end(), from.begin(), ::tolower); - std::transform(to.begin(), to.end(), to.begin(), ::tolower); - return val*pow(10, typemap[from].get<int>() - typemap[to].get<int>()); - }; json isd = json::parse(jsonIsd); json state = {}; + csm::WarningList* parsingWarnings = new csm::WarningList; - try { - state["m_modelName"] = isd.at("name_model"); - state["m_imageIdentifier"] = isd.at("image_identifier"); - state["m_sensorName"] = isd.at("name_sensor"); - state["m_platformName"] = isd.at("name_platform"); - std::cerr << "Model Name Parsed!" << std::endl; - - state["m_startingDetectorSample"] = isd.at("starting_detector_sample"); - state["m_startingDetectorLine"] = isd.at("starting_detector_line"); - - std::cerr << "Detector Starting Pixel Parsed!" << std::endl; + state["m_modelName"] = getSensorModelName(isd, parsingWarnings); + state["m_imageIdentifier"] = getImageId(isd, parsingWarnings); + state["m_sensorName"] = getSensorName(isd, parsingWarnings); + state["m_platformName"] = getPlatformName(isd, parsingWarnings); + std::cerr << "Model Name Parsed!" << std::endl; - // get focal length - { - json jayson = isd.at("focal_length_model"); - json focal_length = jayson.at("focal_length"); - json epsilon = jayson.at("focal_epsilon"); + state["m_startingDetectorSample"] = getDetectorStartingSample(isd, parsingWarnings); + state["m_startingDetectorLine"] = getDetectorStartingLine(isd, parsingWarnings); - state["m_focalLength"] = focal_length; - state["m_focalLengthEpsilon"] = epsilon; + std::cerr << "Detector Starting Pixel Parsed!" << std::endl; - std::cerr << "Focal Length Parsed!" << std::endl; - } - - // get sensor_position - { - json jayson = isd.at("sensor_position"); - json positions = jayson.at("positions")[0]; - json velocities = jayson.at("velocities")[0]; - json unit = jayson.at("unit"); - - unit = unit.get<std::string>(); - state["m_currentParameterValue"] = json(); - state["m_currentParameterValue"][0] = metric_conversion(positions[0].get<double>(), unit); - state["m_currentParameterValue"][1] = metric_conversion(positions[1].get<double>(), unit); - state["m_currentParameterValue"][2] = metric_conversion(positions[2].get<double>(), unit); - state["m_spacecraftVelocity"] = velocities; - - std::cerr << "Sensor Location Parsed!" << std::endl; - } + // get focal length + state["m_focalLength"] = getFocalLength(isd, parsingWarnings); + state["m_focalLengthEpsilon"] = getFocalLengthEpsilon(isd, parsingWarnings); - // get sun_position - // sun position is not strictly necessary, but is required for getIlluminationDirection. - { - json jayson = isd.at("sun_position"); - json positions = jayson.at("positions")[0]; - json unit = jayson.at("unit"); + std::cerr << "Focal Length Parsed!" << std::endl; - unit = unit.get<std::string>(); - state["m_sunPosition"] = json(); - state["m_sunPosition"][0] = metric_conversion(positions[0].get<double>(), unit); - state["m_sunPosition"][1] = metric_conversion(positions[1].get<double>(), unit); - state["m_sunPosition"][2] = metric_conversion(positions[2].get<double>(), unit); - - std::cerr << "Sun Position Parsed!" << std::endl; - } + state["m_currentParameterValue"] = json(); - // get sensor_orientation quaternion - { - json jayson = isd.at("sensor_orientation"); - json quaternion = jayson.at("quaternions")[0]; - - state["m_currentParameterValue"][3] = quaternion[0]; - state["m_currentParameterValue"][4] = quaternion[1]; - state["m_currentParameterValue"][5] = quaternion[2]; - state["m_currentParameterValue"][6] = quaternion[3]; + // get sensor_position + std::vector<double> position = getSensorPositions(isd, parsingWarnings); + if (!position.empty() && position.size() != 3) { + parsingWarnings->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Sensor position does not have 3 values.", + "UsgsAstroFrameSensorModel::constructStateFromIsd()")); + state["m_currentParameterValue"][0] = 0; + state["m_currentParameterValue"][1] = 0; + state["m_currentParameterValue"][2] = 0; + } + else { + state["m_currentParameterValue"] = position; + } - std::cerr << "Sensor Orientation Parsed!" << std::endl; - } + // get sensor_velocity + std::vector<double> velocity = getSensorVelocities(isd, parsingWarnings); + if (!velocity.empty() && velocity.size() != 3) { + parsingWarnings->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Sensor velocity does not have 3 values.", + "UsgsAstroFrameSensorModel::constructStateFromIsd()")); + } + else { + state["m_spacecraftVelocity"] = velocity; + } - // get optical_distortion - { - json jayson = isd.at("optical_distortion"); - std::vector<double> xDistortion = jayson.at("transverse").at("x"); - std::vector<double> yDistortion = jayson.at("transverse").at("y"); - xDistortion.resize(10, 0.0); - yDistortion.resize(10, 0.0); + std::cerr << "Sensor Location Parsed!" << std::endl; - state["m_odtX"] = xDistortion; - state["m_odtY"] = yDistortion; + // get sun_position + // sun position is not strictly necessary, but is required for getIlluminationDirection. + state["m_sunPosition"] = getSunPositions(isd); - std::cerr << "Distortion Parsed!" << std::endl; - } + std::cerr << "Sun Position Parsed!" << std::endl; - // get detector_center - { - json jayson = isd.at("detector_center"); - json sample = jayson.at("sample"); - json line = jayson.at("line"); + // get sensor_orientation quaternion + std::vector<double> quaternion = getSensorOrientations(isd, parsingWarnings); + if (!quaternion.empty() && quaternion.size() != 4) { + parsingWarnings->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Sensor orientation quaternion does not have 4 values.", + "UsgsAstroFrameSensorModel::constructStateFromIsd()")); + } + else { + state["m_currentParameterValue"][3] = quaternion[0]; + state["m_currentParameterValue"][4] = quaternion[1]; + state["m_currentParameterValue"][5] = quaternion[2]; + state["m_currentParameterValue"][6] = quaternion[3]; + } - state["m_ccdCenter"][0] = line; - state["m_ccdCenter"][1] = sample; + std::cerr << "Sensor Orientation Parsed!" << std::endl; - std::cerr << "Detector Center Parsed!" << std::endl; - } + // get optical_distortion + state["m_odtX"] = getTransverseDistortionX(isd, parsingWarnings); + state["m_odtY"] = getTransverseDistortionY(isd, parsingWarnings); - // get radii - { - json jayson = isd.at("radii"); - json semiminor = jayson.at("semiminor"); - json semimajor = jayson.at("semimajor"); - json unit = jayson.at("unit"); + std::cerr << "Distortion Parsed!" << std::endl; - unit = unit.get<std::string>(); - state["m_minorAxis"] = metric_conversion(semiminor.get<double>(), unit); - state["m_majorAxis"] = metric_conversion(semimajor.get<double>(), unit); + // get detector_center + state["m_ccdCenter"][0] = getDetectorCenterLine(isd, parsingWarnings); + state["m_ccdCenter"][1] = getDetectorCenterSample(isd, parsingWarnings); - std::cerr << "Target Radii Parsed!" << std::endl; - } + std::cerr << "Detector Center Parsed!" << std::endl; - // get reference_height - { - json reference_height = isd.at("reference_height"); - json maxheight = reference_height.at("maxheight"); - json minheight = reference_height.at("minheight"); - json unit = reference_height.at("unit"); + // get radii + state["m_minorAxis"] = getSemiMinorRadius(isd, parsingWarnings); + state["m_majorAxis"] = getSemiMajorRadius(isd, parsingWarnings); - unit = unit.get<std::string>(); - state["m_minElevation"] = metric_conversion(minheight.get<double>(), unit); - state["m_maxElevation"] = metric_conversion(maxheight.get<double>(), unit); + std::cerr << "Target Radii Parsed!" << std::endl; - std::cerr << "Reference Height Parsed!" << std::endl; - } + // get reference_height + state["m_minElevation"] = getMinHeight(isd, parsingWarnings); + state["m_maxElevation"] = getMaxHeight(isd, parsingWarnings); - state["m_ephemerisTime"] = isd.at("center_ephemeris_time"); - state["m_nLines"] = isd.at("image_lines"); - state["m_nSamples"] = isd.at("image_samples"); + std::cerr << "Reference Height Parsed!" << std::endl; - state["m_iTransL"] = isd.at("focal2pixel_lines"); + state["m_ephemerisTime"] = getCenterTime(isd, parsingWarnings); + state["m_nLines"] = getTotalLines(isd, parsingWarnings); + state["m_nSamples"] = getTotalSamples(isd, parsingWarnings); - state["m_iTransS"] = isd.at("focal2pixel_samples"); + state["m_iTransL"] = getFocal2PixelLines(isd, parsingWarnings); + state["m_iTransS"] = getFocal2PixelSamples(isd, parsingWarnings); - // We don't pass the pixel to focal plane transformation so invert the - // focal plane to pixel transformation + // We don't pass the pixel to focal plane transformation so invert the + // focal plane to pixel transformation + try { double determinant = state["m_iTransL"][1].get<double>() * state["m_iTransS"][2].get<double>() - state["m_iTransL"][2].get<double>() * state["m_iTransS"][1].get<double>(); @@ -953,27 +919,37 @@ std::string UsgsAstroFrameSensorModel::constructStateFromIsd(const std::string& state["m_transY"][2] = state["m_iTransS"][2].get<double>() / determinant; state["m_transY"][0] = -(state["m_transY"][1].get<double>() * state["m_iTransL"][0].get<double>() + state["m_transY"][2].get<double>() * state["m_iTransS"][0].get<double>()); + } + catch (...) { + parsingWarnings->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not compute detector pixel to focal plane coordinate transformation.", + "UsgsAstroFrameSensorModel::constructStateFromIsd()")); + } - std::cerr << "Focal To Pixel Transformation Parsed!" << std::endl; + std::cerr << "Focal To Pixel Transformation Parsed!" << std::endl; - state["m_referencePointXyz"] = std::vector<double>(3, 0.0); - state["m_currentParameterCovariance"] = std::vector<double>(NUM_PARAMETERS*NUM_PARAMETERS,0.0); - state["m_collectionIdentifier"] = ""; + state["m_referencePointXyz"] = std::vector<double>(3, 0.0); + state["m_currentParameterCovariance"] = std::vector<double>(NUM_PARAMETERS*NUM_PARAMETERS,0.0); + state["m_collectionIdentifier"] = ""; - std::cerr << "Constants Set!" << std::endl; + std::cerr << "Constants Set!" << std::endl; - } - catch(std::out_of_range& e) { - throw csm::Error(csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE, - "ISD missing necessary keywords to create sensor model: " + std::string(e.what()), - "UsgsAstroFrameSensorModel::constructStateFromIsd"); - } - catch(...) { + if (!parsingWarnings->empty()) { + if (warnings) { + warnings->insert(warnings->end(), parsingWarnings->begin(), parsingWarnings->end()); + } + delete parsingWarnings; + parsingWarnings = nullptr; throw csm::Error(csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE, "ISD is invalid for creating the sensor model.", "UsgsAstroFrameSensorModel::constructStateFromIsd"); } + delete parsingWarnings; + parsingWarnings = nullptr; + return state.dump(); } diff --git a/src/Utilities.cpp b/src/Utilities.cpp index e9c82f6cdf766fc760136fea19f3ed9474fa342a..840a3d61922125a09a0b8263fd51ba8222c44c3c 100644 --- a/src/Utilities.cpp +++ b/src/Utilities.cpp @@ -1,5 +1,7 @@ #include "Utilities.h" +using json = nlohmann::json; + // Calculates a rotation matrix from Euler angles void calculateRotationMatrixFromEuler(double euler[], double rotationMatrix[]) { @@ -45,7 +47,7 @@ std::tuple<double, double> computeDistortedFocalPlaneCoordinates( const double& line, const double& sample, const double& sampleOrigin, - const double& lineOrigin, + const double& lineOrigin, const double& sampleSumming, const double& startingSample, const double& lineOffset, @@ -66,7 +68,7 @@ std::tuple<double, double> computeDistortedFocalPlaneCoordinates( double distortedLine = p11 * t1 + p12 * t2; double distortedSample = p21 * t1 + p22 * t2; - return std::make_tuple(distortedLine, distortedSample); + return std::make_tuple(distortedLine, distortedSample); }; // Define imaging ray in image space (In other words, create a look vector in camera space) @@ -88,3 +90,603 @@ void createCameraLookVector( cameraLook[1] /= magnitude; cameraLook[2] /= magnitude; } + +double metric_conversion(double val, std::string from, std::string to) { + json typemap = { + {"m", 0}, + {"km", 3} + }; + + // everything to lowercase + std::transform(from.begin(), from.end(), from.begin(), ::tolower); + std::transform(to.begin(), to.end(), to.begin(), ::tolower); + return val*pow(10, typemap[from].get<int>() - typemap[to].get<int>()); +} + +std::string getSensorModelName(json isd, csm::WarningList *list) { + std::string name = ""; + try { + name = isd.at("name_model"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the sensor model name.", + "Utilities::getSensorModelName()")); + } + } + return name; +} + +std::string getImageId(json isd, csm::WarningList *list) { + std::string id = ""; + try { + id = isd.at("image_identifier"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the image identifier.", + "Utilities::getImageId()")); + } + } + return id; +} + +std::string getSensorName(json isd, csm::WarningList *list) { + std::string name = ""; + try { + name = isd.at("name_sensor"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the sensor name.", + "Utilities::getSensorName()")); + } + } + return name; +} + +std::string getPlatformName(json isd, csm::WarningList *list) { + std::string name = ""; + try { + name = isd.at("name_platform"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the platform name.", + "Utilities::getPlatformName()")); + } + } + return name; +} + +int getTotalLines(json isd, csm::WarningList *list) { + int lines = 0; + try { + lines = isd.at("image_lines"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the number of lines in the image.", + "Utilities::getTotalLines()")); + } + } + return lines; +} + +int getTotalSamples(json isd, csm::WarningList *list) { + int samples = 0; + try { + samples = isd.at("image_samples"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the number of samples in the image.", + "Utilities::getTotalSamples()")); + } + } + return samples; +} + +double getStartingTime(json isd, csm::WarningList *list) { + double time = 0.0; + try { + time = isd.at("starting_ephemeris_time"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the image start time.", + "Utilities::getStartingTime()")); + } + } + return time; +} + +double getCenterTime(json isd, csm::WarningList *list) { + double time = 0.0; + try { + time = isd.at("center_ephemeris_time"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the center image time.", + "Utilities::getCenterTime()")); + } + } + return time; +} + +std::vector<double> getIntegrationStartLines(json isd, csm::WarningList *list) { + std::vector<double> lines; + try { + for (auto& scanRate : isd.at("line_scan_rate")) { + lines.push_back(scanRate[0]); + } + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the integration start lines in the integration time table.", + "Utilities::getIntegrationStartLines()")); + } + } + return lines; +} + +std::vector<double> getIntegrationStartTimes(json isd, csm::WarningList *list) { + std::vector<double> times; + try { + for (auto& scanRate : isd.at("line_scan_rate")) { + times.push_back(scanRate[1]); + } + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the integration start times in the integration time table.", + "Utilities::getIntegrationStartTimes()")); + } + } + return times; +} + +std::vector<double> getIntegrationTimes(json isd, csm::WarningList *list) { + std::vector<double> times; + try { + for (auto& scanRate : isd.at("line_scan_rate")) { + times.push_back(scanRate[2]); + } + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the integration times in the integration time table.", + "Utilities::getIntegrationTimes()")); + } + } + return times; +} + +int getSampleSumming(json isd, csm::WarningList *list) { + int summing = 0; + try { + summing = isd.at("detector_sample_summing"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the sample direction detector pixel summing.", + "Utilities::getSampleSumming()")); + } + } + return summing; +} + +int getLineSumming(json isd, csm::WarningList *list) { + int summing = 0; + try { + summing = isd.at("detector_line_summing"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the line direction detector pixel summing.", + "Utilities::getLineSumming()")); + } + } + return summing; +} + +double getFocalLength(json isd, csm::WarningList *list) { + double length = 0.0; + try { + length = isd.at("focal_length_model").at("focal_length"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the focal length.", + "Utilities::getFocalLength()")); + } + } + return length; +} + +double getFocalLengthEpsilon(json isd, csm::WarningList *list) { + double epsilon = 0.0; + try { + epsilon = isd.at("focal_length_model").at("focal_epsilon"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the focal length uncertainty.", + "Utilities::getFocalLengthEpsilon()")); + } + } + return epsilon; +} + +std::vector<double> getFocal2PixelLines(json isd, csm::WarningList *list) { + std::vector<double> transformation; + try { + transformation = isd.at("focal2pixel_lines").get<std::vector<double>>(); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the focal plane coordinate to detector lines transformation.", + "Utilities::getFocal2PixelLines()")); + } + } + return transformation; +} + +std::vector<double> getFocal2PixelSamples(json isd, csm::WarningList *list) { + std::vector<double> transformation; + try { + transformation = isd.at("focal2pixel_samples").get<std::vector<double>>(); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the focal plane coordinate to detector samples transformation.", + "Utilities::getFocal2PixelSamples()")); + } + } + return transformation; +} + +double getDetectorCenterLine(json isd, csm::WarningList *list) { + double line; + try { + line = isd.at("detector_center").at("line"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the detector center line.", + "Utilities::getDetectorCenterLine()")); + } + } + return line; +} + +double getDetectorCenterSample(json isd, csm::WarningList *list) { + double sample; + try { + sample = isd.at("detector_center").at("sample"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the detector center sample.", + "Utilities::getDetectorCenterSample()")); + } + } + return sample; +} + + +double getDetectorStartingLine(json isd, csm::WarningList *list) { + double line; + try { + line = isd.at("starting_detector_line"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the detector starting line.", + "Utilities::getDetectorStartingLine()")); + } + } + return line; +} + +double getDetectorStartingSample(json isd, csm::WarningList *list) { + double sample; + try { + sample = isd.at("starting_detector_sample"); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the detector starting sample.", + "Utilities::getDetectorStartingSample()")); + } + } + return sample; +} + +double getMinHeight(json isd, csm::WarningList *list) { + double height = 0.0; + try { + json referenceHeight = isd.at("reference_height"); + json minHeight = referenceHeight.at("minheight"); + json unit = referenceHeight.at("unit"); + height = metric_conversion(minHeight.get<double>(), unit.get<std::string>()); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the minimum height above the reference ellipsoid.", + "Utilities::getMinHeight()")); + } + } + return height; +} + +double getMaxHeight(json isd, csm::WarningList *list) { + double height = 0.0; + try { + json referenceHeight = isd.at("reference_height"); + json maxHeight = referenceHeight.at("maxheight"); + json unit = referenceHeight.at("unit"); + height = metric_conversion(maxHeight.get<double>(), unit.get<std::string>()); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the maximum height above the reference ellipsoid.", + "Utilities::getMaxHeight()")); + } + } + return height; +} + +double getSemiMajorRadius(json isd, csm::WarningList *list) { + double radius = 0.0; + try { + json radii = isd.at("radii"); + json semiMajor = radii.at("semimajor"); + json unit = radii.at("unit"); + radius = metric_conversion(semiMajor.get<double>(), unit.get<std::string>()); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the reference ellipsoid semimajor radius.", + "Utilities::getSemiMajorRadius()")); + } + } + return radius; +} + +double getSemiMinorRadius(json isd, csm::WarningList *list) { + double radius = 0.0; + try { + json radii = isd.at("radii"); + json semiMinor = radii.at("semiminor"); + json unit = radii.at("unit"); + radius = metric_conversion(semiMinor.get<double>(), unit.get<std::string>()); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the reference ellipsoid semiminor radius.", + "Utilities::getSemiMinorRadius()")); + } + } + return radius; +} + +std::vector<double> getTransverseDistortionX(json isd, csm::WarningList *list) { + std::vector<double> coefficients; + try { + coefficients = isd.at("optical_distortion").at("transverse").at("x").get<std::vector<double>>(); + coefficients.resize(10, 0.0); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the transverse distortion model X coefficients.", + "Utilities::getTransverseDistortionX()")); + } + } + return coefficients; +} + +std::vector<double> getTransverseDistortionY(json isd, csm::WarningList *list) { + std::vector<double> coefficients; + try { + coefficients = isd.at("optical_distortion").at("transverse").at("y").get<std::vector<double>>(); + coefficients.resize(10, 0.0); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the transverse distortion model Y coefficients.", + "Utilities::getTransverseDistortionY()")); + } + } + return coefficients; +} + +std::vector<double> getRadialDistortion(json isd, csm::WarningList *list) { + std::vector<double> coefficients; + try { + coefficients = isd.at("optical_distortion").at("radial").at("coefficients").get<std::vector<double>>(); + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the radial distortion model coefficients.", + "Utilities::getRadialDistortion()")); + } + } + return coefficients; +} + +std::vector<double> getSunPositions(json isd, csm::WarningList *list) { + std::vector<double> positions; + try { + json jayson = isd.at("sun_position"); + json unit = jayson.at("unit"); + for (auto& location : jayson.at("positions")) { + positions.push_back(metric_conversion(location[0].get<double>(), unit.get<std::string>())); + positions.push_back(metric_conversion(location[1].get<double>(), unit.get<std::string>())); + positions.push_back(metric_conversion(location[2].get<double>(), unit.get<std::string>())); + } + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the sun positions.", + "Utilities::getSunPositions()")); + } + } + return positions; +} + +std::vector<double> getSensorPositions(json isd, csm::WarningList *list) { + std::vector<double> positions; + try { + json jayson = isd.at("sensor_position"); + json unit = jayson.at("unit"); + for (auto& location : jayson.at("positions")) { + positions.push_back(metric_conversion(location[0].get<double>(), unit.get<std::string>())); + positions.push_back(metric_conversion(location[1].get<double>(), unit.get<std::string>())); + positions.push_back(metric_conversion(location[2].get<double>(), unit.get<std::string>())); + } + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the sensor positions.", + "Utilities::getSensorPositions()")); + } + } + return positions; +} + +std::vector<double> getSensorVelocities(json isd, csm::WarningList *list) { + std::vector<double> velocities; + try { + json jayson = isd.at("sensor_position"); + json unit = jayson.at("unit"); + for (auto& velocity : jayson.at("velocities")) { + velocities.push_back(metric_conversion(velocity[0].get<double>(), unit.get<std::string>())); + velocities.push_back(metric_conversion(velocity[1].get<double>(), unit.get<std::string>())); + velocities.push_back(metric_conversion(velocity[2].get<double>(), unit.get<std::string>())); + } + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the sensor velocities.", + "Utilities::getSensorVelocities()")); + } + } + return velocities; +} + +std::vector<double> getSensorOrientations(json isd, csm::WarningList *list) { + std::vector<double> quaternions; + try { + for (auto& quaternion : isd.at("sensor_orientation").at("quaternions")) { + quaternions.push_back(quaternion[0]); + quaternions.push_back(quaternion[1]); + quaternions.push_back(quaternion[2]); + quaternions.push_back(quaternion[3]); + } + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the sensor orientations.", + "Utilities::getSensorOrientations()")); + } + } + return quaternions; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e3567d19534610f1ae123066eba4fd99a9a3720e..3306376a11e7477ba008192ebfda67ebc3b71742 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,7 +5,8 @@ add_executable(runCSMCameraModelTests PluginTests.cpp FrameCameraTests.cpp LineScanCameraTests.cpp - DistortionTests.cpp) + DistortionTests.cpp + ISDParsingTests.cpp) if(WIN32) option(CMAKE_USE_WIN32_THREADS_INIT "using WIN32 threads" ON) option(gtest_disable_pthreads "Disable uses of pthreads in gtest." ON) diff --git a/tests/ISDParsingTests.cpp b/tests/ISDParsingTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c2976e3e902d082f4f75cee84bade8f9ea2d7c81 --- /dev/null +++ b/tests/ISDParsingTests.cpp @@ -0,0 +1,323 @@ +#include "Utilities.h" + +#include <gtest/gtest.h> + +#include "Fixtures.h" + +TEST(MetricConversion, DistanceConversion) { + EXPECT_EQ(1, metric_conversion(1000, "m", "km")); + EXPECT_EQ(1000, metric_conversion(1000, "m", "m")); + EXPECT_EQ(1000, metric_conversion(1, "km", "m")); + EXPECT_EQ(1, metric_conversion(1, "km", "km")); + EXPECT_EQ(0, metric_conversion(0, "m", "km")); + EXPECT_EQ(0, metric_conversion(0, "m", "m")); + EXPECT_EQ(0, metric_conversion(0, "km", "m")); + EXPECT_EQ(0, metric_conversion(0, "km", "km")); + EXPECT_EQ(-1, metric_conversion(-1000, "m", "km")); + EXPECT_EQ(-1000, metric_conversion(-1000, "m", "m")); + EXPECT_EQ(-1000, metric_conversion(-1, "km", "m")); + EXPECT_EQ(-1, metric_conversion(-1, "km", "km")); +} + +TEST(ISDParsing, ModelName) { + json isd = { + {"name_model", "Test"} + }; + EXPECT_EQ("Test", getSensorModelName(isd)); +} + +TEST(ISDParsing, ImageIdentifier) { + json isd = { + {"image_identifier", "Test"} + }; + EXPECT_EQ("Test", getImageId(isd)); +} + +TEST(ISDParsing, SensorName) { + json isd = { + {"name_sensor", "Test"} + }; + EXPECT_EQ("Test", getSensorName(isd)); +} + +TEST(ISDParsing, PlatformName) { + json isd = { + {"name_platform", "Test"} + }; + EXPECT_EQ("Test", getPlatformName(isd)); +} + +TEST(ISDParsing, TotalLines) { + json isd = { + {"image_lines", 16} + }; + EXPECT_EQ(16, getTotalLines(isd)); +} + +TEST(ISDParsing, TotalSamples) { + json isd = { + {"image_samples", 16} + }; + EXPECT_EQ(16, getTotalSamples(isd)); +} + +TEST(ISDParsing, StartTime) { + json isd = { + {"starting_ephemeris_time", -10} + }; + EXPECT_EQ(-10, getStartingTime(isd)); +} + +TEST(ISDParsing, CenterTime) { + json isd = { + {"center_ephemeris_time", -10} + }; + EXPECT_EQ(-10, getCenterTime(isd)); +} + +TEST(ISDParsing, IntegrationStartLines) { + json isd = { + {"line_scan_rate", { + {0, 1, 2}, + {3, 4, 5}, + {6, 7, 8}} + } + }; + std::vector<double> startLines = {0, 3, 6}; + EXPECT_EQ(startLines, getIntegrationStartLines(isd)); +} + +TEST(ISDParsing, IntegrationStartTimes) { + json isd = { + {"line_scan_rate", { + {0, 1, 2}, + {3, 4, 5}, + {6, 7, 8}} + } + }; + std::vector<double> startTimes = {1, 4, 7}; + EXPECT_EQ(startTimes, getIntegrationStartTimes(isd)); +} + +TEST(ISDParsing, IntegrationTimes) { + json isd = { + {"line_scan_rate", { + {0, 1, 2}, + {3, 4, 5}, + {6, 7, 8}} + } + }; + std::vector<double> times = {2, 5, 8}; + EXPECT_EQ(times, getIntegrationTimes(isd)); +} + +TEST(ISDParsing, SampleSumming) { + json isd = { + {"detector_sample_summing", 4} + }; + EXPECT_EQ(4, getSampleSumming(isd)); +} + +TEST(ISDParsing, LineSumming) { + json isd = { + {"detector_line_summing", 4} + }; + EXPECT_EQ(4, getLineSumming(isd)); +} + +TEST(ISDParsing, FocalLength) { + json isd = { + {"focal_length_model", { + {"focal_length", 2}} + } + }; + EXPECT_EQ(2, getFocalLength(isd)); +} + +TEST(ISDParsing, FocalLengthEpsilon) { + json isd = { + {"focal_length_model", { + {"focal_epsilon", 0.1}} + } + }; + EXPECT_EQ(0.1, getFocalLengthEpsilon(isd)); +} + +TEST(ISDParsing, Focal2PixelLines) { + json isd = { + {"focal2pixel_lines", {0, 1, 2}} + }; + std::vector<double> coefficients = {0, 1, 2}; + EXPECT_EQ(coefficients, getFocal2PixelLines(isd)); +} + +TEST(ISDParsing, Focal2PixelSamples) { + json isd = { + {"focal2pixel_samples", {0, 1, 2}} + }; + std::vector<double> coefficients = {0, 1, 2}; + EXPECT_EQ(coefficients, getFocal2PixelSamples(isd)); +} + +TEST(ISDParsing, DetectorCenterLine) { + json isd = { + {"detector_center", { + {"line", 2}} + } + }; + EXPECT_EQ(2, getDetectorCenterLine(isd)); +} + +TEST(ISDParsing, DetectorCenterSample) { + json isd = { + {"detector_center", { + {"sample", 3}} + } + }; + EXPECT_EQ(3, getDetectorCenterSample(isd)); +} + +TEST(ISDParsing, DetectorStartingLine) { + json isd = { + {"starting_detector_line", 1} + }; + EXPECT_EQ(1, getDetectorStartingLine(isd)); +} + +TEST(ISDParsing, DetectorStartingSample) { + json isd = { + {"starting_detector_sample", 2} + }; + EXPECT_EQ(2, getDetectorStartingSample(isd)); +} + +TEST(ISDParsing, MinHeight) { + json isd = { + {"reference_height", { + {"minheight", -1}, + {"unit", "km"}} + } + }; + EXPECT_EQ(-1000, getMinHeight(isd)); +} + +TEST(ISDParsing, MaxHeight) { + json isd = { + {"reference_height", { + {"maxheight", 10}, + {"unit", "km"}} + } + }; + EXPECT_EQ(10000, getMaxHeight(isd)); +} + +TEST(ISDParsing, SemiMajor) { + json isd = { + {"radii", { + {"semimajor", 2}, + {"unit", "km"}} + } + }; + EXPECT_EQ(2000, getSemiMajorRadius(isd)); +} + +TEST(ISDParsing, SemiMinor) { + json isd = { + {"radii", { + {"semiminor", 1}, + {"unit", "km"}} + } + }; + EXPECT_EQ(1000, getSemiMinorRadius(isd)); +} + +TEST(ISDParsing, TransverseX) { + json isd = { + {"optical_distortion", { + {"transverse", { + {"x", {-1, 2, 4}}}} + } + } + }; + std::vector<double> coefficients = {-1, 2, 4, 0, 0, 0, 0, 0, 0, 0}; + EXPECT_EQ(coefficients, getTransverseDistortionX(isd)); +} + +TEST(ISDParsing, TransverseY) { + json isd = { + {"optical_distortion", { + {"transverse", { + {"y", {-11, 21, 24, 16, 20}}}} + } + } + }; + std::vector<double> coefficients = {-11, 21, 24, 16, 20, 0, 0, 0, 0, 0}; + EXPECT_EQ(coefficients, getTransverseDistortionY(isd)); +} + +TEST(ISDParsing, Radial) { + json isd = { + {"optical_distortion", { + {"radial", { + {"coefficients", {0, 1, 2}}}} + } + } + }; + std::vector<double> coefficients = {0, 1, 2}; + EXPECT_EQ(coefficients, getRadialDistortion(isd)); +} + +TEST(ISDParsing, SunPosition) { + json isd = { + {"sun_position", { + {"positions", { + {0, 1, 2}, + {3, 4, 5}, + {6, 7, 8}}}, + {"unit", "km"}} + } + }; + std::vector<double> positions = {0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000}; + EXPECT_EQ(positions, getSunPositions(isd)); +} + +TEST(ISDParsing, SensorPosition) { + json isd = { + {"sensor_position", { + {"positions", { + {0, 1, 2}, + {3, 4, 5}, + {6, 7, 8}}}, + {"unit", "km"}} + } + }; + std::vector<double> positions = {0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000}; + EXPECT_EQ(positions, getSensorPositions(isd)); +} + +TEST(ISDParsing, SensorVelocities) { + json isd = { + {"sensor_position", { + {"velocities", { + {0, 1, 2}, + {3, 4, 5}, + {6, 7, 8}}}, + {"unit", "km"}} + } + }; + std::vector<double> velocity = {0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000}; + EXPECT_EQ(velocity, getSensorVelocities(isd)); +} + +TEST(ISDParsing, SensorOrientations) { + json isd = { + {"sensor_orientation", { + {"quaternions", { + {0, 1, 2, 3}, + {4, 5, 6, 7}, + {8, 9, 10, 11}}}} + } + }; + std::vector<double> rotations = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + EXPECT_EQ(rotations, getSensorOrientations(isd)); +} diff --git a/tests/PluginTests.cpp b/tests/PluginTests.cpp index 2252e8851dc5d0685f51d55a3458e16f8393c7d4..f805a8c0eac100d48adf4861e64ccbffa7811ec1 100644 --- a/tests/PluginTests.cpp +++ b/tests/PluginTests.cpp @@ -112,15 +112,17 @@ TEST_F(FrameIsdTest, ConstructInValidCamera) { UsgsAstroPlugin testPlugin; isd.setFilename("data/constVelocityLineScan.img"); csm::Model *cameraModel = NULL; + csm::WarningList *warnings = new csm::WarningList; try { testPlugin.constructModelFromISD( isd, "USGS_ASTRO_FRAME_SENSOR_MODEL", - NULL); + warnings); FAIL() << "Expected an error"; } catch(csm::Error &e) { EXPECT_EQ(e.getError(), csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE); + EXPECT_FALSE(warnings->empty()); } catch(...) { FAIL() << "Expected csm SENSOR_MODEL_NOT_CONSTRUCTIBLE error"; @@ -128,6 +130,9 @@ TEST_F(FrameIsdTest, ConstructInValidCamera) { if (cameraModel) { delete cameraModel; } + if (warnings) { + delete warnings; + } } TEST_F(ConstVelLineScanIsdTest, Constructible) { diff --git a/tests/data/simpleFramerISD.json b/tests/data/simpleFramerISD.json index ddc5492815a0533b5bbccbc1ab9f260f8732bc62..b22c3e8ab043f663fb569eb15e4800689138c780 100644 --- a/tests/data/simpleFramerISD.json +++ b/tests/data/simpleFramerISD.json @@ -2,6 +2,7 @@ "name_model" : "USGS_ASTRO_FRAME_SENSOR_MODEL", "name_sensor": "SENSOR", "name_platform": "CarryMe", + "image_identifier": "TEST_IMAGE", "center_ephemeris_time": 50, "dt_ephemeris": 100, "focal2pixel_lines": [0.0, 0.0, 10.0],